I have a single Activity in my app (called MainActivity) and when I send notification to user, I want it to get opened if the notification is clicked. the important point is to avoid activity recreation and if there is already an instance of it alive, bring it to front and deliver the data to it. to get this behavior I have tried:
Intent intent = new Intent(getApplicationContext(), MainActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
.putExtra("data", ...);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
and in manifest:
<activity
android:name="...activities.MainActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="#style/Theme.AppCompat.Light.NoActionBar.FullScreen.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
activity>
but the onNewIntent(Intent intent) method never gets called when I touch the notification, instead onCreate is called and the activity is recreated. please note that since I'm testing, I send the notification from the MainActivity itself and while it is open, I drag status bar and click on my notification (which then recreates the MainActivity). what is the problem?
The code that you shared seems to be fine and only concern I see is setIntent() inside onNewIntent().
May be you can try removing them and check. And next is getApplicationContext() that you are passing in the PendingIntent. Try passing it as MainActivity.this.
Below is my snippet for reference. I done it in Kotlin instead of Java. However logic is same.
Manifest.xml
<activity
android:name = ".MainActivity"
android:launchMode = "singleTop">
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Logic for generating notification residing in MainActivity
val intent = Intent(this, MainActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
intent.putExtra("DATA", "SENT FROM PENDING INTENT")
val contentIntent = PendingIntent.getActivity(
applicationContext,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val b = NotificationCompat.Builder(this, getString(R.string.app_name))
b.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Default notification")
.setContentText("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
.setContentIntent(contentIntent)
.setContentInfo("Info")
val nmanager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nmanager.notify(1, b.build())
And this is my onNewIntent() part
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Log.i("onNewIntent", "Called")
intent?.let {
binder.msgView.text = it.getStringExtra("DATA")
Log.i("onNewIntent:", "Received new intent data")
}
}
NOTE : With the help of button action I generate the notification from inside the MainActivity.
Related
Good Morning,
I've made an application that implements GpS Location. I have a service that save my location on LocationChanged event. To avoid that Android close my app I start a notification and all works well. But now, I want that when I click on notification from Action Bar the app come back in foreground.I use fragment and the map fragment is called MappaFragment. I read a lot of messages but It seems not resolve my issue. Below my code, any suggestion is appreciated !
Alex
This is my monitoring Service:
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(Constants.STARTFOREGROUND_ACTION)) {
Log.i(TAG, "Received Start Foreground Intent ");
Intent notificationIntent = new Intent(getApplicationContext(), LocationMonitoringService.class);
Bundle extras = intent.getExtras();
numeroTraccia = extras.getInt("numeroTraccia");
itinerario = extras.getString("itinerarioRiferimento");
notificationIntent.setAction(Constants.MAIN_ACTION);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
Intent.FLAG_ACTIVITY_CLEAR_TASK);
setGiornoRiferimento(gg.checkDayLong(this));
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
0, new Intent(getBaseContext(),MappaFragment.class), 0);
Bitmap icon = BitmapFactory.decodeResource(getResources(), com.example.alex.myApp.R.drawable.smallicon );
Notification.BigTextStyle bigText = new Notification.BigTextStyle();
Notification notification = new Notification.Builder(getApplicationContext())
.setContentTitle("MyApp")
.setTicker(getString(R.string.track))
.setStyle(bigText.bigText(getString(R.string.track)))
.setSmallIcon(R.drawable.smallicon)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE,
notification);
}
This is my Manifest:
<activity android:name="com.example.alex.myApp.MainActivity"
android:screenOrientation="portrait"
android:configChanges="orientation|screenSize|keyboardHidden" />
<activity
android:name="com.example.alex.myApp.SplashScreen"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.example.alex.myApp.services.MonitoringService"
android:enabled="true"/>
<receiver
android:name="com.example.alex.myApp.RestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="RestartWhenStopped">
<intent-filter>
<action android:name="com.example.alex.myApp.ActivityRecognition.RestartSensor"/>
</intent-filter>
</receiver>
Thanks in advance !
Alex
First of all,
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getBaseContext(),MappaFragment.class), 0);
this is incorrect. Fragment can't be opened using intent. You need to launch an activity, then redirect to required fragment.
Next thing is, if app is in background and you click on the notification, it will open launcher activity. So in launcher activity you need to check for bundle object and there you can redirect to particular activity or fragment.
Check this post: Push notification works incorrectly when app is on background or not running and Open specific Activity when notification clicked in FCM it will help you to better understand how FCM works when app is in background and foreground.
Create a BroadCast Receiver like below:
public static class ActionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals("open_activity")) {
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClass(AppContext.getContext(), MappaFragment.class);
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
}
}
And then, Use this notificationIntent.setAction("open_activity");
And Change your PendingIntent to:
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
0, new Intent(AppContext.getContext(), ActionReceiver.class), 0);
NOTE: For USing This you need to Remove These Lines:
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE, notification)
What I'm suggesting is to use a BroadcastReceiver to receive whatever you want to do in Your Notification.
Also, If this isn't working try Adding .setAction(Intent.ACTION_MAIN) to Your Notification like Below:
Notification notification = new Notification.Builder(getApplicationContext())
.setContentTitle("MyApp")
.setTicker(getString(R.string.track))
.setStyle(bigText.bigText(getString(R.string.track)))
.setSmallIcon(R.drawable.smallicon)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingIntent)
.setOngoing(true)
.setAction("open_activity")
.build();
Well I've an app it has basicly three 3 classes which are 1-MainActivity, 2- DetailActivity and 3-Broadcast. even app was closed broadcastreceiver is working and if it receives something, it fires notification. when I pressed notification it is opening Detail class. until this point there is no problem it is working perfect. but if I press back in DetailActivity. it is directing to me home page of phone. but app should direct me to Main class. after app direct me to home page if I go back to app from background running apps it will start main class I am using docs codes which are:
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".DetailActivity"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
and
int id = 1;
...
Intent resultIntent = new Intent(this, DetailActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the back stack
stackBuilder.addParentStack(DetailActivity.class);
// Adds the Intent to the top of the stack
stackBuilder.addNextIntent(resultIntent);
// Gets a PendingIntent containing the entire back stack
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
...
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(id, builder.build());
Actualy I found the answer after some try and error,
PendingIntent.FLAG_UPDATE_CURRENT
After changing this line it works fine, also instead of ".this" I am using getApplicationContext(); for context
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_IMMUTABLE
);
as #MeknessiHamida mentioned..you should override the OnBackPressed method and start a intent to launch your main activity.
you just need to override it in the detailsActivity ( as per your question's ) : make an intent that directs you to the main Activity:
#Override public void onBackPressed() {
//Include the code here
return; }
I use the GoogleTransitionIntentService to show a notification if a user enter a Geofence, but the notification recreate MainActivity.class. I want to resume this activity.
https://github.com/googlesamples/android-play-location/tree/master/Geofencing
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
PendingIntent notificationPendingIntent =
PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setColor(Color.RED)
.setContentTitle(notificationDetails)
.setContentText("Test")
.setContentIntent(notificationPendingIntent);
builder.setAutoCancel(true);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, builder.build());
Manifest:
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
You have to use launchmode as singleTop inside your menifest for the given activity.
android:launchMode = "singleTop"
Try adding android:launchMode="singleTop" for MainActivity in the manifest.
You can add flags like
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
and set
android:launchMode = "singleTop"
to activity in manifest.
From GoogleDoc
If the activity has launch mode singleTop (or the up intent contains FLAG_ACTIVITY_CLEAR_TOP), the parent is brought to the top of the stack, and its state is preserved. The intent is received by the activity's onNewIntent() method. If the activity has launch mode standard (and the up intent does not contain FLAG_ACTIVITY_CLEAR_TOP), the current activity and its parent are both popped off the stack, and a new instance of the parent activity is created to receive the navigation intent.
I'm having an activity with some EditTexts and an Ongoing Notification.
After filling into EditTexts, I come back Home screen by pressing Home button (my app is running in background). All I want is to come back my activity with filled EditTexts (not to create a new one) when I click the Ongoing Notification.
I have tried this
How should i do from notification back to activity without new intent
And this
Notification click: activity already open
They don't work at all !!!
Below is my code snippet
ProtocolMonitorActivity.java
Intent resultIntent = new Intent(this, ProtocolMonitorActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(ProtocolMonitorActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notiBuilder = new NotificationCompat.Builder(
this).setSmallIcon(R.drawable.noti_icon)
.setContentTitle("Protocol Monitor App")
.setContentText("Service is running")
.setOngoing(true);
notiBuilder.setContentIntent(resultPendingIntent);
notiManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notiManager.notify(notiId, notiBuilder.build());
Manifest
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".ProtocolMonitorActivity"
android:label="#string/app_name"
android:process="com.android.phone"
android:launchMode="singleTop"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEVELOPMENT_PREFERENCE" />
</intent-filter>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ProtocolMonitorActivity" />
</activity>
</application>
Does someone have any idea on this?
Thank you so so much!!!
You cannot do this using TaskStackBuilder. The behaviour of TaskStackBuilder is that it always clears the task (recreating any activites).
You just need a "launch Intent" to bring your task to the foreground in whatever state it happens to be in. There's 2 ways to do this:
Varient 1:
final Intent notificationIntent = new Intent(this, ProtocolMonitorActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Variant 2:
final Intent notificationIntent =
PackageManager.getLaunchIntentForPackage(getPackageName());
Then do:
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notiBuilder = new NotificationCompat.Builder(
this).setSmallIcon(R.drawable.noti_icon)
.setContentTitle("Protocol Monitor App")
.setContentText("Service is running")
.setOngoing(true);
notiBuilder.setContentIntent(resultPendingIntent);
notiManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notiManager.notify(notiId, notiBuilder.build());
Just insert this line
resultIntent.setAction(Intent.ACTION_MAIN);
set android:launchMode="singleTop" in your activity tag in menifest inside required activity.
The following is my code that configures notification bar.
PendingIntent pi = PendingIntent.getActivity(
getApplicationContext(),
0,
new Intent(getApplicationContext(), AudioActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT
);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_action_star)
.setContentTitle("My app")
.setContentText("works")
.setContentIntent(pi);
mNotificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE
);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
At runtime, I first show AudioActivity at front-ground, then I open the notification bar that shows a message like "My app works". Clicking on the message opens AudioActivity again.
But it seems that the system calls the onCreate method of AudioActivity when I click notification bar. And before that it does not call the onDestroy of the existing AudioActivity. I was wondering if I have two AudioActivity instances running, and how I can manage them. What I expected is it should have worked as how activities are switched and onStart is used rather onCreate.
Thanks.
The best solution for you is to make it call onNewIntent and reuse the existing activity
In the called activity:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if ("action.action.myactionstring".equals(intent.getAction())) {
finish();
}
}
In the called activities mainfest entry
<activity android:name=".MyNotifiedActivity" >
<intent-filter>
<action android:name="action.action.myactionstring" />
...
</intent-filter>
</activity>
When creating the pending intent
Intent myIntent = new Intent("action string");
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi = PendingIntent.getActivity(
getApplicationContext(),
0,
myIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
The Intent.FLAG_ACTIVITY_SINGLE_TOP flag tells android to reuse the existing instance of the activity (if it exists). onNewIntent is called instead of onCreate, if it isnt already running, the it calls onCreate as usual
#Nick Cardoso: Here is the changed code. I think it works in the way that it does not call onCreate. I was only wondering why it does not call onNewIntent as you expected. Thanks.
PendingIntent pi = PendingIntent.getActivity(
getApplicationContext(),
0,
new Intent("com.example.myapp.ACTION_NOTIFICATION"),
PendingIntent.FLAG_UPDATE_CURRENT
);
NotificationCompat.Builder builder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_action_star)
.setContentTitle("My app")
.setContentText("works")
.setContentIntent(pi);
mNotificationManager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE
);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.ourradio.AudioActivity"
android:label="#string/title_activity_audio"
android:parentActivityName="com.example.ourradio.FeedActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.ourradio.FeedActivity" />
<intent-filter>
<action
android:name="com.example.myapp.ACTION_NOTIFICATION" />
</intent-filter>
</activity>
<service
android:name="com.example.ourradio.AudioService"
android:label="#string/label_service_audio" >
</service>
</application>
#Joe C , a simpler solution maybe add the SINGLE_TOP flag in the androidManifest as a launchMode in the activity like this:
<activity
android:name="com.example.ourradio.AudioActivity"
android:label="#string/title_activity_audio"
android:parentActivityName="com.example.ourradio.FeedActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
you can see this link for more information: http://www.intridea.com/blog/2011/6/16/android-understanding-activity-launchmode