Problem
When the user presses Send "Button 1"(scroll down to see the construction of the app) a new Notification is created from the RefreshService. If the user presses this notification a MainActivity instance gets started and receives a String with the value Button 1 over the Intent.
This value gets displayed.
When the user presses now the Send "Button 2" a new Notification is created from the RefreshService. If the user presses this notification a MainActivity instance gets started and receives a String ALSO with the value Button 1 over the Intent.
So as you can guess, normally there should be the value Button 2.
When the first Button the user pressed was Send "Button 2" then there would allways Button 2 be sent.
The only sollution to get the value of the second button is to restart the phone and pressing the second button first. Even force close doesn't work.
I know that I also can change the UI in another way. But I need this approach in a app where I need to restart the 'MainActivity' with another Intent so the approach should be the same.
Construction
A Activity called MainActivity
A IntentService called RefreshService
MainActivity
public class MainActivity extends Activity implements View.OnClickListener {
public static final String RECEIVED = "received";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((TextView)findViewById(R.id.textView_received)).setText(getIntent().getStringExtra(RECEIVED));
findViewById(R.id.button_1).setOnClickListener(this);
findViewById(R.id.button_2).setOnClickListener(this);
}
#Override
public void onClick(View v) {
Intent intent = new Intent(this, RefreshService.class);
if(v.getId() == R.id.button_1){
intent.putExtra(RECEIVED, "Button 1");
Toast.makeText(this,"Sent \"Button 1\"",Toast.LENGTH_LONG).show();
}
else if(v.getId() == R.id.button_2){
intent.putExtra(RECEIVED, "Button 2");
Toast.makeText(this,"Sent \"Button 2\"",Toast.LENGTH_LONG).show();
}
startService(intent);
}
}
RefreshService
public class RefreshService extends IntentService {
public RefreshService() {
super("RefreshService");
}
#Override
protected void onHandleIntent(Intent intent) {
String received = intent.getStringExtra(MainActivity.RECEIVED);
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.putExtra(MainActivity.RECEIVED, received);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this).setContentTitle("IntentServiceRefresh").setContentText(received).setSmallIcon(R.drawable.ic_notification_small).setContentIntent(pendingIntent);
Notification notification = builder.build();
// Hide the notification after it's selected
notification.flags |= Notification.FLAG_AUTO_CANCEL;
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notification);
}
}
App Layout
My suspicion is that, since the only thing changing in the Intent is the extras, the PendingIntent.getActivity(...) factory method is simply re-using the old intent as an optimization.
In RefreshService, try:
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
See:
http://developer.android.com/reference/android/app/PendingIntent.html#FLAG_CANCEL_CURRENT
Related
My app repeatedly loads data from a server. Among those data are messages.
Whenever new messages are loaded, the MainActivity is called on an Interface's callback method onMessagesReceived(int numOfMessages).
The app has only one Activity and every screen is implemented as a Fragment. Switching of Fragments is managed by a dedicated Navigator class.
My problem is the handling of the user tapping on the Notification. When the user taps on the Notification, the message list should be shown.
public class MainActivity extends AppCompatActivity implements MessageListener {
private static final int MESSAGE_NOTIFICATION_ID = 101010;
private static final String EXTRA_SHOW_MESSAGES = "SHOW_MESSAGES";
private Navigator mNavigator;
#Override
onMessagesReceived(int numOfMessages) {
Intent intent = new Intent(this, MainActivity.class);
testIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
testIntent.putExtra(EXTRA_SHOW_MESSAGES, true);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "testChannel")
.setSmallIcon(R.drawable.ic_message_notification)
.setContentTitle("New messages!")
.setContentText("You got " + numOfMessages + " new messages")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(MESSAGE_NOTIFICATION_ID, builder.build());
}
#Override
protected void onResume() {
super.onResume();
Bundle extras = getIntent().getExtras();
if (extras != null && extras.containsKey(EXTRA_SHOW_MESSAGES)) {
if (extras.getBoolean(EXTRA_SHOW_MESSAGES)) {
mNavigator.openMessageList();
}
}
}
}
At the moment, the MainActivity shows up, when the app is in background, but in onResume, the Bundle returns as null.
When the app is in the foreground, nothing happens at all.
I want to achieve on a click on the Notification:
- When the user is inside the app, the MessageList Fragment should be opened
- When the user is not inside the app, it should be started before opening the MessageList Fragment
Can someone give me a hint, how to proceed from here? Maybe using Intents isn't the right solution here?
You can use intents just put some boolean extras in intent an just check the value in main activity if that extra value is true then call your method.
After some more digging on Intents and Notifications, I finally came up with a solution.
public class MainActivity extends AppCompatActivity implements MessageListener {
private static final int MESSAGE_NOTIFICATION_ID = 101010;
private static final String EXTRA_SHOW_MESSAGES = "SHOW_MESSAGES";
private Navigator mNavigator;
#Override
onMessagesReceived(int numOfMessages) {
Intent intent = new Intent(this, MainActivity.class);
testIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
testIntent.putExtra(EXTRA_SHOW_MESSAGES, true);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "testChannel")
.setSmallIcon(R.drawable.ic_message_notification)
.setContentTitle("New messages!")
.setContentText("You got " + numOfMessages + " new messages")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(MESSAGE_NOTIFICATION_ID, builder.build());
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
this.setIntent(intetnt)
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(EXTRA_SHOW_MESSAGES)) {
if (extras.getBoolean(EXTRA_SHOW_MESSAGES)) {
mNavigator.openMessageList();
}
}
}
}
I moved my code, reading the new Intent, to onNewIntent. This method gets called, when an Activity gets a new Intent and before onResume. This triggers regardless of the Activity being in the foreground or not. I also set this new Intent to be the Activities Intent with setIntent, otherwise the Intent that initialy started my Activity, is called by getIntent().
I am working on an app where I have a service that should always run in the background. This service is responsible for a Notification bar that should always be visible. The notification bar has 2 buttons, where the 1st one is for grabbing some data and storing it and the 2nd one should open an activity that will show all the data. I encountered a problem where when I close the application and then press the notification button that starts an activity after that activity starts, my notification buttons stop responding. Note that both buttons work fine before the point where the 2nd button click starts the activity.
Here is a template code for my notification service and for notification bar button handler
service that handles the Notification bar
public class NotificationBarService extends Service {
private int notificationID;
#Override
public IBinder onBind(Intent intent){
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
notificationID = new Random().nextInt();
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.custom_notification);
contentView.setImageViewResource(R.id.image, R.mipmap.ic_launcher);
contentView.setTextViewText(R.id.title, "Custom notification");
contentView.setTextViewText(R.id.text, "This is a custom layout");
//Handle the button for showing bookmarks on custom notification
Intent buttonsIntent2 = new Intent(this, NotificationBarButtonActivityHandler.class);
buttonsIntent2.putExtra(PENDING_ACTION, SHOW_BOOKMARKS);
contentView.setOnClickPendingIntent(R.id.notificationBarShowBookmarksButton, PendingIntent.getActivity(this, 0, buttonsIntent2, 0));
//Handle the button for adding bookmark on custom notification
Intent buttonsIntent = new Intent(this, NotificationBarButtonActivityHandler.class);
buttonsIntent.putExtra(PENDING_ACTION, REGISTER_BOOKMARK);
contentView.setOnClickPendingIntent(R.id.notificationBarAddBookmarkFromChromeButton, PendingIntent.getActivity(this, 1, buttonsIntent, 0));
RemoteViews notificationView = new RemoteViews(getPackageName(),
R.layout.custom_notification);
Intent switchIntent = new Intent(this, NotificationBarService.class);
PendingIntent pendingSwitchIntent = PendingIntent.getBroadcast(this, 0,
switchIntent, 0);
notificationView.setOnClickPendingIntent(R.id.notificationBarShowBookmarksButton,
pendingSwitchIntent);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setContent(contentView)
.setSmallIcon(R.drawable.notification_small_icon)
.setOngoing(true);
Notification notification = mBuilder.build();
startForeground(notificationID, notification);
return START_STICKY;
}
#Override
public void onDestroy(){
super.onDestroy();
stopForeground(true);
}
}
Class that handles the button press on Notification bar
public class NotificationBarButtonActivityHandler extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String action = (String) getIntent().getExtras().get(NotificationBarService.PENDING_ACTION);
if (action != null) {
if (action.equals(NotificationBarService.REGISTER_BOOKMARK)){
CustomLogger.log("---------------- BUTTON FOR COLLECT DATA WAS PRESSED!!!");
//Does something here
}
else if(action.equals(NotificationBarService.SHOW_BOOKMARKS)){
CustomLogger.log("---------------- BUTTON FOR SHOW DATA WAS PRESSSED!!!");
//Notification bar buttons start not responding right after
//this is executed. Note that this problem only occurs if I close the app
//and press the notification button to execute this code.
//Otherwise this works just fine.
Intent intent2;
intent2 = new Intent(this, BookmarkDisplayActivity.class);
startActivity(intent2);
}
}
finish();
}
}
So basically if I close the application and remove the code that starts the activity, both buttons work as expected but as soon as I start the activity, both buttons stop working.
Ok, I finally solved the issue that I was having with changing the way that I handle button presses. This is what I got now and it works as expected.
In NotificationBarService this is how I handle the listeners for the buttons
Intent addBookmarkIntent = new Intent(this, NotificationBarButtonListener.class);
addBookmarkIntent.setAction(ADD_BOOKMARK_ACTION);
PendingIntent pendingAddBookmarkIntent = PendingIntent.getBroadcast(this, 0, addBookmarkIntent, 0);
contentView.setOnClickPendingIntent(R.id.notificationBarAddBookmarkFromChromeButton, pendingAddBookmarkIntent);
Intent showBookmarkIntent = new Intent(this, NotificationBarButtonListener.class);
showBookmarkIntent.setAction(SHOW_BOOKMARK_ACTION);
PendingIntent pendingShowBookmarkIntent = PendingIntent.getBroadcast(this, 0, showBookmarkIntent, 0);
contentView.setOnClickPendingIntent(R.id.notificationBarShowBookmarksButton, pendingShowBookmarkIntent);
and then I receive a broadcast even and handle it like this
public static class NotificationBarButtonListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if(action.equals(ADD_BOOKMARK_ACTION)){
CustomLogger.log("---------------- BUTTON FOR REGISTER BOOKMARK WAS PRESSED!!! ");
}
else if(action.equals(SHOW_BOOKMARK_ACTION)){
CustomLogger.log("---------------- BUTTON FOR SHOW BOOKMARK WAS PRESSSED!!!");
}
}
}
Note that this required me to add the following line to my manifest
<receiver android:name=".NotificationBarService$NotificationBarButtonListener"/>
I create an app with button for 5 sec notification i'm getting the notification when I'm on the app or outside
so I want to get the notification just when I'm not working on the app
and when I click on the notification enters the app and the notification stops but when I reclose the app the notification appears every 5 min/sec.
mainactivity.java
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.btn1);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Calendar calendar =Calendar.getInstance();
Intent intent = new Intent (getApplicationContext(),AlertReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(),100,intent,PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),AlarmManager.INTERVAL_FIFTEEN_MINUTES/180,pendingIntent);
}
});
}
AlertReciver.class
public class AlertReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent repeating_intent = new Intent(context,Activity12.class);
repeating_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context,100,repeating_intent,PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context).setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("AAA")
.setContentText("BBB")
.setAutoCancel(true);
notificationManager.notify(100,builder.build());
Activity12.class is empty just textview there...!
One possibility is to only show the notification if the app is not running. So you could give the app a static variable, that you'd check before building the notification, that you set to true in onResume() or false in onPause().
There is probably a better way of checking if the app is currently running, but I haven't coded in android java for a while and I haven't got android studio here right now to try it out.
Not able to identify id for item clicked on notification was for unique id which is working out well,now for the same even with different id when i click on notifications i get unique invoice id ,which i am passing to a webservice to get its invoice details,even though its fetching data for that id,the itemActivity page is showing previous details only,how will i update the page with new contents ?
Send Notification code is
public class SampleSchedulingService extends IntentService {
public SampleSchedulingService() {
super("SchedulingService");
}
List<GetReminder> newReminderList;
int invoiceId=0;
String remMes;
InvoiceData1 data1;
int InvM_Id;
public static final String TAG = "Scheduling Demo";
// An ID used to post the notification.
public static int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
#Override
protected void onHandleIntent(Intent intent) {
// BEGIN_INCLUDE(service_onhandle)
// The URL from which to fetch content.
Log.d("MyService", "About to execute MyTask");
//
newReminderList=WebService.invokeGetReminderWS("GetReminder",41);
if(newReminderList!=null){
for(int i=0;i<newReminderList.size();i++) {
sendNotification(newReminderList.get(i).getRemMessage(),newReminderList.get(i).getInvM_Id());
}
}
// Release the wake lock provided by the BroadcastReceiver.
SampleAlarmReceiver.completeWakefulIntent(intent);
// END_INCLUDE(service_onhandle)
}
// Post a notification indicating whether a doodle was found.
private void sendNotification(String msg, int invM_id) {
try {
Intent notificationIntent = new Intent(this, ItemActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
data1=WebService.InvoiceDetailForExeedDiscount1(invM_id);
notificationIntent.putExtra("invoiceList", data1);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this, NOTIFICATION_ID, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(getString(R.string.invoice_alert))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(msg))
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
NOTIFICATION_ID++;}
catch (IOException e) {
} catch (XmlPullParserException e) {
}
}
}
and itemActivity i am reading data like
public class ItemActivity extends Activity implements View.OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final boolean customTitleSupported =
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.itemlist);
if(customTitleSupported){
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,R.layout.item);
}
InvoiceData1 invoiceList = (InvoiceData1) getIntent().getSerializableExtra("invoiceList");
}
its like when we come out of the application and click on second notification,its just blandly showing the invoice details of previous one,der is no call going on to fetch data for clicked on.
As you are using FLAG_ACTIVITY_SINGLE_TOP flag in your PendingIntent you must also implement onNewIntenet() method in ItemActivity to properly handle this launch mode. As per documentation:
This is called for activities that set launchMode to "singleTop" in
their package, or if a client used the FLAG_ACTIVITY_SINGLE_TOP flag
when calling startActivity(Intent). In either case, when the activity
is re-launched while at the top of the activity stack instead of a new
instance of the activity being started, onNewIntent() will be called
on the existing instance with the Intent that was used to re-launch
it.
An activity will always be paused before receiving a new intent, so
you can count on onResume() being called after this method.
Note that getIntent() still returns the original Intent. You can use
setIntent(Intent) to update it to this new Intent.
so it should be sufficient to move some code from your onResume() and limit onNewIntent() implementation to single setIntent() call.
What I am trying to do is to sent a Notification to Wear with a PendingIntent to open a simple Activity of which TextView is set by an Intents Extra (String). The problem is, that the Extras seems to be empty.
Here is my Code for the MainActivity:
public class MainActivity extends ActionBarActivity {
public static final String KEY = "eu.paliga.creatinganotification.Key";
public static final String MSG = "welcome back";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent viewIntent = new Intent(MainActivity.this, ViewEventActivity.class);
viewIntent.putExtra(KEY, MSG);
PendingIntent viewPendingIntent = PendingIntent.getActivity(MainActivity.this, 0, viewIntent, 0);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(MainActivity.this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("trying")
.setContentText("to figure it out")
.setContentIntent(viewPendingIntent);
NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(MainActivity.this);
notificationManager.notify(0, notificationBuilder.build());
}
});
}
....
}
and for the Activity that is started by the Intent:
public class ViewEventActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_viewintent);
Intent intent = getIntent();
if (intent.hasExtra(MainActivity.KEY)) {
String msg = intent.getStringExtra(MainActivity.KEY);
Log.d("MyTag", msg);
((TextView)findViewById(R.id.textView2)).setText(msg);
}
}
}
When you use PendingIntent.getActivity(MainActivity.this, 0, viewIntent, 0) (specifically, the 0 as the last parameter), extras do not get replaced per the PendingIntent overview:
Because of this behavior, it is important to know when two Intents are considered to be the same for purposes of retrieving a PendingIntent. A common mistake people make is to create multiple PendingIntent objects with Intents that only vary in their "extra" contents, expecting to get a different PendingIntent each time. This does not happen. The parts of the Intent that are used for matching are the same ones defined by Intent.filterEquals. If you use two Intent objects that are equivalent as per Intent.filterEquals, then you will get the same PendingIntent for both of them.
They go into details on how to deal with them, although the most common solution is to replace your call with:
PendingIntent.getActivity(MainActivity.this, 0, viewIntent,
PendingIntent.FLAG_UPDATE_CURRENT)
Where FLAG_UPDATE_CURRENT denotes that the system should update the extras in the PendingIntent.