Android AllDay events start or finish on the wrong day - android

I am working on an android app whose goal is to synchronize events between a PHP application and the smartphone. Everything work fine for “regular” events, but I have an issue with “all day” events. I followed the official Android documentation, setting the EVENT_TIMEZONE to UTC and setting timestamps for dtstart and dtend that corresponds to midnight UTC.
However, depending on the phone timezone at event creation time, when I open Google Calendar, events are:
Showing correctly when phone timezone is GMT+2
Starting one day before when phone timezone is GMT-11 -> GMT+1
Finishing one day after when phone timezone is GMT+3 -> GMT+13
You should know that my tests are made in France and we currently are at GMT+2. Google Calendar is configured to use the phone timezone.
Here is a dump of event’s information before insertion :
06-22 17:41:02.339: V/EventEntity - addToCalendar(6535):
Event to be inserted :
calendar_id=8
dtstart=1466719200000 ( = 24/6/2016 0:00:00 )
eventLocation=
title=Server AllDay Event
dtend=1466805600000 ( = 25/6/2016 0:00:00 )
allDay=true
description=
hasAlarm=1
eventStatus=1
eventTimezone=UTC
and here is the code of the method that does the event's insertion, where the previous log comes from :
Context context = AppContext.getAppContext();
ContentValues eventValues = this.getValuesForCalendar();
Log.v("EventEntity - addToCalendar", "Event to be inserted : " + eventValues);
Uri eventUri = context.getContentResolver().insert(Uri.parse("content://com.android.calendar/events"), eventValues);
this.setSystem_id(Long.parseLong(eventUri.getLastPathSegment()));
// === RAPPEL DE RDV ===
String reminderUriString = "content://com.android.calendar/reminders";
ContentValues reminderValues = new ContentValues();
reminderValues.put("event_id", this.getSystem_id());
reminderValues.put("minutes", 5);
// Default(0), Alert(1), Email(2), SMS(3)
reminderValues.put("method", 0);
context.getContentResolver().insert(Uri.parse(reminderUriString), reminderValues);
// =====================
And still, when events are showing correctly, if phone timezone is changed, events span on previous or next day. The behavior in not the same with “all day” events created with Google Calendar :
Events with phone at GMT+2 :
Events with phone's timezone changed to GMT-1 :
So I dumped the calendar with those all day events in it, one created with my app and another created with Google Calendar :
As you can see, the event created through Google Calendar is set to start at 2AM and not at midnight! So I tried setting the dstart and dtend timestamps to midnight at phone timezone (ie at 2AM if phone’s timezone is GMT+2, or 23PM the day before if GMT-1) but events are still spanning to previous or next day.
In a nutshell: I’m lost and I don’t get how to have correct all day events that start and end at the right day and won’t move around the calendar when phone’s timezone changes !

Related

How to use UsageStatsManager?

Background
Google has deprecated the function "getRecentTasks" of "ActivityManager" class. Now all it does is to get the list of apps that the current app has opened.
I've even written a post about it here on StackOverflow, but I noticed it's impossible.
The problem
I've made a post about it (here, and another, similar one created by someone else, here) and requested to re-consider it, and Google decided to make a new class, that seem to provide a similar functionality (more like statistics, but might also be useful), but I can't find out how to use it.
The class is called "UsageStatsManager", and my guess is that the function "queryUsageStats" does the job.
Also, it seems it has a new permission ("android.permission.PACKAGE_USAGE_STATS"), which is a system permission, but it's written that:
declaring the permission implies intention to use the API and the user
of the device can grant permission through the Settings application.
Here's another link about this new functionality.
What I've found
I've looked at the code of Android, and noticed that "Context" has USAGE_STATS_SERVICE , which in the JavaDocs say the next thing:
/**
* Use with {#link #getSystemService} to retrieve a {#link
* android.app.UsageStatsManager} for interacting with the status bar.
*
* #see #getSystemService
* #see android.app.UsageStatsManager
* #hide
*/
public static final String USAGE_STATS_SERVICE = "usagestats";
The weird thing is that not only it says "status bar", but also the packageName doesn't match (should be "android.app.usage.UsageStatsManager" instead) .
I've also added the correct permission:
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
and here's the code I use:
final UsageStatsManager usageStatsManager=(UsageStatsManager)context.getSystemService("usagestats");// Context.USAGE_STATS_SERVICE);
final int currentYear=Calendar.getInstance().get(Calendar.YEAR);
final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY,currentYear-2,currentYear);
In the emulator itself, I went to "Settings"->"security"->"apps with usage access" , and enabled my app.
However, when running the code, all I get is an empty list...
The question
How do you use UsageStatsManager ?
Also, how do you let the user to grant the permission in the easiest way possible? Or is it automatically done, as soon as the app tries to get the needed information?
What happens when trying to use this class yet the user hasn't confirmed it yet?
How can I make the code return me a real list of apps?
I think the documentation was just short hand for the Calendar stuff. I don't think it actually works with just 2014; however I can be wrong.
In order to access the actually list of UsageStats, you would need to create a Calendar object with the correct month,day, and year. Exactly how MRK said in the other answer. I copied and corrected the errors in MRK's code so anyone who sees it in the future can see it.
Calendar beginCal = Calendar.getInstance();
beginCal.set(Calendar.DATE, 1);
beginCal.set(Calendar.MONTH, 0);
beginCal.set(Calendar.YEAR, 2012);
Calendar endCal = Calendar.getInstance();
endCal.set(Calendar.DATE, 1);
endCal.set(Calendar.MONTH, 0);
endCal.set(Calendar.YEAR, 2014);
final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, beginCal.getTimeInMillis(), endCal.getTimeInMillis());
-Credit MRK; corrected by me (he accidentally just put cal instead of beginCal and endCal)
The code for the usage access settings is below. :)
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);
I've created a sample of how to use UsageStats on my Github.
Hopefully it can be of help to someone
https://github.com/ColeMurray/UsageStatsSample
Answering your last question "How can I make the code return me a real list of apps?".
queryUsageStats takes begin time and end time in milliseconds, not the value of the year in int.
Calendar beginCal = Calendar.getInstance();
beginCal.set(Calendar.DATE, 1);
beginCal.set(Calendar.MONTH, 0);
beginCal.set(Calendar.YEAR, 2012);
Calendar endCal = Calendar.getInstance();
endCal.set(Calendar.DATE, 1);
endCal.set(Calendar.MONTH, 0);
endCal.set(Calendar.YEAR, 2014);
final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, beginCal.getTimeInMillis(), endCal.getTimeInMillis());
This should return a list of UsageStats for the years 2012 and 2013 (keep in mind the end time is exclusive of the end result time range).
There is actually an example app included in AOSP sample code: developers/samples/android/system/AppUsageStatistics/
It includes all the bits necessary to use UsageStats in an app:
Declaring the permission in AndroidManifest.xml
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
Show settings to grant permission to access UsageStatsManager
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
Query UsageStatsManager for statistics.
Calendar cal = Calendar.getInstance();
cal.add(Calendar.YEAR, -1);
List<UsageStats> queryUsageStats = mUsageStatsManager
.queryUsageStats(intervalType, cal.getTimeInMillis(),
System.currentTimeMillis());
Creating a list of apps based on UsageStats
To summarize and apply to your example:
You seem to correctly ask for and grant permissions to access usage stats.
You correctly get the UsageStats system service.
However, the time period you query for is way too short: Arguments beginTime and endTime are measured in milliseconds since the epoch. Calendar instances can give you this value with getTimeinMillis(). What you erroneously do is to only give the year numbers (2015 and2017 if you would run the program today). These values are interpreted as milliseconds since the epoch and thus the interval is only 2 milliseconds long and is some time in 1970.
Instead of the following code snippet that you posted, you should copy the example I posted below:
Wrong:
final int currentYear=Calendar.getInstance().get(Calendar.YEAR);
final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY,currentYear-2,currentYear);
Correct:
final long currentTime = System.currentTimeMillis(); // Get current time in milliseconds
final Calendar cal = Calendar.getInstance();
cal.add(Calendar.YEAR, -2); // Set year to beginning of desired period.
final long beginTime = cal.getTimeInMillis(); // Get begin time in milliseconds
final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, beginTime, currentTime);
Use UsageStats will get the wrong information when user opens the notification drawer or on a locked screen. You have to use UsageStatsManager.queryEvents() and look for the latest event with MOVE_TO_FOREGROUND event type.
If you want to see usage statistics of a specific time period, you have to first calculate the length of time in milliseconds of the start and end of your time period since the epoch. (epoch is the number of seconds that have elapsed since 00:00:00 UTC, Thursday 1, 1970.) Or, as I will show in the following sample code, an easy way is to calculate backwards from the current time in milliseconds.
For example, if you want a usage statistic of the past 4 days, you can use the following code:
UsageStatsManager mUsageStatsManager = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);
List<UsageStats> queryUsageStats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
(System.currentTimeMillis() - 345600000), System.currentTimeMillis());
The number 345600000 is the number of milliseconds in 4 days.
And for the INTERVAL_TYPE this article explains it well.
The system collects and aggregates the data over 4 different intervals
and they are: INTERVAL_DAILY, INTERVAL_WEEKLY, INTERVAL_MONTHLY and
INTERVAL_YEARLY. The system records are limited in time, so you’ll be
able to retrieve app usage data for up to 7 days for interval daily,
up to 4 weeks for interval weekly, up to 6 months for monthly and
finally up to 2 years for yearly.
There’s a fifth option to mention:
INTERVAL_BEST will choose the best fitting interval between the four
above based on the timespan you’ve chosen.
you can do like this
//noinspection ResourceType
final UsageStatsManager usageStatsManager=(UsageStatsManager)context.getSystemService("usagestats");// Context.USAGE_STATS_SERVICE);
final int currentYear=Calendar.getInstance().get(Calendar.YEAR);
final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY,currentYear-2,currentYear);

Android Calendar timezone not changing with CalendarContract

I am using the following code to add an event to the calender
Intent intent = new Intent(Intent.ACTION_INSERT);
intent.setType("vnd.android.cursor.item/event");
intent.putExtra(Events.TITLE, "my event title");
intent.putExtra(Events.EVENT_LOCATION, "my city");
intent.putExtra(Events.DESCRIPTION, "description of this event");
intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, dep.getTimeInMillis());
intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, arr.getTimeInMillis());
intent.putExtra(CalendarContract.Events.EVENT_TIMEZONE, departureTimeZone);
intent.putExtra(CalendarContract.Events.EVENT_END_TIMEZONE, arrivalTimeZone);
// Making it private and shown as busy
intent.putExtra(Events.ACCESS_LEVEL, Events.ACCESS_PRIVATE);
intent.putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
CoreApplication.getContext().startActivity(intent);
The problem is that the time it picks up is
One Hour SHORTER than the original time i send and
timezone is always set to device's timezone like "Central European Summer Time GMT +2"
HELP PLEASE!!!!!!!!!!!!!!!
Had the same trouble but found a way out.
TimeZone sets to the device's timeZone by default. To change this and to set it to a specific timeZone use the getRawOffset() property.
This method calculates the milliseconds from the current time. So you can add the milliseconds for your specified timeZone and subtract those for the default timeZone.
When I tried to change it to timeZone 'GMT_ID'
values.put(CalendarContract.Events.DTSTART, startDate.getTime() +TimeZone.getTimeZone(GMT_ID).getRawOffset() -TimeZone.getDefault().getRawOffset());
Hope this helps.
You should check this link Calendar Provider intents, it says that we can't put timezone as intent extras, so send the beginTime and endTime of the event in UTC.
Mentioned in the example of event insert intent, "It uses the CalendarContract.EXTRA_EVENT_BEGIN_TIME and CalendarContract.EXTRA_EVENT_END_TIME extra fields to pre-populate the form with the time of the event. The values for these times must be in UTC milliseconds from the epoch."

Android Dev: Current Time range check

I'm working on an android application, as part of me trying to learn android programming, that will switch audio profiles based on day and time (provided by the user)... So far I have got most of the layout done, I have also created a service that will run in the background to perform few checks...
Right now I'm having a hard time trying to find an elegant way to handle checking if current time falls in time range saved by the user... I'm saving the user's preference for time range in a string format from androids TimerPicker control, I need to check if the current time falls in the user saved times...
Right now I have the following code:
the 'from time' is coming in with the following: hour:minute:AM/PM -- 8:59:AM in string format
the 'to time' is coming in with the following: hour:minute:AM/PM -- 4:59:PM in string format
if(fromAMPM.equals("AM")){
from.set(from.AM_PM, from.AM);
} else {
from.set(from.AM_PM, from.PM);
}
//dont care about the YEAR and MONTH, so set it to current MONTH and YEAR
from.set(rightNow.get(rightNow.YEAR), rightNow.get(rightNow.MONTH), dayOfWeek, fromHour, fromMinute);
if(toAMPM.equals("AM")){
to.set(to.AM_PM, to.AM);
}else{
to.set(to.AM_PM, to.PM);
}
//dont care about the YEAR and MONTH, so set it to current MONTH and YEAR
to.set(rightNow.get(rightNow.YEAR), rightNow.get(rightNow.MONTH), dayOfWeek, toHour, toMinute);
//this is just for me to see what got set:
SimpleDateFormat df3 = new SimpleDateFormat("HH:mm aaa");
String formattedDate1 = df3.format(from.getTime());
String formattedDate2 = df3.format(to.getTime());
After all this processing:
formattedDate1 is returning: 08:59 AM
formattedDate2 is returning: 04:59 AM
Any suggestions?
Thanks
Take a look at the Calendar documentation, under the section "Inconsistent Information"
http://developer.android.com/reference/java/util/Calendar.html
You call:
to.set(rightNow.get(rightNow.YEAR), rightNow.get(rightNow.MONTH), dayOfWeek, toHour, toMinute);
Which sends toHour as HOUR_OF_DAY (24h), not HOUR (12h).
It says that when you supply it with inconsistent information, it just uses the latest information. You're telling it that toHour is 4 on a 24hour scale, which is inconsistent with your PM setting, so it throws the PM setting away.
The easiest change would probably be just to add 12 to toHour instead of setting the AM_PM. Or, don't use the set(year, month, day, hourofday, minute) command and just set hour and am_pm separately.

Android Sensor Timestamp reference time

I'm reading timestamp values from SensorEvent data but I can't work out the reference time for these values. Android documentation just says "The time in nanosecond at which the event happened" As an example:
My current Android device date, October 14th 2011 23:29:56.421 (GMT+2)
System.currentTimeMillis * 1000000 (nanosec) = 1318627796431000000 (that's ok)
sensorevent.timestamp (nanosec) = 67578436328000 = 19 hours 46 min ????
May you help me?
thanks
It appears that what you are dealing with is the number of nanoseconds since the operating system started, also known as "uptime".
Further info on the issue: http://code.google.com/p/android/issues/detail?id=7981
I should add that the linked question SensorEvent.timestamp to absolute (utc) timestamp? deals with the same issue and is where I found the answer.
I know that it's a very old question, but, I'm also struggling for converting SensorEvent.timestamp to a human readable time. So I'm writing here what I've understood so far and how I'm converting it in order to get better solutions from you guys. Any comments will be welcomed.
As I understood, SensorEvent.timestamp is an elapsed time since the device's boot-up. So I have to know the uptime of the device. So if there is an API returning device's boot-up, it will be very easy, but, I haven't found it.
So I'm using SystemClock.elapsedRealtime() and System.currentTimeMillis() to 'estimate' a device's uptime. This is my code.
private long mUptimeMillis; // member variable of the activity or service
...
atComponentsStartUp...() {
...
/* Call elapsedRealtime() and currentTimeMillis() in a row
in order to minimize the time gap */
long elapsedRealtime = SystemClock.elapsedRealtime();
long currentTimeMillis = System.currentTimeMillis();
/* Get an uptime. It assume that elapsedRealtime() and
currentTimeMillis() are called at the exact same time.
Actually they don't, but, ignore the gap
because it is not a significant value.
(On my device, it's less than 1 ms) */
mUptimeMillis = (currentTimeMillis - elapsedRealtime);
....
}
...
public void onSensorChanged(SensorEvent event) {
...
eventTimeMillis = ((event.timestamp / 1000000) + mUptimeMillis);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(eventTimeMillis);
...
}
I think this works for Apps that a millisecond time error is okey. Please, leave your ideas.

How can I get the "network" time, (from the "Automatic" setting called "Use network-provided values"), NOT the time on the phone?

I would like in my application to find a way to synch the date and time with something given by an external source.
I don't want to use the phone time because I might get a difference of maybe 5 minutes around real time. and 5 minutes extra or less = 10 minutes!
I have heard about time information in the GPS satellites or in Network antennas.
I have tried with System.getCurrentTime but i get the current the of the device, so, if my device is set up 5 minutes earlier, it display the wrong time.
EDIT
To make a short question: how can I get this time?
I didn't know, but found the question interesting. So I dug in the android code... Thanks open-source :)
The screen you show is DateTimeSettings. The checkbox "Use network-provided values" is associated to the shared preference String KEY_AUTO_TIME = "auto_time"; and also to Settings.System.AUTO_TIME
This settings is observed by an observed called mAutoTimeObserver in the 2 network ServiceStateTrackers:
GsmServiceStateTracker and CdmaServiceStateTracker.
Both implementations call a method called revertToNitz() when the settings becomes true.
Apparently NITZ is the equivalent of NTP in the carrier world.
Bottom line: You can set the time to the value provided by the carrier thanks to revertToNitz().
Unfortunately, I haven't found a mechanism to get the network time.
If you really need to do this, I'm afraid, you'll have to copy these ServiceStateTrackers implementations, catch the intent raised by the framework (I suppose), and add a getter to mSavedTime.
Get the library from http://commons.apache.org/net/download_net.cgi
//NTP server list: http://tf.nist.gov/tf-cgi/servers.cgi
public static final String TIME_SERVER = "time-a.nist.gov";
public static long getCurrentNetworkTime() {
NTPUDPClient timeClient = new NTPUDPClient();
InetAddress inetAddress = InetAddress.getByName(TIME_SERVER);
TimeInfo timeInfo = timeClient.getTime(inetAddress);
//long returnTime = timeInfo.getReturnTime(); //local device time
long returnTime = timeInfo.getMessage().getTransmitTimeStamp().getTime(); //server time
Date time = new Date(returnTime);
Log.d(TAG, "Time from " + TIME_SERVER + ": " + time);
return returnTime;
}
getReturnTime() is same as System.currentTimeMillis().
getReceiveTimeStamp() or getTransmitTimeStamp() method should be used.
You can see the difference after setting system time to 1 hour ago.
local time :
System.currentTimeMillis()
timeInfo.getReturnTime()
timeInfo.getMessage().getOriginateTimeStamp().getTime()
NTP server time :
timeInfo.getMessage().getReceiveTimeStamp().getTime()
timeInfo.getMessage().getTransmitTimeStamp().getTime()
Try this snippet of code:
String timeSettings = android.provider.Settings.System.getString(
this.getContentResolver(),
android.provider.Settings.System.AUTO_TIME);
if (timeSettings.contentEquals("0")) {
android.provider.Settings.System.putString(
this.getContentResolver(),
android.provider.Settings.System.AUTO_TIME, "1");
}
Date now = new Date(System.currentTimeMillis());
Log.d("Date", now.toString());
Make sure to add permission in Manifest
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
NITZ is a form of NTP and is sent to the mobile device over Layer 3 or NAS layers.
Commonly this message is seen as GMM Info and contains the following informaiton:
Certain carriers dont support this and some support it and have it setup incorrectly.
LAYER 3 SIGNALING MESSAGE
Time: 9:38:49.800
GMM INFORMATION 3GPP TS 24.008 ver 12.12.0 Rel 12 (9.4.19)
M Protocol Discriminator (hex data: 8)
(0x8) Mobility Management message for GPRS services
M Skip Indicator (hex data: 0)
Value: 0
M Message Type (hex data: 21)
Message number: 33
O Network time zone (hex data: 4680)
Time Zone value: GMT+2:00
O Universal time and time zone (hex data: 47716070 70831580)
Year: 17
Month: 06
Day: 07
Hour: 07
Minute :38
Second: 51
Time zone value: GMT+2:00
O Network Daylight Saving Time (hex data: 490100)
Daylight Saving Time value: No adjustment
Layer 3 data:
08 21 46 80 47 71 60 70 70 83
15 80 49 01 00
This seemed to work for me:
LocationManager locMan = (LocationManager) activity.getSystemService(activity.LOCATION_SERVICE);
long networkTS = locMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER).getTime();
Working on Android 2.2 API (Level 8)
Now you can get time for the current location but for this you have to set the system's persistent default time zone.setTimeZone(String timeZone) which can be get from
Calendar calendar = Calendar.getInstance();
long now = calendar.getTimeInMillis();
TimeZone current = calendar.getTimeZone();
setAutoTimeEnabled(boolean enabled)
Sets whether or not wall clock time should sync with automatic time updates from NTP.
TimeManager timeManager = TimeManager.getInstance();
// Use 24-hour time
timeManager.setTimeFormat(TimeManager.FORMAT_24);
// Set clock time to noon
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.HOUR_OF_DAY, 12);
long timeStamp = calendar.getTimeInMillis();
timeManager.setTime(timeStamp);
I was looking for that type of answer I read your answer but didn't satisfied and it was bit old. I found the new solution and share it. :)
For more information visit: https://developer.android.com/things/reference/com/google/android/things/device/TimeManager.html
I read that this
LocationManager locMan = (LocationManager) activity.getSystemService(activity.LOCATION_SERVICE);
long time = locMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER).getTime();
provides correct time, without internet at the cost of some blocking processing.
the time signal is not built into network antennas: you have to use the NTP protocol in order to retrieve the time on a ntp server. there are plenty of ntp clients, available as standalone executables or libraries.
the gps signal does indeed include a precise time signal, which is available with any "fix".
however, if nor the network, nor the gps are available, your only choice is to resort on the time of the phone... your best solution would be to use a system wide setting to synchronize automatically the phone time to the gps or ntp time, then always use the time of the phone.
note that the phone time, if synchronized regularly, should not differ much from the gps or ntp time. also note that forcing a user to synchronize its time may be intrusive, you 'd better ask your user if he accepts synchronizing. at last, are you sure you absolutely need a time that precise ?

Categories

Resources