Android: Cannot replace bundle from a service on Restart - android

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.

Related

Yet another onNewIntent() issue | still not called

Using FirebaseMessagingService to generate notification.
After tapping on it, activity is shown, but onNewIntent() is not called.
Running on 7.1.
GOAL:
receive FCM notifications - works
after tapping on notification, bring up activity and display notification message in a dialog box - doesn't work
activity shows up,
extras.getString() returns null.
After studying many posts so far i got this, what else am i missing ?
Thanks.
EDIT: added onResume() method, extras variable is not null, strings are null
Notification.Builder mBuilder =
new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_icon)
.setContentTitle(title)
.setContentText(message)
.setOnlyAlertOnce(false)
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setVibrate(new long[] {0,400,150,400});
mBuilder.setNumber(++count);
Intent pushIntent = new Intent(this, PushMeMessageDialogActivity.class);
pushIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
pushIntent.putExtra(Constants.INTENT_TOPIC, title);
pushIntent.putExtra(Constants.INTENT_MESSAGE, message);
pushIntent.setAction(Intent.ACTION_MAIN);
pushIntent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent pushPendingIntent = PendingIntent.getActivity(this, count, pushIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pushPendingIntent);
NotificationManager notifyMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notifyMgr.notify(count, mBuilder.build());
Activity:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_message_dialog);
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
#Override
protected void onResume() {
super.onResume();
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if(extras == null)
return;
String strTopic = extras.getString(Constants.INTENT_TOPIC);
String strMessage = extras.getString(Constants.INTENT_MESSAGE);
}
<activity
android:name=".PushMeMessageDialogActivity"
android:launchMode="singleTop"
android:theme="#android:style/Theme.Dialog"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
minSdkVersion 22
targetSdkVersion 28
onNewIntent will not be called every time the activity is shown, as described in the documentation. Only if the Activity already existed it will be brought to the top of the stack and onNewIntent will be called. If the Activity didn't already exist the onCreate will be called.
For most use cases this means that for singleTop Activities you call setIntent in the onNewIntent method and call getIntent in the onResume. That way you always process the Intent that brought the Activity to the top of the stack.

Intent from notification isn't passed to target activity in Android

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

how opening the targeted activity on click of notification when app is in background

I have sent notification through firebase console using key vale pair and handled the notification in launcher activity. below is the tried code:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.hasExtra("click_action")) {
ClickActionHelper ck=new ClickActionHelper();
ck.startActivity(intent.getStringExtra("click_action"), intent.getExtras(), this);
}
}
public class ClickActionHelper {
public void startActivity(String className, Bundle extras, Context context){
Class cls=null;
try {
cls = Class.forName(className);
}catch(ClassNotFoundException e){
}
Intent i = new Intent(context, cls);
i.putExtras(extras);
context.startActivity(i);
}
}
but this way i am not able to open the targeted activity on click of notification. Any ideas?
If you need to navigate any Activity then there should be bind with Notification class(NotificationCompat.Builder) which is missing in implementation.
Below line is very important to redirect any Activity:
NotificationCompat.Builder builder = new **NotificationCompat.Builder(this); builder.setContentIntent(resultPendingIntent);**
Here resultPendingIntent is used for to containing the backstack for the Activity with the notifications as below:
// Gets a PendingIntent containing the entire back stack PendingIntent
resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
The complete code snippet is available here.
int id = 1;
Intent resultIntent = new Intent(this, ResultActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the back stack
stackBuilder.addParentStack(ResultActivity.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());
Edit:
Let me explain how it will works
Suppose you are set response from server as below
{
"registration_ids": ["XXX", ...],
"data": {
"id_offer": "41"
},
"notification": {
"title": "This is the Title",
"text": "Hello I'm a notification",
"icon": "ic_push",
"click_action": "ACTIVITY_XPTO"
}
}
And In your Manifest File
<activity android:name=".ActivityXPTO" android:screenOrientation="sensor" android:windowSoftInputMode="stateHidden"> <intent-filter> <action android:name="ACTIVITY_XPTO" />
<category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
When the app is closed or in background and the user clicks on the notification it opens my ActivityXPTO, to retrieve the id_offer I only need to do:
public class ActivityXPTO extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
String idOffer = "";
Intent startingIntent = getIntent();
if (startingIntent != null) {
idOffer = startingIntent.getStringExtra("id_offer");
// Retrieve the id
}
getOfferDetails(id_offer);
}
}
}
And In Second Way
Send key through data payload like below and get key in MainActivity via getIntent() and call specific activity or fragments.
json1.put("title","Your Title");
json1.put("body","body content");
json1.put("message","Your Message");
json1.put("screen","2"); //secondFragment is 2nd position in nav drawer
json.put("data", json1);
Sample project on GitHub.
Nothing works for me. The thing work for me is simple. Make sure you add this in the activity that you want to open directly.
<intent-filter>
<action android:name="MainActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
And from the push notification you must add a new payload: click_action
it will looks like this -
"notification": {
"title": "hello",
"body": "test message",
"click_action": "MAIN_ACTIVITY"
},
Note: You can name it as you want MAIN_ACTIVITY but must be same in both place.

TaskStackBuilder isn't working as expected

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();
}

android notification always relaunches activity

First time poster here, please excuse my lengthy post - I want to provide all the necessary info.
I am trying to achieve the coveted resume from clicking on notification functionality, but I am having some difficulties. First off, details(code below):
Project Properties
Android Developer Tools Build: v21.1.0-569685
Project Build Target : 4.2.2
Platofrm : 4.2.2
API Level : 17
SDK Target : 17
SDK min : 14
Android Device Chooser : Android Device : Nexus 4
MyApp launches MyAppService via startService
MyAppService's onStartCommand function uses NotificationCompat.Builder object to create a bar in the pull down notification.
The behavior I am trying to achieve :
Launch an app with a notification bar entry(e.g. Mail)
Start MyApp(this will also start MyAppService)
Pull down the notification bar and select MyAppService(yes, MyApp is currently on screen
Notification bar should just roll up w/o doing anything
What I am noticing is that MyApp will minimize out, and a new instance of MyApp will maximize(similar transition to when the app is being launched). I expect this behavior when I select on MyAppService when I have Mail app on top(Mail app to minimize and for MyApp to maximize).
There have been many code snippets and suggestions but I can't seem to get it to work; so here are the contents of my files :
AndroidManifest.xml
<application
android:allowBackup="true"
android:icon="#drawable/eyeview"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.MyApp.MainActivity"
android:label="#string/app_name"
android:launchMode="singleTop"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".service.MyAppService"
android:label="MyAppService">
</service>
</application>
MyApp/MainActivity.java
public static Intent serviceIntent;
public static Messenger clientMessenger;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstance);
if (getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
{
finish();
return;
}
setContentView(R.layout.activity_main);
...
[stuff specific to app, like setting up Fragments]
...
MainActivity.serviceIntent = new Intent(this, MyAppService.class);
MainActivity.serviceIntent.putExtra("clientMessenger", clientMessenger);
startService(MainActivity.serviceIntent);
}
MyAppService.java
#Override
private int onStartCommand(Intent intent, int flags, int startId)
{
NotificationCompat.Builder ncB = new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(R.drawable.icon_app)
.setContentTitle("MyAppService")
.setSmallIcon(R.drawable.app);
ncB.setContentText("Click to start MyApp");
Intent nIntent = new Intent(getApplicationContext(), MainActivity.class);
nIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
nIntent.setAction(Intent.ACTION_MAIN);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(getApplicationContext());
stackBuilder.addNextIntent(nIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
notificationBuilder.setContentIntent(resultPendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
int nId = 1;
Notification notification = ncB.build();
notification.flags = Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
notificationManager.notify(nId, notification);
sendMessengerToClient(intent);
startForeground(nId, ncB.build());
return Service.START_NOT_STICKY;
}
I hope I included all the enough information - I've been tweaking the code I got from SO just to see if this would work - in EVERY case, the topmost application minimizes out to black screen, and MyApp would maximize and MainActivity.java's onCreate function gets called.
I just want the notification pulldown to just scroll up if MyApp is already topmost.
Thanks in advance!
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);
By setting PendingIntent.FLAG_UPDATE_CURRENT and Intent.FLAG_ACTIVITY_SINGLE_TOP flags you can achieve this I guess. It won't create your activity if its already running.
In my case, the answer have by Shashika worked but in a weird fashion. I HAD TO RESTART THE PHONE! :)

Categories

Resources