Intent Action isn't changing after recalling the activity - android

I have this Activity called Posting:
<activity
android:name="Posting"
android:label="#string/app_name"
android:launchMode="singleTop"
android:uiOptions="splitActionBarWhenNarrow">
<intent-filter>
<action android:name="com.emergency.StopDistributing"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="com.emergency.nothingMuch"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
I am navigating to this specific activity using action: com.emergency.nothingMuch. as demonstrated in the following code:
i = new Intent("com.emergency.nothingMuch");
startActivity(i);
now in Posting class I have a Button that when clicked it should bind to a service and get data from it and do some tasks. also a notification will show that by clicking on the notification, the activity will unbind the service and do other tasks.
the notification has a PendingIntent to this same Posting class but with different action. the following code is:
Intent iStop = new Intent("com.emergency.StopDistributing");
PendingIntent stopTrackingPI = PendingIntent.getActivity(this, 1, iStop, PendingIntent.FLAG_UPDATE_CURRENT);
builder.addAction(android.R.drawable.ic_media_pause, "Stop Tracking", stopTrackingPI);
now in onResume I have a if statement to check for the Intent Action.
if(getIntent().getAction().equals("com.emergency.StopDistributing")) {
stopPosting();
}
After adding Log.d before the if statement. when I press on the notification action. the intent action I get is still nothingMuch. it doesn't change to com.emergency.StopDistributing.
I also used android:launchMode="singleTop" for this activity because I want to modify the existing activity rather than opening a new one.
So why the Intent's action isn't changing when clicked on the notification and how can I fix it. Thanks in advance.
P.S: I don't want to use setIntent(iStop); to change it because I have other PendingIntents so I need another solution.

Try using the onNewIntent() method in your Activity to check the action of the Intent, rather than using getIntent(), which will still return the original intent.
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.

Related

Android - reopen main activity from notification

Launcher activity for my app is called LaunchActivity. Inside this activity I check if the user is logged. If yes, then a network call is made to validate the user and if everything is fine MainActivity is started. If user runs app for first time or user validation fails, LoginActivity runs.
Inside LaunchActivity there is function that runs appropriate activity:
private void start(Class<? extends Activity> startActivity) {
Intent intent = new Intent(this, startActivity);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
finish();
startActivity(intent);
}
App is receiving notifications. After notification click i want to open app or bring it top if it is on background.
Code responsible for notification intent:
Intent intent = new Intent(context, LaunchActivity.class);
intent.putExtra("SHOW_NOTIFICATION_LIST", "");
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent notificationIntent = PendingIntent.getActivity(
context,
0,
intent,
0);
builder.setContentIntent(notificationIntent);
Notification notification = builder.build();
The problem is that with my current implementation after click, LaunchActivity is reopened even when MainActivity is on background. I can not figure out how to make the pending intent reopen just MainActivity (without LaunchActivity and valdation) if it is on background OR start LaunchActivity when there are no activity running on background ( app is not running ). I would be very grateful for any kind of help.
edit:
Activities declaration inside manifest:
<activity android:name=".activities.LaunchActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activities.MainActivity"
android:launchMode="singleTop"
android:screenOrientation="userPortrait" />
<activity
android:name=".activities.LoginActivity"
android:excludeFromRecents="true"
android:launchMode="singleTop"
android:noHistory="true"
android:screenOrientation="userPortrait"
android:windowSoftInputMode="adjustResize" >
</activity>
The reason for the absurd behavior is that because you are finishing the LaunchActivity when you call start()
private void start(Class<? extends Activity> startActivity) {
....
finish(); // This is the culprit
....
}
Remove finish() from start() and it will work fine as expected.
If you can't remove finish() because of your app behavior, then change your PendingIntent to launch MainActivity instead of LaunchActivity
Intent intent = new Intent(context, MainActivity.class);
And in MainActivity's onCreate() check if the user is already logged in . If not then navigate him to your LoginActivity.
Create a BroadcastReceiver which reacts to three different intents
ACTION_CREATED: set flag to true
ACTION_DESTROYED: set flag to false
ACTION_LAUNCH: if flag, start MainActivity, otherwise start LaunchActivity
(flag can be e.g. a boolean in sharedpreferences or a static field)
in MainActivity:
in onCreate: send ACTION_CREATED broadcast
in onDestroy: send ACTION_DESTROYEDbroadcast
The notification should send an ACTION_LAUNCHbroadcast.
Note: actions can be named as whatever you want. They also should start with you package name, so you don't interfere with other apps.

Determine how Activity was started

I have an app that starts an activity that isn't the MainActivity first, but it is possible within the course of the app to start the activity on its own. I would like code that runs when the activity is closed to be able to determine if it should go the front of the application(first run) or if it should go back to the previous Activity on the stack(all other runs). Is it possible to determine how an Activity was started inside of it?
You said:
I would like to determine within the course of the Child
Activity what Parent Activity started the Child. In my case that will
either be the Launcher or the MainActivity.
Unfortunately, there is no way to find out what Activity launched your Activity. This information is not available. However...
You can tell if the launcher started your Activity by checking the Intent for ACTION = MAIN and CATEGORY = LAUNCHER:
Intent intent = getIntent();
if (Intent.ACTION_MAIN.equals(intent.getAction()) && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
// started by launcher
}
You can also check if the Activity was launched from the list of recent tasks by checking for Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY like this:
Intent intent = getIntent();
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
// Launched from recent task list
}
If this isn't enough for you, then you can always add an "extra" yourself when launching the child Activity from the parent, so that it can tell what Activity started it. For example:
Intent intent = new Intent(this, ChildActivity.class);
intent.putExtra("startedFromMainActivity", true);
startActivity(intent);
and then in your child Activity you can check like this:
Intent intent = getIntent();
if (intent.hasExtra("startedFromMainActivity") {
// started from MainActivity
}
You can store a value in the intent launching your activity, and once opened read it to adapt your behaviour:
intent.putExtra(key,value);
And on the activity side (in onCreate for eg):
getIntent().getExtra(key,defaultValue);
Default value is what you get if no value is found.
getExtra depends o the type of the data stored, so there is getIntExtra ,booleanExtra ,stringExtra ...
Learn more here
your manifesto file exchange mainactivity DEFAULT...
<activity
android:name="com.example.iiintent.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="com.example.iiintent.al">
<intent-filter>
<category android:name="android.intent.category.LAUNCHER" />
</activity>

android : How to work around the fact that a singleInstance activity cannot expect to rely onActivityResult when starting subActivity?

I am making an android application, which has the following execution flow:
A service registers a PendingIntent with the AlarmManager
When the alarm is launched, a Receiver receives the intent, and (given some conditions) calls startsActivity() for my Main Activity, which in the manifest has been declared as android:launchMode="singleInstance". Note that for this call to work, the intent passed should have an Intent.FLAG_ACTIVITY_NEW_TASK
When started, Main Activity modifies itself a bit, and calls startActivityForResult for an Activity, which we'll call WebviewActivity (because it contains a webview, but that's besides the point)
When the user is done interacting with theWebViewActivity, setResult() and finish() are called on it, and one would expect for MainActivity.onActivityResult() to be called.
But of course this does not happen, as has been documented in many discussions here, the reason apparently being that an Activity launched from a singleInstance Activity, runs in a different Task.
A solution I think would be to have the WebActivity start the MainActivity instead.
The question is, is there a way to maintain onActivityResult being called at the right time? In that case, which aspects from the starting point of the execution flow should change?
Please note that MainActivity should not have multiple instances at the same time (it is basically an interface to the service) but if its launchMode is set to standard, the Receiver, because of the FLAG_ACTIVITY_NEW_TASK that is required, will do just that.
Manifest declaration of MainActivity:
<activity android:name=".activities.MainActivity"
android:label="#string/app_name"
android:launchMode="singleInstance"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
android:uiOptions=”splitActionBarWhenNarrow”
</activity>
Receiver launches MainActivity by calling
onReceive(Context context, Intent intent)
{
intent.setClass(context, MainActivity.class);
int flag = Intent.FLAG_ACTIVITY_NEW_TASK;
intent.setFlags(flag);
context.startActivity(intent);
}
I use the following workaround for this problem:
Activity A is the caller
Activity B is the singleInstance activity from which I want the result
In activity A I register a broadcast receiver as following
PickReceiver receiver=new PickReceiver();
IntentFilter filter=new IntentFilter();
filter.addAction("ActivityA_pick");
registerReceiver(receiver,filter);
class PickReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if(intent.getAction().equals("ActivityA_pick")){
//get data from intent extras
}
}
In ActivityB when it's time to send the data I use:
sendBroadcast("ActivityA_pick").putExtra("...data...");
finish();
This way i can get the result I want when I want a result from one of my own activities. If you want a result from the system or another app you can adjust this using a dummy activity that doesn't have launch mode singleInstance, have it start the activity for result and when it gets it onActivityResult it sends the broadcast to the caller.
Hope this helps
As the Main Activity is a single instance, is doing what it has been told.
So yes, you have to start the Main Activity from the Web Activity in order to be coherent with the tasks executions

Android clicking launcher icon starts a new Activity ONLY if issued after starting that activity from notification

Launching an activity by clicking the app icon in launcher, should bring the activity to foreground just like picking it from history. So no onCreate call should exist.
However,if we try to do this after starting the activity by clicking a notification, then the launcher just starts another instance of the activity.
What flags must I add so that the launcher keeps working as expected ( resuming the exact state of the app from background )?
I'll post the essential code.
This starts the notification:
Intent resumeIntent = new Intent(this, MainActivity.class);
PendingIntent resumePendingIntent = PendingIntent.getActivity(this, 2,
resumeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification resumeNotification = new Notification.Builder(this).setContentTitle(
"Resume style")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(resumePendingIntent)
.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
notificationManager.notify(1, launcherNotification);
This is how the manifest activity looks:
<activity
android:name="com.example.ihatenotifiicationsapp.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>
This resumeIntent will be automatically added the FLAG_ACTIVITY_NEW_TASK by Android. This flag will allow the resuming of the application from background if present.
All nice until here, but, If after you click this notification and resume the app, you click the app from launcher then Android launches another instance of MainActivity.
This breakes my application and the backstack ( you will have 2 MainActivity in the stack, weird for user ).
The funnies thing is this happens ( clicking the launcher behaviour to launch another instance ) only after you click the notification.
You can use the Flag android:launchMode="singleTask" in your activity Tag if you want this behavior. This prevents the OS from launching any other Instance, if there is currently one Active. See the SDK Doku for more information on launchbehaviors here
I edited this Answer corresponding to Emanuel Moecklin Comment below. Mixed the lauchModes up.
Excerpt from the Doku:
The system creates the activity at the root of a new task and routes
the intent to it. However, if an instance of the activity already
exists, the system routes the intent to existing instance through a
call to its onNewIntent() method, rather than creating a new one.
Try
Intent resumeIntent = new Intent(this, MainActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP;
If set, and the activity being launched is already running in the
current task, then instead of launching a new instance of that
activity, all of the other activities on top of it will be closed and
this Intent will be delivered to the (now on top) old activity as a
new Intent.
http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP

Start new activity but with the same class name as previous one

I've been trying to find the solution for the following problem, but with no success:
I have an activity class that is showing web pages(presentation) which need to be logged (what user entered, duration, etc.). On some pages there are buttons which should open new presentation.
Calling activity - CLMWebView.class:
Intent intent = new Intent(this, CLMWebView.class);
this.startActivity(intent);
Between those lines of code are some intent.putExtra which are, I belive, not relevant for this.
As you can see, I am trying to start new activity with the same class as the calling activity. However, nothing happens. Any ideas?
EDIT:
Android manifest for CLMWebView.class
<activity android:name="com.msoft.views.CLMWebView"
android:configChanges="orientation|keyboard"
android:icon="#drawable/svicon"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.PICK"/>
<action android:name="android.intent.action.CHOOSER" />
<action android:name="android.intent.action.WEB_SEARCH" />
</intent-filter>
</activity>
singleTop activity, visible, and in same task is not recreated.
Quoting the official docs:
Similarly, a new instance of a "singleTop" activity may also be
created to handle a new intent. However, if the target task already
has an existing instance of the activity at the top of its stack, that
instance will receive the new intent (in an onNewIntent() call); a new
instance is not created. In other circumstances — for example, if an
existing instance of the "singleTop" activity is in the target task,
but not at the top of the stack, or if it's at the top of a stack, but
not in the target task — a new instance would be created and pushed on
the stack.
A better alternative will be to place the functionality in a fragment, and add a new fragment to back stack each time.

Categories

Resources