How to prevent my application to get cached value of TimeZone.getDefault()? - android

I am using TimeZone.getDefault() to set the timezone of Calendar class:
Calendar cal = Calendar.getInstance(TimeZone.getDefault());
Log.i("TEST", cal.get(Calendar.HOUR) + ":" + cal.get(Calendar.MINUTE));
However, when users change the timezone of their device from Setting, my application represents the time using the former timezone until they force stop (from App info settings) the application and restart it.
How could I prevent the caching of getDefault()?

It's not pretty, but you could potentially call setDefault(null) to explicitly wipe the cached value. As per the documentation, this will only affect the current process (that is, your app).
Having nulled out the cached value, the next time you call getDefault(), the value is reconstructed freshly:
/**
* Returns the user's preferred time zone. This may have been overridden for
* this process with {#link #setDefault}.
*
* <p>Since the user's time zone changes dynamically, avoid caching this
* value. Instead, use this method to look it up for each use.
*/
public static synchronized TimeZone getDefault() {
if (defaultTimeZone == null) {
TimezoneGetter tzGetter = TimezoneGetter.getInstance();
String zoneName = (tzGetter != null) ? tzGetter.getId() : null;
if (zoneName != null) {
zoneName = zoneName.trim();
}
if (zoneName == null || zoneName.isEmpty()) {
try {
// On the host, we can find the configured timezone here.
zoneName = IoUtils.readFileAsString("/etc/timezone");
} catch (IOException ex) {
// "vogar --mode device" can end up here.
// TODO: give libcore access to Android system properties and read "persist.sys.timezone".
zoneName = "GMT";
}
}
defaultTimeZone = TimeZone.getTimeZone(zoneName);
}
return (TimeZone) defaultTimeZone.clone();
}
You should probably combine this with a broadcast listener for ACTION_TIMEZONE_CHANGED and only null out the default value if you receive such a broadcast.
Edit: come to think of it, a neater solution would be to extract the newly set time zone from the broadcast. From the broadcast documentation:
time-zone - The java.util.TimeZone.getID() value identifying the new time zone.
You can then simply use this identifier to update the cached default:
String tzId = ...
TimeZone.setDefault(TimeZone.getTimeZone(tzId));
Any subsequent calls to getDefault() will then return the correct/updated time zone.

Related

Time of Android phone on first boot after unboxing?

What will be the display time and date on first boot after unboxing android device if it is not connected to internet connection or without sim? On what basis the time and date is setup by default?
On the first boot, android will set the system time with Build.TIME. And Build.TIME is get from system prop ro.build.data.rtc, which is the time when the image is build. Some factory test will be executed in factory, so it will have been boot some times. It may not be the first boot when user unbox the phone, and boot it.
This is done by AlarmManagerService.java#onStart.
#Override
public void onStart() {
mInjector.init();
...
synchronized (mLock) {
mHandler = new AlarmHandler();
mConstants = new Constants();
...
// We have to set current TimeZone info to kernel
// because kernel doesn't keep this after reboot
setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
// Ensure that we're booting with a halfway sensible current time. Use the
// most recent of Build.TIME, the root file system's timestamp, and the
// value of the ro.build.date.utc system property (which is in seconds).
final long systemBuildTime = Long.max(
1000L * SystemProperties.getLong("ro.build.date.utc", -1L),
Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
if (mInjector.getCurrentTimeMillis() < systemBuildTime) {
Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis()
+ ", advancing to build time " + systemBuildTime);
mInjector.setKernelTime(systemBuildTime);
}

How to compare date and time in kotlin android?

I have an app on the PlayStore and I am building a feature where the user will not see ads more than a specific number in one day.
I am thinking about comparing the current date and time to the previously saved one but haven't find a proper way to do that.
How can I compare date and time to know if 24 hours have passed or not?
Some posts that I found but not helpful:
medium.com
stackoverflow
stackoverflow
tl;dr
[This Answer uses Java syntax. You’ll have to translate to Kotlin syntax.]
if
(
Duration // Represents elapsed time on the scale of hours-minutes-seconds.
.between( // Calculates elapsed time between two points in time.
Instant.parse( "2021-03-23T15:30:57.013678Z" ) , // Last moment when an ad was show.
Instant.now() // Current moment.
) // Returns a `Duration` object.
.toHours() // Extract total number of whole hours from the `Duration` object.
>= 24L // Test if equals-to or greater-than 24 hours.
)
{ show ad }
java.time
You asked:
… know if 24 hours have passed or not?
Use the modern java.time classes defined in JSR 310. The java.time classes are built into Android 26 and later. Most of the functionality is available in earlier Android using the latest tooling’s “API desugaring“.
Instant adShown = Instant.parse( "2021-03-23T15:30:57.013678Z" ) ;
Instant now = Instant.now() ;
Duration d = Duration.between( adShown , now ) ;
long hoursSinceAdShown = d.toHours() ;
if( hoursSinceAdShown >= 24L ) { … show ad }
Record your next ad-showing as text in standard ISO 8601 format.
String output = Instant.now().toString() ;
2021-03-23T15:30:57.013678Z
Your Question asked for two different things:
Once per day
Every 24 hours
The first involves a calendar, dates, and a time zone. The second does not. I showed you code for the second.
You can use a scheduled executor service to trigger from a background thread the next showing of an ad at a specific moment. Search Stack Overflow to learn more as this has been covered many times already.
Use this code to check the current date, Yesterday or Particulardate. Pass Epoch time to this method
// input format (we get a value as Epoch)
private val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
private val outputFormat = SimpleDateFormat("MMM dd")
// have to pass the time value as Epoch time.
private fun calculateDateMonth(time: String): String {
var returnValue = ""
val dateTime = DateTime((time.toLong()) * 1000L)
val inputTime = inputFormat.parse(dateTime.toString())
val convertDateMonth = outputFormat.format(inputTime!!)
val timeInMilliseconds = outputFormat.parse(convertDateMonth)!!
val mTime: Calendar = Calendar.getInstance()
mTime.setTimeInMillis(timeInMilliseconds.time)
val now = Calendar.getInstance()
returnValue = when {
now[Calendar.DATE] == mTime[Calendar.DATE] // check isToday
now[Calendar.DATE] - mTime[Calendar.DATE] == 1 // check Yesterday
else -> convertDateMonth // Month and Date
}
return returnValue
}

Trying to perform a text change in my textview at a reoccurring time on daily basis in my android project

let say i will like to automatically change my textview text at 02:00pm everyday how do I implement this functionality.
val df = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.JAPAN).parse("2:00pm")
val systemDat = Calendar.getInstance(Locale.JAPAN).after(df)
if (systemDat) {
binding.includeTokyoSession.text_one.text = "successful"
} else {
binding.includeTokyoSession.text_one.text = "failure"
}
I suppose you want to change the text of your TextView after a particular time, but it seems that you're not aware of the date when comparing and you have a couple of mistakes in your code.
First, this line of code:
DateFormat.getTimeInstance(DateFormat.SHORT, Locale.JAPAN).parse("2:00pm")
will return a Date instance with this date and time in your local timezone 01-01-1970 02:00:00. However, you need to get a Date instance with today's date and the time 14:00:00.
Second, this line of code:
Calendar.getInstance(Locale.JAPAN).after(df)
this is a wrong usage of the Calendar::after() function, and that's because you can only pass a Calendar object to the function in order to get the right comparison result, otherwise it will always return false.
In your case you're passing a Date object.
Following is the implementation of the Calendar::after() function.
public boolean after(Object when) {
return when instanceof Calendar
&& compareTo((Calendar)when) > 0;
}
If you want to proper compare the current time today with 14:00 (comparing only the time today), here is a modification to your code:
val calendarToCompare = Calendar.getInstance(Locale.JAPAN).apply {
set(Calendar.HOUR_OF_DAY, 14)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val systemDat = Calendar.getInstance().after(calendarToCompare)
if (systemDat) {
textview.text = "successful"
} else {
textview.text = "failure"
}
If you want to perform a lifecycle-aware view update (ex. to set the text of your textview), you can check this gist.

Update Only Once Per Day

I have some things in my Android application that need to update once per day.
It's pretty simple I think, but I have no idea in what format I need to format the date and time (if time is needed) and how to check if an update has been done today (where today is between 00:01am and 23:59pm in the user's local time). The update should not be done if it was already done for today.
Here's what I DO know how to do:
Save the last update date in SharedPreferences (but how do I get a
string of it, I do not know)
Get things from SharedPreferences (but I
don't know how to compare dates in string format)
It is irrelevant what format you choose. It is just matter of recalculations.
I'd suggest using milliseconds since epoch, as all system calls use it, so it would be easier for you to use the same.
As 1000 millis is 1 second it's easy to figure out that 1000*60*60*24 equals to one day (24hrs). So, if storedMillis is bigger than NOW - 1000*60*60*24, (and NOW is i.e. System.currentTimeMillis()), then it is too early to do the check. If storedMillis is smaller, then save your NOW timestamp and do the check:
long now = System.currentTimeMillis();
long diffMillis = now - lastCheckedMillis;
if( diffMillis >= (3600000 * 24) ) {
// store now (i.e. in shared prefs)
// do the check
} else {
// too early
}
EDIT
I am interested in doing it when the app is first opened for the
current day, even if the last update was done 10 minutes ago.
It's just the matter how to get the proper millis to compare against. Replace long now = System.currentTimeMillis(); from above code with following code block:
Calendar cal = Calendar.getInstance();
cal.clear(Calendar.HOUR);
cal.clear(Calendar.HOUR_OF_DAY);
cal.clear(Calendar.MINUTE);
cal.clear(Calendar.SECOND);
cal.clear(Calendar.MILLISECOND);
long now = cal.getTimeInMillis();
which shall do the trick.
If you store your date in format 20121122 (YYYYmmdd) then you can compare is like 20121122 > 20121123. But it must be stored as int or cast to int when comparing.
Store the timestamp (System.currentTimeMillis() ) of the Last execution and compair it with the currient one. If the difference is more than 24 hours... You know it or?
Set up an Alarm with AlarmManager that executes every 24 hours, then do stuff
Check this question: Alarm Manager Example
It's a more complicated approach than the rest, but makes sure things are done, while with the other options the app must be executed in order to check if it has to update whatever.
Here is the method
public boolean runOnceADay() {
SharedPreferences shp= c.getSharedPreferences(Constants.GENERAL_SHP, MODE_PRIVATE);
SharedPreferences.Editor editor= shp.edit();
long lastCheckedMillis = shp.getLong(Constants.ONCE_A_DAY, 0); // "KEY" you may change yhe value
long now = System.currentTimeMillis();
long diffMillis = now - lastCheckedMillis;
if (diffMillis >= (3600000 * 24)) { // set up your time circulation
editor.putLong(Constants.ONCE_A_DAY, now);
editor.commit();
Util.showMessage(c, "Once a Day Test");
return false;
} else {
Util.showMessage(c, "Too Early");
return true;
}
}

check current time between time interval android

I want to create an app that will allow the user to check whether or not the current time falls between a specified time interval. More specifically, I created a sql table using sqllite program, with the table specifying an end time and a start time for each record. The problem is that the type of data each field can be is limited to text, number, and other type other than a datetime type. So, how would I be able to check if the current time is between the start and end time since the format of time is in h:mm and not just an integer value that I could just do less than or greater than? Do I have to convert the current time to minutes?
You should be able to do the comparison even if time is not stored in the datetime type, here is a link that explains the conversion from string to time.
If that doesn't work, convert the time to seconds (int) and calculate the difference.
Try this. You can save and retrieve your time as String:
String to long: String.valueOf()
long to String: Long.valueOf()
Then, you use this procedure to check time:
//Time checker
private boolean timeCheck(long startTimeInput, long endTimeInput) {
long currentTime = System.currentTimeMillis();
if ((currentTime > startTimeInput) && (currentTime < endTimeInput)) {
return true;
} else {
return false;
}
}
And in your main program check it like this:
//You kept the times as String
if (timeCheck(Long.valueOf(start), Long.valueOf(end))) {
//it is in the interval
} else {
//not in the interval
}
I hope it helps.

Categories

Resources