I've followed a tutorial and implemented GCM push notification on my existing app. I've set Main activity to show notification. User receives a notification, clicks on it and notification display on Main activity. The problem is on app relaunch it shows notification every time.
Main Activity
public void registerGCM() {
....
protected Void doInBackground(Void... params) {
ServerUtilities.register(context, uname, uemail, regId);
}
}
Im calling this function on onCreate of Main activity to register app. This process is working fine.
Here's the BoradcaseReceiver code in Main activity outside of onCreate function.
private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
String newMessage = intent.getExtras().getString(EXTRA_MESSAGE);
WakeLocker.acquire(getApplicationContext());
Toast.makeText(getApplicationContext(), "New Message: " + newMessage, Toast.LENGTH_LONG).show();
WakeLocker.release();
}
};
It should only show notification once when user clicks it (this works fine), but its also showing notification every time use launch the app. How do i prevent this?
Please let me know if im not clear enough.
Any help will be highly appreciated.
Thanks!
If i understood you correctly your missing this documentation
add this attrs into your manifest between activity tags for your result activity i.e MainActivity (for more information check link above)
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
i can't see your notification receiver class so make sure you are setting resultIntent flags like
resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
note: dont forget to give every notification an unique id
Related
I have a two-activity android app (the activities are both single-top) and I am handling push notifications. Every push notification scenario is handled perfectly for me except for one, which is because of how the push notification intents are constructed.
The scenario that does not perform as desired is when the user is in one activity when the push notification comes in, and then they navigate to a different activity, at which point they then decide to select the push notification from their phone's dropdown bar. My problem is that the app then attempts to go back to the activity that was active when the notification was created, which isn't what I want. I want it to still do everything that it would do with the notification and its data the same way, but instead do it on the current activity and not switch back.
I know why it is happening, because the notification creation code is designed like this:
// create notification
val builder = NotificationCompat.Builder(context,
channelId)
// more things are done to set up the builder...
// here is why the problem is happening
val notificationIntent = if (we_are_in_activity_one)
Intent(context, ActivityOne::class.java)
else
Intent(context, ActivityTwo::class.java)
notificationIntent.putExtras(data_from_notification)
builder.setContentIntent(PendingIntent.getActivity(context,
notificationId,
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT))
//Auto cancel
builder.setAutoCancel(true)
builder.priority = NotificationCompat.PRIORITY_HIGH
//Return the generated notification
return builder
What I am wondering is what can be done or changed so that when the user selects the notification, it doesn't automatically start up the activity that it was originally bundled with if circumstances have changed. Is there maybe an obvious flag I am missing that I can use?
Another piece of info that may be helpful is that it is only a problem when they are in activity 2, as activity 1 is the launcher activity. So if the notification is created while in 2, but they navigate back to 1, Activity 2 won't be active anymore. When they tap the notification, it restarts activity 2, which isn't what I want. I would only want the notification to actually go back to Activity 2 if it is still active (aka the user is still on it).
Thanks in advance and let me know if I can provide any more helpful information
If I understand correctly, do you want to receive notification on the user's current activity?
You can do it as follows.
You need to verify that the second activity is running. If it is not, your notification will open on the main screen. If it is, the notification will be opened on it in onNewIntent().
An easy way to achieve this would be:
public static boolean isActivityActive(Activity activity) {
return !activity.isFinishing() && !activity.isDestroyed();
}
and then
Intent intent = new Intent(context, !isActivityActive(new ActivityTwo()) ? ActivityOne.class : ActivityTwo.class);
This method will return true if the activity is open.
Alternatively you can use:
activity.getWindow().getDecorView().isShown();
in isActivityActive(Activity) The value returned by this expression changes in onStart() / onStop()
If the method above does not provide the result you expect, you can create a class for reuse and save the activity state to shared keys that you can use in any activity.
Only a few lines of code are needed.
First do the following:
public class MyPreference {
private Context c;
private SharedPreferences.Editor editor;
private SharedPreferences preferences;
public MyPreference (Context c) {
this.c = c;
preferences = c.getSharedPreferences("shared_preferences", Context.MODE_PRIVATE);
}
public void save (String preference, boolean value) {
editor = preferences.edit();
editor.putBoolean(preference, value);
editor.apply();
}
public boolean boo (String preference) {
return preferences.getBoolean(preference, false);
}
public String is_second_activity_active = "is_second_activity_active";
}
You can now save the life cycles of your second activity by starting MyPreference as follows:
public class ActivityTwo extends AppCompatActivity {
MyPreference p;
#Override
protected void onCreate(Bundle state) {
super.onCreate(state);
p = new MyPreference(context);
// some source...
}
#Override
protected void onStart() {
super.onStart();
p.save(p.is_second_activity_active, true);
// your activity is active
}
#Override
protected void onStop() {
super.onStop();
p.save(p.is_second_activity_active, false);
// your activity is not active
}
}
You can understand the life cycle of the Activity here
Finally, in your notification class, retrieve the status of your second activity:
// create notification
// ...
MyPreference p = new MyPreference(context);
Intent intent = new Intent
(context, p.boo(p.is_second_activity_active) ? ActivityTwo.class : ActivityOne.class);
// continue...
Sorry my English, i am not fluent. I hope you understand.
I don't write in kotlin, so you can convert the code.
Let me know if it helped you.
Thank you. > <
CHANGE
It is not possible to change the behavior of a notification after it has been created in one way. You can update the notification when circumstances change.
You need to define setOnlyAlertOnce (true)in your NotificationCompat.Builder, and then send a new notification with the same id as the old notification.
If the id is the same, and setOnlyAlertOnce (true), your notification will be updated without a pop-up, sound or vibration warning. You can use the same Builder for each update.
Although this works well, the documentation itself warns that if the notification is updated several times in a short period of time, some notifications will not be packaged.
Caution: Android applies a rate limit when updating a notification. If you post updates to a notification too frequently (many in less than one second), the system might drop some updates.
Documentation
In your case, the notification would need to be updated whenever the user signs in and out of ActivityTwo.class, and that would not be a good thing.
Alternatively, I recommend that you try this.
Instead of opening the activity directly by notification, open it from a BroadcastReceiver,
and then decide which activity to open..
To broadcast your notification, your Intent needs to look like this
// create notification
// ...
Intent intent = new Intent();
intent.setAction(MyNotification.ACTION_OPEN);
intent.putExtra("get", "something");
// use getBroadCast instead of getActivity
PendingIntent pIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pIntent);
// configure your builder normally
Now in your BroadcastReceiver, make sure the user opened their notification, and what activity they were in when they did it.
public class MyNotification extends BroadcastReceiver {
public static String ACTION_OPEN = "com.example.intent.action.NOTIFICATION_OPEN";
#Override
public void onReceive(Context context, Intent intent) {
MyPreference m = new MyPreference(context);
boolean open_notification = ACTION_OPEN.equals(intent.getAction());
if (open_notification ) {
boolean is_active = m.boo(m.is_second_activity_active);
String log = is_active ? "Open in ActivityTwo" : "Open in ActivityOne";
Intent new_intent = new Intent(context, is_active ? two.class : one.class);
new_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
new_intent.putExtra("get", intent.getExtras());
context.startActivity(new_intent);
Log.e("log", log);
}
}
}
If you prefer, use your method to find out if the user is in the second activity, or use the examples I mentioned earlier.
NOTE: The onStart () and onStop () methods are actually better than onResume () and onDestroy () to check if the user is still in the activity.
onDestroy () is never called when the user closes the application, from recent applications.
Finally, declare your broadcast in your AndroidManifest.xml
<receiver android:name=".MyNotification"
android:exported="false">
<intent-filter>
<action android:name="com.example.intent.action.NOTIFICATION_OPEN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
Do not forget add android:launchMode="singleTop" to your ActivityOne and ActivityTwo
The notification will open according to the user's activity, no matter where it was created.
Thus, if the activity is the only thing that changes, there is no need to update notifications whenever circumstances change.
It should work now. x-x
For example in WhatsApp. When I call someone a activity opens up even if the app is in background or killed. How can I implement the same? Also I don't want my application to start a new activity cause this application will be used in a controlled environment. My application will always be in the background. The users will receive notifications when the app is open (background or foreground) so when they receive this notification they app should automatically app (not start a new activity) but just push the current activity to the foreground.
To best understand this is what I am trying to implement:
User A opens the app and then minimizes it. So now he is eligible to receive notifications. The application is currently on Activity A. When the user receives the notification we push the data received into a list and show it on Activity A. All I want to do is when User A receives a push notification to just push the Activity A to foreground no creating Activity A again or anything of that sort but just bring it to foreground. Any help would be much appreciated. Thank you!
You can use intent flag.
Intent.FLAG_ACTIVITY_SINGLE_TOP
And your code will be like
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// TODO: Handle FCM messages here.
Intent intent= new Intent(this,MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
}
Follow this answer. You can register a broadcast in your activity, or directly call the method from your onMessageReceived()
On message received start NotificationActivity that will check if your application already running then finish that activity will bring you last opened activity on the stack, And if app not running will start your MainActivity
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
startActivity(new Intent(this , NotificationActivity.class));
}
}
public class NotificationActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// If this activity is the root activity of the task, the app is not running
if (isTaskRoot())
{
// Start the app before finishing
Intent startAppIntent = new Intent(getApplicationContext(), MainActivity.class);
startAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startAppIntent);
}
// Now finish, which will drop the user in to the activity that was at the top of the task stack
finish();
}
}
Check this answer, and this answer for more details.
I want to know how to push notification with out letting user know that a push notification is received.Just my application should be aware of the notification and do the task required depended upon notification.
public class GCMIntentService extends GCMBaseIntentService
{
#Override
protected void onMessage(Context context, Intent intent)
{
Log.i(TAG, "Received message");
if (intent.getExtras().containsKey("payload"))
{
String message = intent.getExtras().getString("payload");
CommonUtilities.displayMessage(context, message);
// notifies user
//generateNotification(context, message);
}
}
}
here in this method dont notify user when the message comes.
EDIT:
this answer is applicable only if you have used GCM directly in your app implemented by yourself.
If you are using parse.com for this then you cant achieve what you have asked in question. because parse API internally notifies this user.
I am making an app where I receive XMPP packets. My app starts in foreground and starts a Sticky Service if not started and start receive messages in backgrounds and make notifications of them. The notifications works well and when clicked they show the data. Issue comes (that too sometimes in some mobile phones) when I make a sendBroadcast call from service so that I can asynchronously update the text of message in Activity when it is opened.
My app is made with a Activity and within that there are two Fragments(SherlockFragment)
So I made a private broadcastreceiver in one of fragment where I want to show the text (updated text).
Code is written below
public class newMessage extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Log.v("ONMESSAGE", "broadcast");
String action = intent.getAction();
if(action.equalsIgnoreCase("NewMessage")){
Bundle extra = intent.getExtras();
String username = extra.getString("from");
String message = extra.getString("message");
showMessage(username, message);
Log.v("ONMESSAGE", "in broadcast is " + username);
}
}
}
I then make a statement of below in Fragment class
private newMessage messageReceiver = new newMessage();
in onCreateView I wrote the below line
getActivity().registerReceiver(messageReceiver, new IntentFilter("NewMessage"));
I did not unregister the broadcast receiver now and I tried doing that too.
When I receive a message in my service, I made call to below code.
Intent i = new Intent("NewMessage");
i.putExtra("message", message);
i.putExtra("from", from);
context.sendBroadcast(i);
I did not add the broadcats to to manifest cause I dont know how to do it when written inside a class.
Sometimes, it calls the sendbroadcase and everything works well however sometimes it does not call and just generate the notification.
I'm wanting to implement what CommonsWare describes on this blog post: http://commonsware.com/blog/2010/08/11/activity-notification-ordered-broadcast.html. The post makes sense, and I was able to browse the example source here: https://github.com/commonsguy/cw-advandroid/tree/master/Broadcast.
What I'm curious about is if calling LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast); inside of a service will still be picked up by a broadcast receiver of the type you define in your manifest.
In case what I'm asking isn't clear, what I'm trying to do is use the LocalBroadcastManager because the broadcasts from my service don't necessarily need to be seen system wide and I'd rather keep them private if possible, but I also want to display notifications if the user closes my app and the service is still running. Is there a way to combine both of those capabilities without sending a broadcast twice inside of the service?
(What I don't want to have to do) like:
LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast);
sendOrderedBroadcast(broadcast);
What I'm curious about is if calling LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast); inside of a service will still be picked up by a broadcast receiver of the type you define in your manifest.
No. LocalBroadcastManager only works with receivers registered with the LocalBroadcastManager singleton itself. Moreover, LocalBroadcastManager does not support ordered broadcasts, last I checked.
what I'm trying to do is use the LocalBroadcastManager because the broadcasts from my service don't necessarily need to be seen system wide and I'd rather keep them private if possible
So long as you are not using an <intent-filter> on your BroadcastReceiver in the manifest, and therefore are using an explicit Intent as the broadcast itself, your broadcast will only be seen by yourself and the bit of the OS that manages broadcasts. Other apps will not be able to spy upon it.
If you only have 2 objects that might handle your broadcast (in your case an Activity and a notifications controller), you can achieve the behavior of a ordered broadcast using only the LocalBroadcastManager.
The general idea is:
Set up your Service so that it broadcasts an Intent to your Activity with a particular action when you want to display your result
In your Activity create a BroadcastReceiver that handles your Service result Intent, and register it on the LocalBroadcastManager with an IntentFilter using the action from step 1
In your Service, when the results are available, try to send the result Intent using LocalBroadcastManager.getInstance(Context).sendBroadcast(Intent) this method returns a boolean that indicates if the broadcast has been sent to at least one receiver. If this boolean is false, it means that your Activity didn't handle your broadcast and you should show a notification instead.
In your service:
public UnzipService extends IntentService {
public static final String ACTION_SHOWRESULT = UnzipService.class.getCanonicalName() + ".ACTION_SHOWRESULT";
#Override
protected void onHandleIntent(Intent intent) {
Thread.sleep(500); // Do the hard work
// Then try to notify the Activity about the results
Intent activityIntent = new Intent(this, YourActivity.class);
activityIntent.setAction(ACTION_SHOWRESULT);
activityIntent.putExtra(SOME_KEY, SOME_RESULTVALUE); // Put the result into extras
boolean broadcastEnqueued = LocalBroadcastManager.getInstance(this).sendBroadcast(activityIntent);
if (!broadcastEnqueued) { // Fallback to notification!
PendingIntent pendingIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
.notify(SOME_ID, new NotificationCompat.Builder(this)
.setContentIntent(pendingIntent)
.setTicker("results available")
.setContentText("results")
.build());
}
}
}
In your Activity:
public YourActivity extends Activity {
private BroadcastReceiver resultReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
processResult(intent); // Results Intent received through local broadcast
}
}
private IntentFilter resultFilter = new IntentFilter(UnzipService.ACTION_SHOWRESULT);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate();
Intent intent = getIntent();
if (UnzipService.ACTION_SHOWRESULT.equals(intent.getAction())) {
// The Activity has been launched with a tap on the notification
processResult(intent); // Results Intent contained in the notification PendingIntent
}
}
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this)
.registerReceiver(resultReceiver, resultFilter);
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(resultReceiver);
super.onPause();
}
private void processResult(Intent intent) {
// Show the results from Intent extras
}
}
This should be a complete working example.
I hope this helps who is trying to implement ordered broadcasts with LocalBroadcastManager from support library!
I understand you want to achieve the following:
"I have an event that occurs in the background. I want to update my activity, if the activity is on the screen. Otherwise, I want to raise a Notification." (#TheCommonsBlog)
You can achieve this behaviour by implementing a ResultReceiver.
Examples Restful API service and
http://itekblog.com/background-processing-with-intentservice-class/
What you basically do is instance a ResultReceiver in your Activity and pass it to the Service like a Parcelable parameter through an intent. Then, each time your service whats to update the UI, the service verifies the ResultReceiver object for NULL. If not NULL, you update the Ui via the onReceiveResult interface. Else, you raise a notification. When your activity dismisses, make sure you set the ResultReceiver on the Service to NULL.
Hope it helps.
PS: IMO, broadcasts are too much work and hard to control.
Use LocalBroadcastManager and broadcasts become easy to use.
I am not in favor of updating an Activity if an event occurs in the background. The user might already be doing something else in the Activity. Seems to me that a Notification is sufficient; it's always visible and remains until the user dismisses it. Gmail and Gcal work like this; Gmail doesn't update the current screen if a new mail comes in. If you want to know how to handle the task flow for handling a notification when the user is already in the app, see the Notifications API guide and also the [Notifying The User2 training class.