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.
Related
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 !
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);
In my application some functionality get closed after 9:00 PM. Its working fine if date setting of device is auto updated. But suppose right now its 10:00 pm and user changed the time 10:00 Pm to 08:00 Pm then my code is working. I want to avoid that thing.
Get the system time in UTC (Universal Coordinate Time). This answer gives it in more detail
DateFormat df = DateFormat.getTimeInstance();
df.setTimeZone(TimeZone.getTimeZone("gmt"));
Calendar cal = df.getCalendar();
int hour = cal.get(Calendar.HOUR_OF_DAY);
if (hour > 21 && hour < 9) // or 9:00pm in your time-zone converted to gmt...
{
// Do something, or don't do something
}
you have to use the external time service like this one. anything else like System.currentTimeMillis() or new Date() can be "hacked" by user
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 ?
I have a program where it relies heavily on identifying the week number for the year. I have done the leg work and figured out all the problems that will cause and settle with this method. I works perfect for years that have 53 weeks and such. My only issue is that when I run it on my emulator for 2.2 it works perfect, like this is week 19 and its correct. when I run it on my phone a G1, the week shows 20. How do I fix this?
Here is my week code:
/**
* Format the date into a number that is the year*100 plus the week i.e. 2008 and its week 11
* would show as 811
* #param - String of a date to create a week id, must be in format of 2011-01-31 (YYYY-MM-DD)
* #return returns next weeks id
*/
public static int getWeekId(String date){
// Set the first day of week to Monday and set the starting new year weeks
// as a full first week in the new year.
Calendar c = Calendar.getInstance();
c.setFirstDayOfWeek(Calendar.MONDAY);
c.setMinimalDaysInFirstWeek(7);
if (!date.equalsIgnoreCase("")) {
String[] token = date.split("-", 3);
int year = Integer.parseInt(token[0]);
int month = Integer.parseInt(token[1])-1; // months are 0-11 stupid..
int day = Integer.parseInt(token[2]);
c.set(year, month, day);
}
int yearWeek = ( (c.get(Calendar.YEAR) - 2000)*100 + (c.get(Calendar.WEEK_OF_YEAR)));
Log.d("getWeekId()"," WEEK_OF_YEAR: " + c.get(Calendar.WEEK_OF_YEAR));
return yearWeek;
}
(I'd leave this in a comment, but my account does not yet have commenting permissions.)
When called with 2011-01-01, the code currently returns 1152. Is this as intended?
For what it's worth, there is likely more intricacy to it than you've written. I don't say this to be mean, it's just that there are probably lots of interesting weird cases that you haven't considered. There is a good Java library that knows a ton of stuff about times, and may have this code written already, Check it out: http://joda-time.sourceforge.net/
Apparently its a bug in the android phone...so watch out for this when using android 1.6.