This question already has answers here:
How to stop displaying message from Toast when Application is closed?
(5 answers)
Closed 9 years ago.
Assume that you have a Button that whenever you click on it, it displays a Toast with the message "Hello".
If you decide to click on that Button 20 times repeatedly, the Toasts will be displayed asynchronously, waiting each one its turn. However, this is not what I really want.
What I want is the following:
Whenever I press the Button, I want it to cancel the previous displayed Toast and display the actual one. So that when I close the app, no Toasts will be displayed if ever the user decides to mess with the app by clicking on the Button 100 times within a very small period.
You'll need to declare your Toast at a class level, and then call toast.cancel() before constructing a new Toast object and showing it.
public class XYZ extends Activity {
Toast mToast;
public void onCreate(Bundle b) {
.....
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if(mToast != null)
mToast.cancel();
mToast = Toast.makeText.....;
}
});
}
}
Here' another solution. If all you want is to prevent multiple toasts from being displayed for fast clicks then a combination of AlarmManager and a PendingIntent should work too. Now bear in mind, I haven't tested this and haven't checked if it compiles.
AlarmManager mAlarm;
PendingIntent mPendingIntent;
//toast delay for a second
int toastDelay = 1000;
#Override
public void onCreate (Bundle savedInstanceState) {
Intent i = new Intent(context, MySimpleBroadcastReceiver.class);
//optionally set an action
i.setAction("show:toast");
mPendingIntent = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
mAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
public void onClick(View v) {
//set the alarm to trigger 1 second from current time
mAlarm.set(AlarmManager.RTC_WAKEUP, (System.currentTimeMillis() + toastDelay), mPendingIntent);
}
#Override
protected void onDestroy () {
if (mAlarm != null) {
mAlarm.cancel(mPendingIntent);
}
mAlarm = null;
mPendingIntent = null;
}
Create the broadcast receiver and remember to add it to your AndroidManifest.xml:
public class MySimpleBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive (Context context, Intent intent) {
//optionally check the action that triggered the broadcast..useful if you want to use this broadcast for other actions
if (intent.getAction().equals("show:toast")) {
Toast.makeText(context, "Hello", Toast.LENGTH_SHORT).show();
}
}
}
You can read about PendingIntent.FLAG_CANCEL_CURRENT.
Related
I have a dialog box that starts when any push notification arrives and it is of 30 seconds. It is working fine if the app is in foreground but if the app is in background, the main activity starts and dialog box has to appear with the left over timer. But here, I am able to show dialog box but the time again starts from 30 seconds which I don't want.
What I have tried so far:
1. In FirebaseMessagingService, I have set a broadcastReceiver to send broadcast to other activities to start dialog. It is working fine and sending broadcast to start activities. and when I click on notification, dialog box appears but not showing timer.
2. I have tried to use service to start timer and the service is starting but timer is not working.
3. I have created another broadcastReceiver in first broadcast Receiver to set time in dialogBox. This is working even in background but not updating the timer values.
4. Starting countdown timer in background on receiving push notification I have used this as well but in this how to update values.
if(event.equals(eventName)){
String remoteMessageDate = remoteMessage.getData().get(eventName).toString();
intent = new Intent(this, DashboardMapsActivity.class);
Gson gson = new Gson();
booking = gson.fromJson(remoteMessageDate, BookingResult.class);
sendAllocateBroadcast(AppConstants.INSTANCE.getRIDE_REQUEST(), booking);
intent.putExtra(AppConstants.INSTANCE.getRIDE_REQUEST(),booking);
Log.w(TAG, "setActions: "+ Calendar.getInstance().getTime().toLocaleString());
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
// intent.setFlags(Intent.FL);
String message = "You have new booking";
createNotification(message);
BroadcastReceiver
BroadcastReceiver rideRequestreceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.hasExtra(AppConstants.INSTANCE.getRIDE_REQUEST()))
{
Toast.makeText(DashboardMapsActivity.this,"booking Received",Toast.LENGTH_SHORT).show();
booking = (BookingResult) intent.getSerializableExtra(AppConstants.INSTANCE.getRIDE_REQUEST());
countDownTimer(booking.getBooking().getBookingId());
setFCMNewRideDialogValue(booking);
}
}
};
CountDownTimer
private void countDownTimer(final Integer bookingId) {
Intent intent1 = new Intent(this, TimerService.class);
startService(intent1);
countDownTimer = new CountDownTimer(30000,1000) {
#Override
public void onTick(long millisUntilFinished) {
// Log.w(TAG, "onTick: "+ TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished));
rideRequestDialog.setTimerValues(TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished));
}
#Override
public void onFinish() {
// Log.w(TAG, "onFinish: timer finish " );
rideRequestDialog.rideDismiss();
rejectRide(bookingId);
}
}.start();
}
OnCreate
if (intent.hasExtra(AppConstants.INSTANCE.getRIDE_REQUEST())) {
booking = (BookingResult) intent.getSerializableExtra(AppConstants.INSTANCE.getRIDE_REQUEST());
if(!(rideRequestDialog.dialogShowing()))
{
setFCMNewRideDialogValue(booking);
}
} else {
// Do something else
}
I want to show the dialog box with working timer values
I have read every question there is about Android, AlarmManager and cancelling.
I currently use an Activity starting a receiver through:
long msInterval = 1;
Intent intent = new Intent(this, Updater.class);
intent.setAction("theAction");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 12, intent, 0);
Updater.origin = pendingIntent;
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (msInterval), msInterval, pendingIntent);
This starts the receiver Updater one millisecond after this code was called, with request code 12 (chosen arbitrarily, using 0 produces the same incorrect behaviour). It also sets the origin of Updater to the currently scheduled PendingIntent, which is later used to cancel the alarm.
Updater looks like this:
public class Updater extends BroadcastReceiver {
public static int flaggedClose = 0;
public static PendingIntent origin;
#Override
public void onReceive(Context context, Intent intent) {
// Do some work
Log.w("running", "run");
if (Updater.flaggedClose != 0) {
if(flaggedClose == 1) {
Toast.makeText(context, "Finished!", Toast.LENGTH_SHORT).show();
}
flaggedClose++; // Only show Toast once
Log.w("running", "close");
origin.cancel();
AlarmManager alarms = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarms.cancel(origin);
}
}
}
What it does at the moment is just to log the message "run", which is done ~1000 times/s. When the Activity's onStop() is called, Updater.flaggedClose is set to 1. I can be see this in Logcat since it starts printing the log warning "close". However, the alarm is still on, so every other logged message is "run" and every other is "close". In best case, the alarm is closed after a few seconds. Worst case I need to restart the phone. In the description of AlarmManager, it specifically states that close closes "Any alarm, of any type, whose Intent matches this one (as defined by filterEquals(Intent)), will be canceled". Why are there still alarms being triggered?
As ci_ mentioned in the comments, it is possible that "those 100 "extra" alarms already triggered before the cancel happens". For anyone else who has the same problem, here is a solution. I tested the AlarmManager and it seems to work best if you have a delay of at least 200 ms. for a lower delay use a Handler. Example from the question using Handler:
public class MainActivity extends Activity {
private boolean pressed = false;
private boolean done = false;
private Handler worker;
private Runnable method;
long msInterval = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
worker = new Handler();
method = getMethod();
Button toggle = (Button)(findViewById(R.id.toggle));
toggle.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(!pressed) {
worker.post(method);
pressed = true;
} else {
done = true;
}
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
done = true;
}
private Runnable getMethod() {
return new Runnable() {
public void run() {
Log.w("running", "run");
if (done) {
Toast.makeText(getApplicationContext(), "Finished!", Toast.LENGTH_SHORT).show();
Log.w("running", "close");
} else {
worker.postDelayed(method, msInterval);
}
}
};
}
}
On first button press the handler starts the runnable, and on each call the runnable calls itself. On second button press condition done is set to true, thus the runnable finishes after one (cleanup) round.
In my project I am trying to bring my application to front after app is finished (User pressed back button). But it only gives me the last opened app to front. So if I am using a different app in the mean time it will open this app and not my app. Maybe some one can help me - I tried a lot of different combinations with Flags, but it didn't work. Additionally I didn't found a solution to get my app back if the display is locked - Maybe someone has a good solution for that or can give me advice.
Thanks
public class IncomingBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
MyActivity myActivity = (MyActivity) context;
myActivity.doWork();
}
public class MyActivity extends Activity {
private boolean appInBackground = false;
...
#Override
public void onStop() {
appInBackground = true;
super.onStop();
}
#Override
public void finish() {
if (exitPressed) {
super.finish();
} else {
appInBackground = true;
moveTaskToBack(false);
}
}
public void doWork() {
// bring app to foreground
if (appInBackground) {
Intent intent1 = new Intent(getBaseContext(), MyActivity.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent1);
}
....
}
}
Android Manifest launchMode: android:launchMode="singleTask"
UPDATE:
I used my app to indicate incoming SIP calls, I had a look at the demo application for WalkieTalkieActivity (https://android.googlesource.com/platform/development/+/master/samples/SipDemo)
// main Activity
#Override
public void onCreate(Bundle savedInstanceState) {
...
callReceiver = new IncomingBroadcastReceiver ();
this.registerReceiver(callReceiver, new IntentFilter(
"com.example.INCOMING"));
...
// me = SipProfile
Intent i = new Intent();
i.setAction("com.example.INCOMING");
PendingIntent pi = PendingIntent.getBroadcast(getBaseContext(), 0,
i, Intent.FILL_IN_DATA);
manager.open(me, pi, null);
}
EXAMPLE:
My app is in foreground and I press back button to take it to back. Then I open another app for example hangouts, than I press the home button to go back to main screen. If the broadcast receiver gets an incoming call "INCOMING" than my app should appear, but it opens hangouts.
My app is in foreground and I press back button
When you press back button, the current activity instance is lost.hence the value of appInBackground set in
#Override
public void onStop() {
appInBackground = true;
super.onStop();
}
is lost too.
so next time when you receive the broadcast and arrive at public void doWork(), appInBackground is null.
Now I have found a solution: I should take the pendingIntent in my doWork() method and send it. Now my app is taking to front, even if it wasn't the last opened app. Thanks for the hints, now I have to work on bringing my app to front if screenlock is enabled. Any ideas?
public void doWork() {
// bring app to foreground
if (appInBackground) {
Intent intent1 = new Intent();
intent1.setClass(getBaseContext(),MyActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
pendingIntent.send();
}
...
}
I have set an alarm using AlarmManager and PendingIntent. The app has text counter which is a typical timer clock that counts backwards until it reaches 0. I would like to know whether a alarm is already set so that I can update my text counter and start/stop button states accordingly if my activity is killed and restored.
Question/Problem:
isAlarmActive flag value is always TRUE.
Returns true even if app is uninstalled and re-installed.
Used solution from the previous question but doesnt seem to work. Any help will be great.
protected void onCreate(Bundle savedInstanceState) {
....
boolean isAlarmActive = (PendingIntent.getBroadcast(MainActivity.this, 0,
new Intent(INTENT_ALARM_ACTION),
PendingIntent.FLAG_NO_CREATE) != null);
if(isAlarmActive)
changeButtonStatus(ButtonStatus.STOP);
else
changeButtonStatus(ButtonStatus.STARt);
}
private OnClickListener mBtnListener = new OnClickListener()
{
#Override
public void onClick(View v) {
if(mButtonStatus == ButtonStatus.START){
setAlarmOnAlarmManager();
changeButtonStatus(ButtonStatus.STOP);
}else{
cancelAlarmOnAlarmManager();
changeButtonStatus(ButtonStatus.START);
}
}
public void setAlarmOnAlarmManager()
{
Calendar cal = Calendar.getInstance();
long triggerTime = cal.getTimeInMillis() + mAlarmInterval;
mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mPendingIntent);
}
public void cancelAlarmOnAlarmManager()
{
MainActivity.this.getSystemService(Context.ALARM_SERVICE);
mAlarmManager.cancel(mPendingIntent);
unregisterReceiver(mAlarmReceiver);
}
I have used a timer method in an Activity class. In that method I have an intent from Activity class to a BroadcastReceiver class.
This BroadcastReceiver class will call on every 15 minutes at background by using AlarmManager.
When I call the BroadcastReceiver class I would like to raise an AlertDialog.
public void timerMethod(){
Intent intent = new Intent(Activity.this,
BroadcastReceiverClass.class
);
PendingIntent sender = PendingIntent.getBroadcast(
QualityCallActivity.this,0, intent, 0
);
// We want the alarm to go off 30 seconds from now.
long firstTime = SystemClock.elapsedRealtime();
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
firstTime, 60*1000, sender);
}
BroadcastReceiverClass.java
public void onReceive(Context context, Intent intent)
{
dialogMethod();
}
How can I raise an AlertDialog from BroadcastReceiver class from a background process?
If your activity is running when the BroadcastReceiver gets the intent you should be able to use runOnUiThread to run a method that creates an AlertDialog, e.g.:
public void onReceive(Context context, Intent intent)
{
runOnUiThread(new Runnable() {
public void run() {
AlertDialog.Builder d = new AlertDialog.Builder(MyActivity.this);
b.setMessage("This is a dialog from within a BroadcastReceiver");
b.create().show();
}
});
}
This works if you make your BroadcastReceiver an inner class to your Activity.
In short: It is not possible.
Only Activity's can create/show dialogs. In fact, this has been asked more then once:
AlertDialog in BroadcastReceiver
How can I display a dialog from an Android broadcast receiver?
Also, this would give a very bad user-experience:
If the user is not in your application (let's say he's playing a
Game) and your Dialog pops up every 15 minutes, this will be very
annoying for him.
If the user is in your application, there are several other (better
suited) ways to notify him that something has been executed.
Better suited ways
In fact, you can create/show a Toast from an BroadcastReceiver. This Toast will also bee shown when the user is not "in your application".
Also, you can send a Notification (shown in the Notification-Bar at the top of your screen) from a BroadcastReceiver. A tutorial on how to do this (it does not differ from how you do it in an Activity, except that you use the passed Context-Object from the onReceive-method).
The Notification will also be shown when the user is not "in your application" and is IMO the best solution to this problem.
1) In Activity:
public static Context ctx;
onCreate {
ctx = this;
}
public void showAlertDialog(Context context, String title, String message) {
final AlertDialog alertDialog = new AlertDialog.Builder(context).create();
// Setting Dialog Title
alertDialog.setTitle(title);
// Setting Dialog Message
alertDialog.setMessage(message);
// Setting OK Button
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Okay",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which)
{
alertDialog.dismiss();
}
});
// Showing Alert Message
alertDialog.show();
}
2) In BroadcastReceiver.onReceive:
YourActivity ac= new YourActivity ();
ac.showAlertDialog(YourActivity.ctx, "test", "test");