Tamgotchi like app day/night cycle - android

To learn android I'm making a tamagotchi like app. Its food lvl decreases 1 every hour so if you dont feed it for some hours it dies. I also have that between 8pm and 8am its asleep. Only there is a problem. To change its state to sleeping you need to open the app between 8pm and 8am. That gives the following problem:
If you feed it, lets say at 7pm, 1 hour before it sleeps, and you dont open the app between 8pm and 8 am but at 9am the following day he thinks 13 hours have elapsed instead of 1 (he shouldnt count the sleeping hours) Do you guys ahve any tips?
this is the sleepy and decay code
public void checkSleepyTime()
{
c = Calendar.getInstance();
int hour = c.get(Calendar.HOUR_OF_DAY);
int daypart = c.get(Calendar.AM_PM);
if (hour >= 20 && daypart == 1)
{
foodButton.setText("ZZzz");
prefs.edit().remove("foodTime").commit();
buddy.setSleeping(true);
}
else
{
foodButton.setText("Awake");
buddy.setSleeping(false);
}
}
.
public void initBuddy()
{
debugView.setText("FoodLevel: " + buddy.getFoodLevel());
if(!buddy.getSleeping() && buddy.getAlive())
{
long currentTime = prefs.getLong("currentTime", getCurentTime());
long foodTime = prefs.getLong("foodTime", getCurentTime());
while (foodTime < currentTime)
{
if (currentTime - foodTime >= ONE_HOUR)
{
buddy.decayFood();
}
foodTime = foodTime + ONE_HOUR;
}
}

If the time since last feeding is longer than sleep time, check if the expected sleep period falls into that time. Then act accordingly (quick hack would be to add the sleep time to the time of last feeding). Also check if more than one day has passed since the feed time. Something like this:
if (currentTime - foodTime >= ONE_HOUR)
{
if (currentTime - foodTime >= WHOLE_NIGHT && sleepPeriodFitsInBetween(foodTime, currentTime))
{
foodTime+=WHOLE_NIGHT;
int numberOfFullDays=countNumberOfDays(currentTime - foodTime);
if(numberOfFullDays>1)
{
currentTime+=numberOfFullDays*(24-WHOLE_HIGHT); // assuming WHOLE_NIGHT is in hours.
}
}
...
}

I now have the following two methods that get the current time and the close time (saved in onStop();) and i converted it to days/weeks/months/years. I want to check how many nights there are between the closeTIme and the currentTIme but I'm not sure what to do with the info. Can anyone point me in the right direction ?
public void checkSleepTimes()
{
closeCalendar = timeHelper.convertLongToCal(loadCloseTime());
closeArray = loadDateArrayList(closeArray, closeCalendar);
currentCalendar = timeHelper.convertLongToCal(getCurentTime());
closeArray = loadDateArrayList(currentArray, currentCalendar);
long curTime = getCurentTime();
int times = 0;
long totalSleepTime = 0;
while (closeTime < curTime)
{
closeTime = closeTime + WHOLE_DAY;
times ++;
}
//TODO Check how many times it was between 08:00 and 20:00
totalSleepTime = SLEEP_TIME * times; // not sure if correct approach
}
public ArrayList loadDateArrayList(ArrayList arrayList, Calendar calendar)
{
arrayList.add(0,calendar.DATE);
arrayList.add(1,calendar.WEEK_OF_YEAR);
arrayList.add(2,calendar.MONTH);
arrayList.add(3,calendar.YEAR);
return arrayList;
}

Related

How to count app usage time while app is on foreground?

I'm working on an android app for tracking daily app usage. The idea is that a user can set daily time limit for any app and a notification will appear within at most 2 minutes after the limit is exceeded. (The reason for delay: I've created an alarm system using AlarmManager class that will go off every minute to run a JobIntentService which will check whether limit for any app is exceeded)
I've used queryEvents method of UsageStatsManager class to count app usage time.
Here's my code for counting app usage time (I've followed How to use queryEvents):
HashMap<String, Integer> getTimeSpent(Context context, String packageName, long beginTime, long endTime) {
UsageEvents.Event currentEvent;
List<UsageEvents.Event> allEvents = new ArrayList<>();
HashMap<String, Integer> appUsageMap = new HashMap<>();
UsageStatsManager usageStatsManager = (UsageStatsManager)context.getSystemService(Context.USAGE_STATS_SERVICE);
UsageEvents usageEvents = usageStatsManager.queryEvents(beginTime, endTime);
while (usageEvents.hasNextEvent()) {
currentEvent = new UsageEvents.Event();
usageEvents.getNextEvent(currentEvent);
if(currentEvent.getPackageName().equals(packageName) || packageName == null) {
if (currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED
|| currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED) {
allEvents.add(currentEvent);
String key = currentEvent.getPackageName();
if (appUsageMap.get(key) == null)
appUsageMap.put(key, 0);
}
}
}
for (int i = 0; i < allEvents.size() - 1; i++) {
UsageEvents.Event E0 = allEvents.get(i);
UsageEvents.Event E1 = allEvents.get(i + 1);
if (E0.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED
&& E1.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED
&& E0.getClassName().equals(E1.getClassName())) {
int diff = (int)(E1.getTimeStamp() - E0.getTimeStamp());
diff /= 1000;
Integer prev = appUsageMap.get(E0.getPackageName());
if(prev == null) prev = 0;
appUsageMap.put(E0.getPackageName(), prev + diff);
}
}
return appUsageMap;
}
In short the above code counts the time difference of the timestamp when an app goes foreground (UsageEvents.Event.ACTIVITY_RESUMED) and the timestamp when it goes background (UsageEvents.Event.ACTIVITY_PAUSED). Then it adds this difference to the total usage time of the app.
The problem is that the amount of time spent on foreground can't be counted unless the app goes background. So, if usage limit is exceeded, notification won't appear until the app goes background.
Is it actually possible to get foreground time while app is on foreground?
N.B. I've tried queryUsageStats along with UsageStats.getTotalTimeInForeground() but couldn't succeed since queryUsageStats had some other issues not related to this question.
I've solved the issue.
Adding difference of current time and timestamp of current running app going foreground does the trick.
I just added the following code before the return statement:
UsageEvents.Event lastEvent = allEvents.get(allEvents.size() - 1);
if(lastEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED) {
int diff = (int)System.currentTimeMillis() - (int)lastEvent.getTimeStamp();
diff /= 1000;
Integer prev = appUsageMap.get(lastEvent.getPackageName());
if(prev == null) prev = 0;
appUsageMap.put(lastEvent.getPackageName(), prev + diff);
}
It is pretty straightforward, I should have thought about it before posting the question.

Android - UsageStats getTotalTimeInForeground doesn't update current foreground app [duplicate]

I'm working on an android app for tracking daily app usage. The idea is that a user can set daily time limit for any app and a notification will appear within at most 2 minutes after the limit is exceeded. (The reason for delay: I've created an alarm system using AlarmManager class that will go off every minute to run a JobIntentService which will check whether limit for any app is exceeded)
I've used queryEvents method of UsageStatsManager class to count app usage time.
Here's my code for counting app usage time (I've followed How to use queryEvents):
HashMap<String, Integer> getTimeSpent(Context context, String packageName, long beginTime, long endTime) {
UsageEvents.Event currentEvent;
List<UsageEvents.Event> allEvents = new ArrayList<>();
HashMap<String, Integer> appUsageMap = new HashMap<>();
UsageStatsManager usageStatsManager = (UsageStatsManager)context.getSystemService(Context.USAGE_STATS_SERVICE);
UsageEvents usageEvents = usageStatsManager.queryEvents(beginTime, endTime);
while (usageEvents.hasNextEvent()) {
currentEvent = new UsageEvents.Event();
usageEvents.getNextEvent(currentEvent);
if(currentEvent.getPackageName().equals(packageName) || packageName == null) {
if (currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED
|| currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED) {
allEvents.add(currentEvent);
String key = currentEvent.getPackageName();
if (appUsageMap.get(key) == null)
appUsageMap.put(key, 0);
}
}
}
for (int i = 0; i < allEvents.size() - 1; i++) {
UsageEvents.Event E0 = allEvents.get(i);
UsageEvents.Event E1 = allEvents.get(i + 1);
if (E0.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED
&& E1.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED
&& E0.getClassName().equals(E1.getClassName())) {
int diff = (int)(E1.getTimeStamp() - E0.getTimeStamp());
diff /= 1000;
Integer prev = appUsageMap.get(E0.getPackageName());
if(prev == null) prev = 0;
appUsageMap.put(E0.getPackageName(), prev + diff);
}
}
return appUsageMap;
}
In short the above code counts the time difference of the timestamp when an app goes foreground (UsageEvents.Event.ACTIVITY_RESUMED) and the timestamp when it goes background (UsageEvents.Event.ACTIVITY_PAUSED). Then it adds this difference to the total usage time of the app.
The problem is that the amount of time spent on foreground can't be counted unless the app goes background. So, if usage limit is exceeded, notification won't appear until the app goes background.
Is it actually possible to get foreground time while app is on foreground?
N.B. I've tried queryUsageStats along with UsageStats.getTotalTimeInForeground() but couldn't succeed since queryUsageStats had some other issues not related to this question.
I've solved the issue.
Adding difference of current time and timestamp of current running app going foreground does the trick.
I just added the following code before the return statement:
UsageEvents.Event lastEvent = allEvents.get(allEvents.size() - 1);
if(lastEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED) {
int diff = (int)System.currentTimeMillis() - (int)lastEvent.getTimeStamp();
diff /= 1000;
Integer prev = appUsageMap.get(lastEvent.getPackageName());
if(prev == null) prev = 0;
appUsageMap.put(lastEvent.getPackageName(), prev + diff);
}
It is pretty straightforward, I should have thought about it before posting the question.

Google Fit steps returning for wrong day

I have a user who is experiencing an odd bug but I don't have a pre-existing model or idea of how it's happening and can't replicate it myself. Our app gets from Google Fit's API the previous day's steps value. The user is at GMT +5, but when I test from that time zone I am unable to replicate the issue, nor do my app's other users in GMT +X time zones experience this problem.
At the moment I don't really even know where to start looking/what additional information I can provide to be more helpful about solving this problem. The code that we use to pull the steps value is below. Any help, or even any tips/questions on additional helpful data would be awesome.
Code Flow
Javascript: It's a hybrid app using PhoneGap, so it starts and ends in javascript. Relevant fields passed are startDate, endDate, and bucket:
navigator.health.queryAggregated({
startDate: new Date(new Date().getTime() - 6 * 24 * 60 * 60 * 1000), // seven days ago
endDate: new Date(), // now
dataType: 'steps',
bucket: 'day'
},
function(data){
data.forEach(function(data_entry){
var date = new Date(data_entry.startDate).toISOString().substring(0, 10);
steps_record[date] = data_entry.value;
})
}
)
Java: The java code is where the steps value is actually retrieved, the day bucketing determined by startDate/st, endDate/et, and bucket/hasbucket.
long st = args.getJSONObject(0).getLong("startDate");
long et = args.getJSONObject(0).getLong("endDate");
String datatype = args.getJSONObject(0).getString("dataType");
boolean hasbucket = args.getJSONObject(0).has("bucket");
boolean customBuckets = false;
String bucketType = "";
if (hasbucket) {
bucketType = args.getJSONObject(0).getString("bucket");
Calendar c = Calendar.getInstance();
c.setTimeInMillis(st);
c.clear(Calendar.MINUTE);
c.clear(Calendar.SECOND);
c.clear(Calendar.MILLISECOND);
if (!bucketType.equalsIgnoreCase("hour")) {
c.set(Calendar.HOUR_OF_DAY, 0);
if (bucketType.equalsIgnoreCase("week")) {
c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
} else if (bucketType.equalsIgnoreCase("month")) {
c.set(Calendar.DAY_OF_MONTH, 1);
} else if (bucketType.equalsIgnoreCase("year")) {
c.set(Calendar.DAY_OF_YEAR, 1);
}
}
st = c.getTimeInMillis();
c.setTimeInMillis(et);
c.clear(Calendar.MINUTE);
c.clear(Calendar.SECOND);
c.clear(Calendar.MILLISECOND);
if (bucketType.equalsIgnoreCase("hour")) {
c.add(Calendar.HOUR_OF_DAY, 1);
} else {
c.set(Calendar.HOUR_OF_DAY, 0);
if (bucketType.equalsIgnoreCase("day")) {
c.add(Calendar.DAY_OF_YEAR, 1);
} else if (bucketType.equalsIgnoreCase("week")) {
c.add(Calendar.DAY_OF_YEAR, 7);
} else if (bucketType.equalsIgnoreCase("month")) {
c.add(Calendar.MONTH, 1);
} else if (bucketType.equalsIgnoreCase("year")) {
c.add(Calendar.YEAR, 1);
}
}
et = c.getTimeInMillis();
}
DataReadRequest.Builder builder = new DataReadRequest.Builder();
builder.setTimeRange(st, et, TimeUnit.MILLISECONDS);
int allms = (int) (et - st);
if (hasbucket) {
if (bucketType.equalsIgnoreCase("hour")) {
builder.bucketByTime(1, TimeUnit.HOURS);
} else if (bucketType.equalsIgnoreCase("day")) {
builder.bucketByTime(1, TimeUnit.DAYS);
} else {
// use days, then will need to aggregate manually
builder.bucketByTime(1, TimeUnit.DAYS);
}
} else {
builder.bucketByTime(allms, TimeUnit.MILLISECONDS);
}
Based from this thread, make sure that your timezone settings on both sides are matched and correct for your current location as well as any variation in Daylight Savings Time in your region.
Here's a related thread which might also help: Google FIT api returns different step counts

Android Java - Joda Date is slow

Using Joda 1.6.2 with Android
The following code hangs for about 15 seconds.
DateTime dt = new DateTime();
Originally posted this post
Android Java - Joda Date is slow in Eclipse/Emulator -
Just tried it again and its still not any better. Does anyone else have this problem or know how to fix it?
I also ran into this problem. Jon Skeet's suspicions were correct, the problem is that the time zones are being loaded really inefficiently, opening a jar file and then reading the manifest to try to get this information.
However, simply calling DateTimeZone.setProvider([custom provider instance ...]) is not sufficient because, for reasons that don't make sense to me, DateTimeZone has a static initializer where it calls getDefaultProvider().
To be completely safe, you can override this default by setting this system property before you ever call anything in the joda.
In your activity, for example, add this:
#Override
public void onCreate(Bundle savedInstanceState) {
System.setProperty("org.joda.time.DateTimeZone.Provider",
"com.your.package.FastDateTimeZoneProvider");
}
Then all you have to do is define FastDateTimeZoneProvider. I wrote the following:
package com.your.package;
public class FastDateTimeZoneProvider implements Provider {
public static final Set<String> AVAILABLE_IDS = new HashSet<String>();
static {
AVAILABLE_IDS.addAll(Arrays.asList(TimeZone.getAvailableIDs()));
}
public DateTimeZone getZone(String id) {
if (id == null) {
return DateTimeZone.UTC;
}
TimeZone tz = TimeZone.getTimeZone(id);
if (tz == null) {
return DateTimeZone.UTC;
}
int rawOffset = tz.getRawOffset();
//sub-optimal. could be improved to only create a new Date every few minutes
if (tz.inDaylightTime(new Date())) {
rawOffset += tz.getDSTSavings();
}
return DateTimeZone.forOffsetMillis(rawOffset);
}
public Set getAvailableIDs() {
return AVAILABLE_IDS;
}
}
I've tested this and it appears to work on Android SDK 2.1+ with joda version 1.6.2. It can of course be optimized further, but while profiling my app (mogwee), this decreased the DateTimeZone initialize time from ~500ms to ~18ms.
If you are using proguard to build your app, you'll have to add this line to proguard.cfg because Joda expects the class name to be exactly as you specify:
-keep class com.your.package.FastDateTimeZoneProvider
I strongly suspect it's because it's having to build the ISO chronology for the default time zone, which probably involves reading all the time zone information in.
You could verify this by calling ISOChronology.getInstance() first - time that, and then time a subsequent call to new DateTime(). I suspect it'll be fast.
Do you know which time zones are going to be relevant in your application? You may find you can make the whole thing much quicker by rebuilding Joda Time with a very much reduced time zone database. Alternatively, call DateTimeZone.setProvider() with your own implementation of Provider which doesn't do as much work.
It's worth checking whether that's actually the problem first, of course :) You may also want to try explicitly passing in the UTC time zone, which won't require reading in the time zone database... although you never know when you'll accidentally trigger a call which does require the default time zone, at which point you'll incur the same cost.
I only need UTC in my application. So, following unchek's advice, I used
System.setProperty("org.joda.time.DateTimeZone.Provider", "org.joda.time.tz.UTCProvider");
org.joda.time.tz.UTCProvider is actually used by JodaTime as the secondary backup, so I thought why not use it for primary use? So far so good. It loads fast.
The top answer provided by plowman is not reliable if you must have precise timezone computations for your dates. Here is an example of problem that can happen:
Suppose your DateTime object is set for 4:00am, one hour after daylight savings have started that day. When Joda checks the FastDateTimeZoneProvider provider before 3:00am (i.e., before daylight savings) it will get a DateTimeZone object with the wrong offset because the tz.inDaylightTime(new Date()) check will return false.
My solution was to adopt the recently published joda-time-android library. It uses the core of Joda but makes sure to load a time zone only as needed from the raw folder. Setting up is easy with gradle. In your project, extend the Application class and add the following on its onCreate():
public class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
JodaTimeAndroid.init(this);
}
}
The author wrote a blog post about it last year.
I can confirm this issue with version 1, 1.5 and 1.62 of joda. Date4J is working well for me as an alternative.
http://www.date4j.net/
I just performed the test that #"Name is carl" posted, on several devices. I must note that the test is not completely valid and the results are misleading (in that it only reflects a single instance of DateTime).
From his test, When comparing DateTime to Date, DateTime is forced to parse the String ts, where Date does not parse anything.
While the initial creation of the DateTime was accurate, it ONLY takes that much time on the very FIRST creation... every instance after that was 0ms (or very near 0ms)
To verify this, I used the following code and created 1000 new instances of DateTime on an OLD Android 2.3 device
int iterations = 1000;
long totalTime = 0;
// Test Joda Date
for (int i = 0; i < iterations; i++) {
long d1 = System.currentTimeMillis();
DateTime d = new DateTime();
long d2 = System.currentTimeMillis();
long duration = (d2 - d1);
totalTime += duration;
log.i(TAG, "datetime : " + duration);
}
log.i(TAG, "Average datetime : " + ((double) totalTime/ (double) iterations));
My results showed:
datetime : 264
datetime : 0
datetime : 0
datetime : 0
datetime : 0
datetime : 0
datetime : 0
...
datetime : 0
datetime : 0
datetime : 1
datetime : 0
...
datetime : 0
datetime : 0
datetime : 0
So, the result was that the first instance was 264ms and more than 95% of the following were 0ms (I occasionally had a 1ms, but never had a value larger than 1ms).
Hope this gives a clearer picture of the cost of using Joda.
NOTE: I was using joda-time version 2.1
Using dlew/joda-time-android gradle dependency it takes only 22.82 ms (milliseconds). So I recommend you using it instead of overriding anything.
I found solution for me. I load UTC and default time zone. So it's loads very fast. And I think in this case I need catch broadcast TIME ZONE CHANGE and reload default time zone.
public class FastDateTimeZoneProvider implements Provider {
public static final Set<String> AVAILABLE_IDS = new HashSet<String>();
static {
AVAILABLE_IDS.add("UTC");
AVAILABLE_IDS.add(TimeZone.getDefault().getID());
}
public DateTimeZone getZone(String id) {
int rawOffset = 0;
if (id == null) {
return DateTimeZone.getDefault();
}
TimeZone tz = TimeZone.getTimeZone(id);
if (tz == null) {
return DateTimeZone.getDefault();
}
rawOffset = tz.getRawOffset();
//sub-optimal. could be improved to only create a new Date every few minutes
if (tz.inDaylightTime(new Date())) {
rawOffset += tz.getDSTSavings();
}
return DateTimeZone.forOffsetMillis(rawOffset);
}
public Set getAvailableIDs() {
return AVAILABLE_IDS;
}
}
This quick note to complete the answer about date4j from #Steven
I ran a quick and dirty benchmark comparing java.util.Date, jodatime and date4j on the weakest android device I have (HTC Dream/Sapphire 2.3.5).
Details : normal build (no proguard), implementing the FastDateTimeZoneProvider for jodatime.
Here's the code:
String ts = "2010-01-19T23:59:59.123456789";
long d1 = System.currentTimeMillis();
DateTime d = new DateTime(ts);
long d2 = System.currentTimeMillis();
System.err.println("datetime : " + dateUtils.durationtoString(d2 - d1));
d1 = System.currentTimeMillis();
Date dd = new Date();
d2 = System.currentTimeMillis();
System.err.println("date : " + dateUtils.durationtoString(d2 - d1));
d1 = System.currentTimeMillis();
hirondelle.date4j.DateTime ddd = new hirondelle.date4j.DateTime(ts);
d2 = System.currentTimeMillis();
System.err.println("date4j : " + dateUtils.durationtoString(d2 - d1));
Here are the results :
debug | normal
joda : 3s (3577ms) | 0s (284ms)
date : 0s (0) | 0s (0s)
date4j : 0s (55ms) | 0s (2ms)
One last thing, the jar sizes :
jodatime 2.1 : 558 kb
date4j : 35 kb
I think I'll give date4j a try.
You could also checkout Jake Wharton's JSR-310 backport of the java.time.* packages.
This library places the timezone information as a standard Android asset and provides a custom loader for parsing it efficiently. [It] offers the standard APIs in Java 8 as a much smaller package in not only binary size and method count, but also in API size.
Thus, this solution provides a smaller binary-size library with a smaller method count footprint, combined with an efficient loader for Timezone data.
As already mentioned you could use the joda-time-android library.
Do not use FastDateTimeZoneProvider proposed by #ElijahSh and #plowman. Because it is treat DST offset as standart offset for the selected timezone. As it will give "right" results for the today and for the rest of a half of a year before the next DST transition occurs. But it will defenetly give wrong result for the day before DST transition, and for the day after next DST transition.
The right way to utilize system's timezones with JodaTime:
public class AndroidDateTimeZoneProvider implements org.joda.time.tz.Provider {
#Override
public Set<String> getAvailableIDs() {
return new HashSet<>(Arrays.asList(TimeZone.getAvailableIDs()));
}
#Override
public DateTimeZone getZone(String id) {
return id == null
? null
: id.equals("UTC")
? DateTimeZone.UTC
: Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
? new AndroidNewDateTimeZone(id)
: new AndroidOldDateTimeZone(id);
}
}
Where AndroidOldDateTimeZone:
public class AndroidOldDateTimeZone extends DateTimeZone {
private final TimeZone mTz;
private final Calendar mCalendar;
private long[] mTransition;
public AndroidOldDateTimeZone(final String id) {
super(id);
mTz = TimeZone.getTimeZone(id);
mCalendar = GregorianCalendar.getInstance(mTz);
mTransition = new long[0];
try {
final Class tzClass = mTz.getClass();
final Field field = tzClass.getDeclaredField("mTransitions");
field.setAccessible(true);
final Object transitions = field.get(mTz);
if (transitions instanceof long[]) {
mTransition = (long[]) transitions;
} else if (transitions instanceof int[]) {
final int[] intArray = (int[]) transitions;
final int size = intArray.length;
mTransition = new long[size];
for (int i = 0; i < size; i++) {
mTransition[i] = intArray[i];
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public TimeZone getTz() {
return mTz;
}
#Override
public long previousTransition(final long instant) {
if (mTransition.length == 0) {
return instant;
}
final int index = findTransitionIndex(instant, false);
if (index <= 0) {
return instant;
}
return mTransition[index - 1] * 1000;
}
#Override
public long nextTransition(final long instant) {
if (mTransition.length == 0) {
return instant;
}
final int index = findTransitionIndex(instant, true);
if (index > mTransition.length - 2) {
return instant;
}
return mTransition[index + 1] * 1000;
}
#Override
public boolean isFixed() {
return mTransition.length > 0 &&
mCalendar.getMinimum(Calendar.DST_OFFSET) == mCalendar.getMaximum(Calendar.DST_OFFSET) &&
mCalendar.getMinimum(Calendar.ZONE_OFFSET) == mCalendar.getMaximum(Calendar.ZONE_OFFSET);
}
#Override
public boolean isStandardOffset(final long instant) {
mCalendar.setTimeInMillis(instant);
return mCalendar.get(Calendar.DST_OFFSET) == 0;
}
#Override
public int getStandardOffset(final long instant) {
mCalendar.setTimeInMillis(instant);
return mCalendar.get(Calendar.ZONE_OFFSET);
}
#Override
public int getOffset(final long instant) {
return mTz.getOffset(instant);
}
#Override
public String getShortName(final long instant, final Locale locale) {
return getName(instant, locale, true);
}
#Override
public String getName(final long instant, final Locale locale) {
return getName(instant, locale, false);
}
private String getName(final long instant, final Locale locale, final boolean isShort) {
return mTz.getDisplayName(!isStandardOffset(instant),
isShort ? TimeZone.SHORT : TimeZone.LONG,
locale == null ? Locale.getDefault() : locale);
}
#Override
public String getNameKey(final long instant) {
return null;
}
#Override
public TimeZone toTimeZone() {
return (TimeZone) mTz.clone();
}
#Override
public String toString() {
return mTz.getClass().getSimpleName();
}
#Override
public boolean equals(final Object o) {
return (o instanceof AndroidOldDateTimeZone) && mTz == ((AndroidOldDateTimeZone) o).getTz();
}
#Override
public int hashCode() {
return 31 * super.hashCode() + mTz.hashCode();
}
private long roundDownMillisToSeconds(final long millis) {
return millis < 0 ? (millis - 999) / 1000 : millis / 1000;
}
private int findTransitionIndex(final long millis, final boolean isNext) {
final long seconds = roundDownMillisToSeconds(millis);
int index = isNext ? mTransition.length : -1;
for (int i = 0; i < mTransition.length; i++) {
if (mTransition[i] == seconds) {
index = i;
}
}
return index;
}
}
The AndroidNewDateTimeZone.java same as "Old" one but based on android.icu.util.TimeZone instead.
I have created a fork of Joda Time especially for this. It loads for only ~29 ms in debug mode and ~2ms in release mode. Also it has less weight as it doesn't include timezone database.

how to change format of chronometer?

One of my problem is of changing the format of chronometer in android.
i have the problem that chronometer shows its time in 00:00 format and i want it to come in 00:00:00 format.does anyone knows the answer?
Here is an easy and smart solution for time format 00:00:00 in chronometer in android
Chronometer timeElapsed = (Chronometer) findViewById(R.id.chronomete);
timeElapsed.setOnChronometerTickListener(new OnChronometerTickListener(){
#Override
public void onChronometerTick(Chronometer cArg) {
long time = SystemClock.elapsedRealtime() - cArg.getBase();
int h = (int)(time /3600000);
int m = (int)(time - h*3600000)/60000;
int s= (int)(time - h*3600000- m*60000)/1000 ;
String hh = h < 10 ? "0"+h: h+"";
String mm = m < 10 ? "0"+m: m+"";
String ss = s < 10 ? "0"+s: s+"";
cArg.setText(hh+":"+mm+":"+ss);
}
});
timeElapsed.setBase(SystemClock.elapsedRealtime());
timeElapsed.start();
I was dealing with the same issue. The easiest way to achieve this goal would be overriding updateText method of the Chronometer, but unfortunately it is a private method so I have done this in this way :
mChronometer.setText("00:00:00");
mChronometer.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
#Override
public void onChronometerTick(Chronometer chronometer) {
CharSequence text = chronometer.getText();
if (text.length() == 5) {
chronometer.setText("00:"+text);
} else if (text.length() == 7) {
chronometer.setText("0"+text);
}
}
});
I know that it would be better to write custom widget but for small project it can be suitable.
Setting format on chronometer allows formatting text surrounding actual time and time is formatted using DateUtils.formatElapsedTime .
Hope that helps.
In Kotlin, found a better solution with no memory allocation for the String every second
cm_timer.format = "00:%s"
cm_timer.setOnChronometerTickListener({ cArg ->
val elapsedMillis = SystemClock.elapsedRealtime() - cArg.base
if (elapsedMillis > 3600000L) {
cArg.format = "0%s"
}
else {
cArg.format = "00:%s"
}
})
where cm_timer is Chronometer
Chronometers setFormat method is used to add formated text like:
"Time %s from start"
resulting
"Time 00:00 from start"
In Chronometer you can not select between formats HH:MM:SS or MM:SS.
// You can use this code. Setting the format and removing it
//Removing the format (write this in oncreate)
if (mChronometerformat) {
mSessionTimeChro.setOnChronometerTickListener(new OnChronometerTickListener() {
#Override
public void onChronometerTick(Chronometer chronometer) {
if (chronometer.getText().toString()
.equals("00:59:59"))
chronometer.setFormat(null);
}
});
}
// Setting the format
mSessionTimeChro.setBase(SystemClock.elapsedRealtime());
if (diffHours == 0) {
mSessionTimeChro.setFormat("00:%s");
mChronometerformat = true;
}
mSessionTimeChro.start();
I was working on a countdown timer that used this format but it was for HH:MM:SS. Is that what youre looking for or are you looking for MM:SS:milliseconds. You could work with this code and make it count up from 0 instead of down. Here is the link -
Formatting countdown timer with 00:00:00 not working. Displays 00:00:00 until paused then displays the time remaining
and this might help -
Show milliseconds with Android Chronometer
Why just not to use Date class??
timeElapsed.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
#Override
public void onChronometerTick(Chronometer cArg) {
long time = SystemClock.elapsedRealtime() - cArg.getBase();
Date date = new Date(time);
DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
String dateFormatted = formatter.format(date);
cArg.setText(dateFormatted);
}
});
Simple and clean

Categories

Resources