What I'm trying to do is create a notification in Android that will open an Activity whenever it is tapped. This activity is called "NotificationsActivity" and its parent is "MainActivity". Whenever the user is presented with NotificationsActivity I want them to be able to press the back button to get to the MainActivity. I've been using the instructions here (http://developer.android.com/guide/topics/ui/notifiers/notifications.html#DirectEntry) to try and get this to work. However, no matter what I try, whenever I press back from the NotificationsActivity the app exits completely. The only issue that I can think of is that my app has a login screen and I have also identified it as a MAIN and LAUNCHER. I don't think this would cause a problem, but that's all I can think of.
To summarize. What I want is:
Tap notification
Open NotificationsActivity
Press Back
Open MainActivity
But what I'm getting is:
Tap notification
Open NotificationsActivity
Press Back
Exit App
Pertinent code is displayed below.
From AndroidManifest.xml:
<activity
android:name=".LoginActivity"
android:label="#string/app_name"
android:launchMode="singleTop"
android:noHistory="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name=".NotificationsActivity"
android:parentActivityName=".MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>
And here is my Java code:
private void notifyRecordChanges(){
String notificationText = "test notification";
Context context = this;
NotificationCompat.Builder builder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_notify)
.setAutoCancel(true)
.setContentText(notificationText);
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(context, NotificationsActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(NotificationsActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(resultPendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1, builder.build());
}
EDIT: I removed the LoginActivity class from the project and it made no difference. So, now I have no idea what I'm doing wrong.
SECOND EDIT:
To add to Skizo's answer, I had to support the back button on the action bar. Here is the code to handle that. I call "goBack" from both the method override provided by Skizo and the code below.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case android.R.id.home:
goBack();
break;
}
return super.onOptionsItemSelected(item);
}
private void goBack(){
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
}
Just Override the onBackPressed() on your NotificationsActivity.
Copy&Paste this method :
#Override
public void onBackPressed() {
// super.onBackPressed();
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
}
Related
I'm new on android development and I still don't understand Intent very well. Here i'm using Firebase to create notifications, I add an Intent to my notification, but when my app is in foreground and I click on the notification nothing happens (when the app is killed or in background it work well).
The strange thing is at a moment it was working, when I clicked on the notification the function "onNewIntent" of my "MainActivity" class was called, but now nothing happen anymore, and I think I changed nothing on the code.
Here is how I create my intent :
Notifications notifs = new Notifications(this);
String title = remoteMessage.getData().get("title");
String body = remoteMessage.getData().get("body");
String url = remoteMessage.getData().get("url");
Intent intentNotif = new Intent(this, MainActivity.class);
intentNotif.setAction(Intent.ACTION_MAIN);
intentNotif.addCategory(Intent.CATEGORY_LAUNCHER);
intentNotif.putExtra("url", url);
intentNotif.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
//intentNotif.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
notifs.showNotif(title, body, true, intentNotif);
Here is how I add the intent to the notification :
this.builderGeneric.setContentIntent(PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT));
I also tried to create an intent with a custom action but it's not working, I tryed different intent flags, but I feel like i'm trying random things without knowing what I do, so that's why I'm asking for your help.
EDIT :
Here is how I create the notification if it's usefull :
Notifications(Context context) {
this.context = context;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
this.notifManager = context.getSystemService(NotificationManager.class);
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
channel.setDescription(CHANNEL_DESCRIPTION);
channel.enableLights(false);
channel.enableVibration(false);
channel.setSound(null, null);
if (this.notifManager != null) {
this.notifManager.createNotificationChannel(channel);
}
} else {
this.notifManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
this.builderGeneric = new Notification.Builder(context)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher_round);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
this.builderGeneric.setChannelId(this.CHANNEL_ID);
}
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
this.builderGeneric.setSmallIcon(R.mipmap.ic_launcher_foreground);
}
}
public void showNotif(String title, String text, boolean cancelable, Intent intent) {
this.builderGeneric.setContentTitle(title)
.setContentText(text)
.setOngoing(!cancelable)
.setContentIntent(PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT));
Notification notifGeneric = this.builderGeneric.build();
this.notifManager.notify(1, notifGeneric);
}
EDIT 2 : Here is my manifest :
<application
android:allowBackup="true"
android:fullBackupContent="#xml/backup_descriptor"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:launchMode="standard"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
<receiver
android:name=".NetworkChangeReceiver"
android:label="NetworkChangeReceiver">
<intent-filter
android:name=".NetworkIntentFilter"
android:label="NetworkIntentFilter">
<action
android:name="android.net.conn.CONNECTIVITY_CHANGE"
tools:ignore="BatteryLife" />
<action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
</intent-filter>
</receiver>
<service android:name=".MyFirebaseService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
OK, First try to remove
intentNotif.setAction(Intent.ACTION_MAIN);
intentNotif.addCategory(Intent.CATEGORY_LAUNCHER);
Next, adding android:launchMode="singleTask" (also removing android:launchMode="standard") inside your activity tag in AndroidManifest.xml.
Then try again, please aware that with the launchMode is singleTask and if you click on your notification while your MainActivity is opened -> onNewIntent will be triggered (because Android will not create one more instance of this Activity) otherwise onCreate will be called.
And if it works, I would like to recommend you to read more about LaundMode here
When I click on Notification I should be navigated to Main2Activity and when I click on back button of Main2Activity I should be navigated back to MainActivitybut I am getting navigated back to Home screen.
Is there any mistake in my code?
NotificationCompat.Builder noti = new NotificationCompat.Builder(MainActivity.this);
noti.setContentTitle("Message for you!");
noti.setContentText("Hi!!This is message for you");
noti.setSmallIcon(R.drawable.ic_launcher_background);
noti.setTicker("app name:message app");
noti.setAutoCancel(true);
Intent intent = new Intent(MainActivity.this,Main2Activity.class);
TaskStackBuilder taskStackBuilder=TaskStackBuilder.create(MainActivity.this);
taskStackBuilder.addParentStack(MainActivity.class);
taskStackBuilder.addNextIntent(intent);
PendingIntent pendingIntent=
taskStackBuilder.getPendingIntent(1234,PendingIntent.FLAG_UPDATE_CURRENT);
noti.setContentIntent(pendingIntent);
Notification notification=noti.build();
NotificationManager notificationManager = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(1234,notification);
Mainifest.XML file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sainathpawar.notifications">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Main2Activity"
android:parentActivityName=".MainActivity">
<intent-filter>
<action android:name="second_filter" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
Please try this
<activity
android:name=".Main2Activity"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>
It is because you are not within a stack. You only launched one activity. Your default MainActivity has not been created or launched.
You can handle the back button press with android.R.id.home in the OnMenuItemSelected callback and redirect them wherever you would like. You can try the "parent activity" route as well, but I'm not certain how that works when launched from notification without context of the parent to launch it to begin with.
If you go that route, update to let us know if it worked for you.
Otherwise you can easily use my answer as well.
EDITED FOR CLARITY
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
startActivity(Main2Activity.this, MainActivity.class);
return true;
finish();
}
return super.onOptionsItemSelected(item);
}
taskStackBuilder.getPendingIntent(1234,PendingIntent.FLAG_UPDATE_CURRENT);
noti.setContentIntent(pendingIntent);
There was error in coding at above line.If we see here the requestcode is 1234 which is same as ID of notify method
notificationManager.notify(1234,notification);
so it was navigating back to Home screen because Android OS was thinking as if its on MainActivity because of 1234 request code in getPendingIntent.
****Solution is:****change requestCode of getPendingIntent() method from 1234 to any random number I changed it to 0 and it worked for me.
You can achieve this by PendingIntent without TaskStackBuilder as:
Intent parentIntent = new Intent(this, MainActivity.class);
parentIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Intent resultIntent = new Intent(this, Main2Activity.class);
final PendingIntent pendingIntent = PendingIntent.getActivities(context, 0,
new Intent[] {parentIntent, resultIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
noti.setContentIntent(pendingIntent);
In my app I have starting activity which starts/restore app's activities and pass to it all intent extras if there are any:
manifest:
<activity android:name=".StartActivity"
android:theme="#style/Theme.AppCompat.Light.NoActionBar.FullScreen"
android:screenOrientation="sensorLandscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Code:
public class StartActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preloader);
String id=getIntent().getStringExtra("id");
Log.d("push~","start activity oncreate:"+id);
Intent i = new Intent(getIntent());
openLastActivity(i);
finish();
}
#Override
protected void onNewIntent(Intent intent) {
String id=intent.getStringExtra("id");
Log.d("push~","start activity new intent:"+id);
openLastActivity(intent);
super.onNewIntent(intent);
}
private void openLastActivity(Intent i){
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
if(GameActivity._this!=null) {
i.setClass(this,GameActivity.class);
startActivity(i);
} else {
i.setClass(this,SelectorActivity.class);
startActivity(i);
}
}
}
Also my app has service which generates notifications with following content intent:
Intent notificationIntent= new Intent(context, StartActivity.class);
notificationIntent.putExtra("id",id);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(contentIntent);
I expect id string will be passed to the last active activity.
The problem is when I run the app via notification tap it starts app and pass id as expected but if there is another notification(when I'm still in the app) neither onCreate nor onNewIntent method of the StartActivity is called therefore no data are passed to main activities.
UPD:Don't know why but android:launchMode="singleTask" for StartActivity solved my problem.Now all intents are receiving in onCreate
Don't know why but android:launchMode="singleTask" for StartActivity solved my problem.Now all intents are receiving in onCreate
I have 3 activities suppose MainActivity, FirstActivity, NotificationActivity and when user clicks on a notification from my app activity C will be launched, what I want is the user to be able to go back to B on pressing back button, I want to achieve this without overriding the OnBackPressed() method on doing some R&D i found a method of using the manifest to specify the parent activity to go back to another activity, its working from B->A and C->A but I cant figure how to get C->B working. I'll post the manifest file which I am working on. If needed I'll post the class files if needed.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.notificationmultipledemo"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<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=".FirstActivity"
android:label="#string/title_activity_first"
android:parentActivityName=".MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>
<activity android:name=".NotificationActivity"
android:label="#string/title_activity_notification"
android:parentActivityName=".FirstActivity">
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value=".FirstActivity" />
</activity>
</application>
Here is the Class A which is MainActivity
public class MainActivity extends Activity
{
private NotificationManager myNotificationManager;
private int notificationIdOne = 111;
private int notificationIdTwo = 112;
private int numMessagesOne = 0;
private int numMessagesTwo = 0;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button notOneBtn = (Button) findViewById(R.id.notificationOne);
notOneBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
displayNotificationOne();
}
});
Button notTwoBtn = (Button) findViewById(R.id.notificationTwo);
notTwoBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
displayNotificationTwo();
}
});
}
protected void displayNotificationOne() {
// Invoking the default notification service
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentTitle("New Message with explicit intent");
mBuilder.setContentText("New message from Cheers received");
mBuilder.setTicker("Explicit: New Message Received!");
mBuilder.setSmallIcon(R.drawable.ic_launcher);
// Increase notification number every time a new notification arrives
mBuilder.setNumber(++numMessagesOne);
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(this, NotificationActivity.class);
//resultIntent.putExtra("notificationId", notificationIdOne);
// when the user presses the notification, it is auto-removed
mBuilder.setAutoCancel(true);
//This ensures that navigating backward from the Activity leads out of the app to Home page
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the back stack for the Intent
stackBuilder.addParentStack(FirstActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
// start the activity when the user clicks the notification text
mBuilder.setContentIntent(resultPendingIntent);
myNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Date now = new Date();
long uniqueId = now.getTime();//use date to generate an unique id to differentiate the notifications.
// pass the Notification object to the system
Calendar c = Calendar.getInstance();
SimpleDateFormat simpledateformat_notification = new SimpleDateFormat("HH:mm:ss");
String Notificationdate = simpledateformat_notification.format(c.getTime());
String[] Notification_time = Notificationdate.split(":");
//int Beacon_hour = Integer.parseInt(Notification_time[0]); //7
int Notification_minutes =Integer.parseInt(Notification_time[2]);
myNotificationManager.notify(Notification_minutes, mBuilder.build());
}
}
Inside the displayNotificationOne() replace
stackBuilder.addParentStack(FirstActivity.class);
with
stackBuilder.addParentStack(NotificationActivity.class);
if you want the user to navigate from NotificationActivity to FirstActivity when the back button is pressed, also you will be able to go from FirstActivity to the MainActivity here becuase of the manifest. If you don't want that to happen then remove the meta data and parentActivityName in the activity section of FirstActivity.
I am trying to pass some values by using a bundle through a pending intent to my activity from a service. If the app is just started everything works fine but when the app is in resume mode, though my service receives new values from the remote server and put them in the pendingintent to pass to the activity, the activity shows the old values. Here is the code on service side:
private void sendNotification(String wholemsg) {
mNotificationManager = (NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
/* Do something to extract salesID and notificationmessage from wholemsg
....
....
....
salesID=....
notificationmessage=...
....
*/
Bundle bundle=new Bundle();
bundle.putString("msg", notificationmessage);
bundle.putString("strsalesID", salesID);
notificationIntent.replaceExtras(bundle);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_ONE_SHOT+PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("AppV001 Notification")
.setStyle(new NotificationCompat.BigTextStyle().bigText(notificationmessage))
.setContentText(notificationmessage);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
And this is onRestart method on my activity:
#Override
protected void onRestart() {
super.onRestart();
NotificationManager mNotificationManager = (NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancel(1);
try {
Intent intent = getIntent();
if (intent.hasExtra("msg") && intent.hasExtra("strsalesID")) {
String strmsgtitle = intent.getStringExtra("msg");
salesID = intent.getStringExtra("strsalesID");
titletext.setText(getString(R.string.str_ettitle) + salesID);
}
} catch (Exception ex) {
return;
}
}
The problem is that the salesID holds the previous value when the app comes back from hidden mode. It seems that the service cannot change the bundle of activity while it is hidden.
Many thanks in advance for your time!
I have found what was wrong with what my code and I want to post it here in case someone else faced the same problem. acj was right and there was a problem with my launch mode. My original application manifest was this:
<activity
android:name="com.example.appv001.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>
Since the launch mode wasn't declared the standard mode was taken automatically. We can have four launchMode(look at this):
The 'standard' is the default value. The four values fall into two
groups:
'standard' and 'singleTop' can instantiate multiple activity instances and the instance will stay in the same task.
For 'singleTask' or 'singleInstance', the activity class uses the singleton pattern, and that instance will be the root activity of a
new task.
Since my activity had standard launch mode, when the service was calling it a new instance of the activity was created and the intent was passed to the this new activity. Therefore instead of invoking "onNewIntent()", the "onCreate" method was invoked, in the other words the "onNewIntent()" was never called to set the bundle and etc. I changed the activity manifest to this and set the launch mode to singleTask (android:launchMode="singleTask) and the problem just solved:
<activity
android:name="com.example.appv001.MainActivity"
android:label="#string/app_name"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
I hope this helps someone else.