im working on auto profile changer application for android...this is my 2nd application im developing on android, so dont know all the bells and whistles of android development...
any way...i am having some trouble calculating the next time when the timer code is suppose to execute...mainly the issue is around the time that spans over midnight into the next day...
for example, say that the user created a sleeping profile that starts at 10:30PM and goes until 8AM the next day...using this as a example, the execution is getting inside the proper condition that i have placed in the code, but the nextUpdateInterval is not being set correctly...im still trying to figure out how to account/calculate time that spans over to the next day, and at this point im starting to think that im might be making this task overly complicated? any suggestions?
here is the snippet of the code:
timer.scheduleAtFixedRate( new TimerTask() {
public void run() {
//..... some code to convert user stored times into proper time formats
//PM to PM
if(isFromTimePM == true && isToTimePM == true){
}//PM to AM
else if(isFromTimePM == true && isToTimePM == false){
if(rightNowDate.getTime() >= fromDate.getTime() && rightNowDate.getTime() >= toDate.getTime()){
foundProfileIndex = i;
i = profileArrayListSize;
nextUpdateInterval = rightNowDate.getTime() - toDate.getTime();
}
}//AM to AM
else if(isFromTimePM == false && isToTimePM == false){
}//AM to PM
else if(isFromTimePM == false && isToTimePM == true){
}}, 0, nextUpdateInterval);
Thanks,
P
Forget about PM and AM. Since you appear to be using dates, when you use the getTime() method you are getting the number of milliseconds elapsed since the Epoch. Simply subtract these two and you will have the time interval in milliseconds:
long nextUpdateInterval = secondDate.getTime() - firstDate.getTime();
The nextUpdateInterval is a long and is 'time in milliseconds between successive task executions'.
none of this secondDate - firstDate was ever necessary, what i ended up doing was the following:
converted all times to miliseconds
converted the milliseconds into the date objects
used the compareTo to compare the 'to' 'from' dates to current date/time
if i found a profile match, then i stored the 'to' date as the next run date
created a reScheduleTimer method that took date as a paramter...and i just used the .schdule(timertask, date) to resechule the timer...
Related
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
}
I'm trying to use UsageStatsManager to get the foreground app on a Nexus 5 with Marshmallow. I remember it used to work, but for some reason I'm getting null strings for package/class names now.
Here's my implementation
public String[] getForegroundPackageNameClassNameByUsageStats() {
String packageNameByUsageStats = null;
String classByUsageStats = null;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager)getSystemService("usagestats");
final long INTERVAL = 1000;
final long end = System.currentTimeMillis();
final long begin = end - INTERVAL;
final UsageEvents usageEvents = mUsageStatsManager.queryEvents(begin, end);
while (usageEvents.hasNextEvent()) {
UsageEvents.Event event = new UsageEvents.Event();
usageEvents.getNextEvent(event);
if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
packageNameByUsageStats = event.getPackageName();
classByUsageStats = event.getClassName();
Log.d(TAG, "packageNameByUsageStats is" + packageNameByUsageStats + ", classByUsageStats is " + classByUsageStats);
}
}
}
return new String[]{packageNameByUsageStats,classByUsageStats};
}
For some reason, it doesn't go into the while loop, i.e. usageEvents.hasNextEvent() is false. Because of this, it returns null package/class names.
Any idea what I'm doing wrong?
Thanks.
OK, so I found that once I set the interval to 10000 instead of 1000, it works. Apparently a 1s interval is too small.
I am using this myself. I think the usage stats will only be updated when an app comes to foreground. So if the foreground app got to the foreground (and stayed) before your 'begin' timestamp then you will not get it. :(
On the other hand when you use a long time ago you will get a giant list where you only need the highest time to determine the foreground app.
So what I do is I create 3 different times: 1min ago, 1 hour ago and 12 hours ago.
When I get an empty list on 1min I repeat request with 1h and so on. That way I get foreground most of the time. But I never get it to work ALL of the time.
I really miss the old way of just asking the package manager which app is foreground (prior to android 5), the new way is a bit messy.
I was able to make an app with a scrollable calendar, like the image:
Preview and Component Tree
The calendar itself, for 2016, looks like this:
2016 Calendar
It's a five week based calendar, and it's meant to be built like that, for its purposes.
When you run the app you have a calendar, with a week or so displayed.Then you can scroll the days and see the whole calendar. The months names stand still on the left.
Clicking the arrows updates the calendar, showing past and future years, as expected.
It works.
I still don't understand A LOT of java, but I could manage to make it work.
One search here, another there, one tutorial here, another example of code there...
It works.
The problem is: it's too slow to update the calendar.
It takes about 3 seconds or so to update the days, and you can't scroll the calendar during that time.
After the update the scroll is normal. You go right and left. No problem.
It's the processes to update all those TextViews that's killing me... Yes, I do have A LOT of TextViews, and that's where I think I could start getting some help.
The calendar is designed to display five weeks, monday to sunday, which gives me 35 TextViews per month... that's 420 TextViews...
The following method clears the calendar prior to update:
public void Clears_the_Calendar() {
int iId;
TextView tvDay;
for (iId = 0; iId < 420; iId++) {
tvDay= (TextView)findViewById(iarrTextView_Days_ids[iId]);
tvDay.setText(" ");
}
}
As you can see, I use the array iarrTextView_Days_ids to reference the TextViews. It looks like:
int[] iarrTextView_Days_ids = new int[]
{R.id.M01_D01, R.id.M01_D02, ... , R.id.M01_D34, R.id.M01_D35,
R.id.M02_D01, R.id.M02_D02, ... , R.id.M02_D34, R.id.M02_D35,
.
.
.
R.id.M12_D01, R.id.M12_D02, ... , R.id.M12_D34, R.id.M12_D35}
And this is what I use to build the calendar:
private void mtBuild_Calendar(int iYear, int iColumn) {
int i, iId, iDay, iMonth1, iMonth2, iTotal_Days;
TextView tvDay;
Date dtDate = null;
int iRight_Column = 35;
String sDay, sDate = "01/01/" + iYear;
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Calendar cMain_Calendar = Calendar.getInstance();
Calendar cAuxiliary_Calendar = Calendar.getInstance();
try {
dtDate = sdf.parse(sDate);
cMain_Calendar.setTime(dtDate);
cAuxiliary_Calendar.setTime(dtDate);
} catch (ParseException e) {
e.printStackTrace();
}
cMain_Calendar.setTime(dtDate);
cAuxiliary_Calendar.setTime(dtDate);
Clears_the_Calendar();
// displays "1" (jan/1), according to iColumn, which is determined in another method:
iId = iColumn - 1;
tvDay = (TextView)findViewById(iarrTextView_Days_ids[iId]);
tvDay.setText("1");
// It's not important to be extremely precise about leap years:
if ((iYear % 4) == 0) {
iTotal_Days = 366;
} else {
iTotal_Days = 365
}
for (i = 1; i < iTotal_Days - 1; i++) {
// Adds a day to the cAuxiliary_Calendar and compares its MONTH with the previous day from cMain_Calendar
cAuxiliary_Calendar.add(Calendar.DATE, 1);
iMonth1 = cMain_Calendar.get(Calendar.MONTH);
iMonth2 = cAuxiliary_Calendar.get(Calendar.MONTH);
// Performs the comparisons needed to change the months:
if (iColumn < iRight_Column) {
if (iMonth2 == iMonth1) {
iColumn = iColumn + 1;
iId = iId + 1;
} else {
iColumn = iColumn + 1;
iId = iId + 36;
}
} else {
if (iMonth2 == iMonth1) {
iColumn = 1;
iId = iId - 34;
} else {
iColumn = 1;
iId = iId + 1;
}
}
// Adds a day to cMain_Calendar and displays it in the proper place according to iId:
cMain_Calendar.add(Calendar.DATE, 1);
iDay = cMain_Calendar.get(Calendar.DAY_OF_MONTH);
sDay = Integer.toString(iDay);
tvDay = (TextView) findViewById(iarrTextView_Days_ids[iId]);
tvDay.setText(sDay);
}
}
At first a method clears all the 420 TextViews, and then just the 365 or 366 needed for the year are fulfilled.
Still, that's a lot of work... this afternoon I started taking a look at canvas.drawText
For a moment it seemed to me that it would be faster, cause for what I understood TextView uses canvas inside its core. But I couldn't even find a way to make a simple canvas.drawText to work inside the ScrollView.
So the questions are:
Should I use canvas.drawText instead of TextViews? How can I do that?
Or, is there any other faster way to display 365 numbers like the app is supposed to do?
(new info, not sure how it looks...)
Trying new thigs:
Perhaps I misunderstood what adapters can do, but for what I've learned it won't help me... cause once an year is fully loaded into the view there's no delay in scrolling the calendar. The problem is how long it takes to refresh all the 365 TextViews when I want to change from one year to another. In fact, I could manage to reduce that delay to less than 1 second at this point, cause I finally follow the Android Studio advice about too many nested weights.. All my TextViews where using that attibute on its style. I removed them all, letting weights just for the LinearLayouts.
Right now I'm stuck in trying to find a way to pass a string to a method which could draw a text into the view. I still can't get a drawText to work inside the ScrollView, cause I can't find a way to call it. Seems to me that it would only works inside an onDraw, which only triggers when I load a view. But I need to get drawText doing its job 420 times (when I clear the calendar) plus 365 times when a new calendar is shown.
Am I supposed to load 800 views into the ScrollView, each time I want to chage the year ???
I also changed the main LinearLayout to RelativeLAyout, but things are getting weird cause I had to set a lot of heights (which was 0dp prior to the change) and now I'm having troubles on changing from portrait to landscape... but that's a minor issue.
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;
}
}
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.