I am very new to Android so this maybe a really basic question:
I have a Service which starts when a user press a button
The service will start a new task and Loop it every N Seconds.
The seconds are set over the Settings Tab in my Android App.
In the OnStart() Method in the service get the preferences:
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
And set the Settings, for example:
final Long pollInterval = Long.parseLong(settings.getString("pollInterval", "1000"));
The Task is Polling on a Database for new entrys.
My Goal is to restart the Service after going back to the main frame so the onCreate method get the new Preferences or to refresh the preferences in the running task to change the Poll interval.
I tried to restart the Service with:
startService(serviceIntent);
stopService(serviceIntent);
But for any reason this doesn't work.
Thanks in advance for the answers.
Use this one to repeat service
try {
//Create a new PendingIntent and add it to the AlarmManager
Intent intent = new Intent(this, Service.class);
PendingIntent pendingIntent = PendingIntent.getService(this,
12345, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am =
(AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(),
2*60*60,pendingIntent);
} catch (Exception e) {}
Also stop service when your task is completed
Related
I've set an alarm manager to download web content every 24h at 10:40am but apart from executing at proper time, apparently it launches few seconds after successfully running the app. I want this to run exclusively at the set time.
#Override
public void onReceive(Context context, Intent intent) {
mAuth = FirebaseAuth.getInstance();
currentUser = mAuth.getCurrentUser();
DownloadTask task = new DownloadTask();
task.execute("https://api.apify.com/v2/key-value-stores/3Po6TV7wTht4vIEid/records/LATEST?disableRedirect=true");
String contentText = "Nowe zakażenia: " +infectionsInfo+ " Nowe śmierci: "+deathsInfo;
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "notifyLemubit1")
.setSmallIcon(R.drawable.unnamed)
.setContentTitle("Punkty zostały przyznane!")
.setContentText(contentText)
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.notify(200, builder.build());
and
calendar1.set(Calendar.HOUR_OF_DAY,10);
calendar1.set(Calendar.MINUTE,40);
Intent intent1 = new Intent(MenuActivity.this, PointsBroadcast.class);
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(MenuActivity.this, 0, intent1, 0);
AlarmManager alarmManager1 = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager1.setRepeating(AlarmManager.RTC_WAKEUP,calendar1.getTimeInMillis(),AlarmManager.INTERVAL_DAY, pendingIntent1);```
All repeating alarms are inexact in order not to easily let developers abuse them.
If you want the action to be done in that exact time you need to use one-time alarms. To make them "repeating" you need to schedule the first one initially and always schedule the next one once the alarm is fired.
Although your issue might be due to this plus some other issue.
Exact alarms should only be used for extremely important tasks so I suggest considering using WorkManager so you don't affect the battery of the device as much.
So far I've been using AlarmManager and BroadcastReceiver like this:
private void setUpStreakResetAlarm() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.add(Calendar.DAY_OF_MONTH, 1);
Intent intent = new Intent(getApplicationContext(), DailyCounterCheckReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 100, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}
And in my DailyCounterCheckReceiver class:
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
int counterOld = sharedPreferences.getInt(GlobalUtilities.SHARED_PFC_STREAK_COUNTER_OLD_KEY, 0);
int counterToday = sharedPreferences.getInt(GlobalUtilities.SHARED_PFC_STREAK_COUNTER_KEY, 0);
SharedPreferences.Editor editor = sharedPreferences.edit();
// If user has increased counter on this day, increase the old check for tomorrow
if (counterToday > counterOld) {
counterOld = counterToday;
editor.putInt(GlobalUtilities.SHARED_PFC_STREAK_COUNTER_OLD_KEY, counterOld);
editor.putBoolean("increasedOld", true);
editor.putBoolean("reset", false);
}
// etc.....
But with new Android versions, background tasks like that just get killed and it's very unreliable.
So what can I use instead? Work Manager, Foreground Service, something else?
And don't I still need AlarmManager to trigger them?
My use case is extremely simple, so I don't think I need some super complex solution, but there are so many options out there. What is better for my simple case?
Edit:
Angel's comment would solve my reset problem, but I also do the same for triggering notifications at certain times:
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putLong("lastTimeOfNotifTrigger", System.currentTimeMillis());
editor.apply();
repository = new NotificationRepository(context);
repository.startGetNextNotificationAsync(this);
}
How could I solve that in the easiest way?
You could use alarmManager.setExact to schedule the alarms (so that your receiver is called exactly at the time you want even if the phone is sleeping or in Doze mode). Once your receiver is called you can schedule the next. Remember to also add receivers detecting BOOT and PACKAGE_CHANGED so that you can re-schedule the alarms in case the phone is rebooted or the app updated.
You could also use Work Manager as you said to schedule a job, if you don't need it to be run at an exact time.
Your code shouldn't have any other issue running in newer versions of Android if you are not trying to launch an Activity from that receiver (you can't launch an activity anymore from the background).
I want to start activity from notification. I want to open an activity, which is successor of some other activities.
Example activities: IntroActivity -> Photos -> SpecificPhoto. What I want to achieve: In case user clicks on notification, I want to open SpecificPhoto activity. Keep in mind, that app can be running (for example PhotosActivity is displayed), or it can be shut down.
I want to preserve back button functionality (move to PhotosActivity on back pressed).
On notification click, I need to launch IntroActivity, because user needs to login here in case he is not.
I tried following (using constants in activities, code):
On PhotosActivity onCreate:
redirectToActivity();
RedirectToActivity method:
private void redirectToActivity() {
Intent intent = getIntent();
int activityCode = intent.getIntExtra("code", 0);
switch (activityCode) {
case SpecificPhotoActivity.CODE:
startActivity(new Intent(this, SpecificPhotoActivity.class));
break;
default:
return;
}
}
By applying this approach, I can traverse the whole activity stack and go to the activity I want. However, this approach is not working in every case. Sometimes, the activity_code is not set (don't know why) and therefore we end in the first activity.
Is there any more professional approach to solve this issue? I believe this must be solved somehow in many apps.
What you want is called TaskStackBuilder.
Here's how you should construct the intent, that would navigate to SpecificPhotoActivity:
Intent action = new Intent(context, SpecificPhotoActivity.class);
PendingIntent pendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(action)
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
In order to correctly specify stack of activities, you should provide android:parentActivityName inside manifest file:
<application ...>
<activity android:name=".SpecificPhotoActivity"
android:parentActivityName=".PhotosActivity"/>
</application>
With this parameter you have specified, that the parent of SpecificPhotoActivity is PhotoActivity, thus TaskStackBuilder would understand where to navigate as soon as back button is clicked inside SpecificPhotoActivity.
Construction of the notification should be as follows:
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle(...)
.setContentText(...)
.setSmallIcon(...)
.setContentIntent(pendingIntent)
.build();
manager.notify(NOTIFICATION_ID, notification);
Now notification click would open SpecificPhotoActivity. A click on back button would navigate to PhotosActivity.
What's left is authorization handling. I suppose you are able to apprehend whether user is authorized or no during the construction of the notification. Hence, following approach should work:
PendingIntent pendingIntent = null;
if (authorized) {
Intent action = new Intent(context, SpecificPhotoActivity.class);
pendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(action)
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
} else {
Intent action = new Intent(context, IntroActivity.class);
action.putExtra("photos_flow", true);
pendingIntent = PendingIntent.getActivity(context, 0, action, 0);
}
Now, inside IntroActivity after successful authorization:
void onAuthorized() {
if(getIntent().getBooleanExtra("photos_flow", false)) {
// most possibly you should pass some id into SpecificPhotoActivity's intent
Intent[] intents = new Intent[]{new Intent(this, PhotosActivity.class), new Intent(this, SpecificPhotoActivity.class)};
startActivities(intents);
finish();
}
}
My alarm code is working fine as default ringtone is playing well on time. But not able to stop the alarm tone. In receiver class I inserted the following code.
try {
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
Ringtone r = RingtoneManager.getRingtone(context, notification);
r.play();
} catch (Exception e) {
e.printStackTrace();
}
I also inserted the code for cancel the alarm in main class, which is also working well with the code
alarmMgr.cancel(pendingIntent);
But once the default ringtone start playing and user want to stop this by stop button then it is not working.
In main class with stopbutton.setOnClickListener I inserted the following code
Intent intent = new Intent(getBaseContext(), Reciver.class);
stopService(intent);
I think it can be stop by r.stop();
but how should I use it in stop button.
Make a Global instance of Ringtone and on button click use r.stop() use this link to perform the operation in service. Let me know if I understood the question correctly.
A broadcast intent registered while you schedule the alarm but to ensure a cancel you have to registered another intent that would boradcast cancel event to system.
Look at piece of code working fine during a cancel event.
Intent intent = new Intent(this, YourReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 123, intent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
I am trying to schedule an alarm to run even when the app is in the background. The code below runs fine if the app is in the foreground. However, as soon as I press the back button I get a "leaked Intent Receiver error" suggesting that I am missing a call to unregisterReceiver(). I tried adding unregisterReceiver(receiver) to onStop() and onPause(), but continue to get the same error. Any help is much appreciated!
Also a side question, if I do finally figure out how to unregister the receiver, will that prevent my alarm from triggering? Thanks.
// Alarm
public void SetAlarm()
{
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override public void onReceive( Context context, Intent _ )
{
// code to run when alarm hits
context.unregisterReceiver( this ); // this == BroadcastReceiver, not Activity
}
};
this.registerReceiver( receiver, new IntentFilter("com.blah.blah.somemessage") );
PendingIntent pintent = PendingIntent.getBroadcast( this, 0, new Intent("com.blah.blah.somemessage"), 0 );
AlarmManager manager = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
// set alarm to fire 5 sec (1000*5) from now (SystemClock.elapsedRealtime())
manager.set( AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000*10, pintent );
}