Android - reopen main activity from notification - android

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.

Related

android handle activity launching

I have the following situation:
when I launch my app the following scenario takes place:
I start the activity StartActivity
If I never did a login (I know it based on some shared preference) launch LoginActivity and show a login layout (username and password)
If I already did a login I start LoginActivity anyway, but I use (crypted) data saved on shared preferences without asking again credentials to the user.
When the login succeed I launch the activity StartActivity
Now in my manifest file I have the following IntentFilter for MainActivity:
<intent-filter android:label="Conio" >
<action android:name="android.intent.action.VIEW" />
<data android:scheme="myprotocol" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
And everything works well with a big "BUT":
everything works only when my app is already opened: my app is brought to front, the onCreate method is called and with getIntent.getData() I can access the URI which started my app.
THE BIG PROBLEM:
WHAT I NEED IS :
When my app is not already started everything should begin from StartActivity as per previous explanation
When my app is running the behaviour must be the current one
To accomplish this I made the following attempt:
I applied that IntentFilter to a new Activity , in this Activity's onCreate method I put the following code
Intent intent = getPackageManager().getLaunchIntentForPackage("my.package.name");
if (intent != null) {
intent.setData(intent.getData());
intent.putExtra("test", "test");
startActivity(intent);
finish();
}
but what happens here is that when app is not running the app correctly starts from StartActivity, but if if the app is already running, MainActivity is brought to front but I can't access the intent extras, in fact this code do not works
#Override
protected void onResume() {
super.onResume();
//this code returns null
String test=getIntent.getStringExtra("test");
}
how can I make all this to work?
if the app is already running, MainActivity is brought to front but I can't access the intent extras:
To get Intent deliver when Activity is running either use LocalBroadcastManager sending new Intent to running Activity instead of calling startActivity method.
OR
Without LocalBroadcastManager add FLAG_ACTIVITY_SINGLE_TOP in Intent or add android:launchMode="singleTask" in AndroidManifest.xml when starting MainActivity :
intent.setData(intent.getData());
intent.putExtra("test", "test");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
and override onNewIntent method in MainActivity:
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//get data from intent
}

Hierarchy of launching activities in android

I have a confusion with how android starts its launcher activities.
If I am declaring a launcher activity in the manifest file like this
<activity android:name=".Activities.Home">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
and I have an Application class which calls an activity based on a check like
if(ParseUser.getCurrentUser() == null){
Intent intent = new Intent(context,Home.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}else{
Intent intent = new Intent(context,MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
which one has the precedence ? The manifest or the Application. What is the flow of events ? e.g. Application->Manifest (or) Manifest->Application (or) Application overrides Manifest ?
If I am receiving a notification in Android, my Application class is called. This makes the activities in the application class to be started, like shown above. Is there a way to detect who calls the Application class ? I mean whether the user starts it, or it starts from the notification which comes in ?
If there is a way to figure this out. How do I prevent the activity in the Application class to be called when I receive a notification ?
Thanks in advance.
Just pass the boolean extras from application and make the diffrence in the call from Application class and User Launch.
Intent intent = new Intent(context,Home.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("APP_CALL",True);
startActivity(intent);
Now in your Home activity Just check for APP_CALL if it is called from Application class then bool value will be true else false.
Also you can write in splash activity for checking login .

Android - How to do nothing when tap on notification app and the app is already opened

I have a notification in my app and I want that when I tap on the notification:
If the app is opened: do nothing;
If the app is closed: open the app;
Current code:
Intent intent = new Intent();
intent.setClass(mContext, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mContext);
mBuilder.setSmallIcon(icon);
mBuilder.setContentTitle(string);
mBuilder.setWhen(System.currentTimeMillis());
mBuilder.setOngoing(true);
mBuilder.setContentIntent(contentIntent);
mBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
mBuilder.setColor(mContext.getResources().getColor(R.color.theme_color));
Notification notification = mBuilder.build();
mNotificationMgr.notify(id, notification);
Activity manifest:
<activity
android:name=".MainActivity"
android:configChanges="keyboardHidden|orientation|mcc|mnc"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:label="#string/app_name"
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
If I tap on the notification and the app is already opened, it opens another app in front of the own app!
If the app is closed, everything is OK!
I'd replace this line:
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
With the following:
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
From the documentation:
public static final int FLAG_ACTIVITY_SINGLE_TOP
If set, the activity will not be launched if it is already running at the top of the history stack.
public static final int FLAG_ACTIVITY_REORDER_TO_FRONT
If set in an Intent passed to Context.startActivity(), this flag will
cause the launched activity to be brought to the front of its task's
history stack if it is already running.
For example, consider a task consisting of four activities: A, B, C,
D. If D calls startActivity() with an Intent that resolves to the
component of activity B, then B will be brought to the front of the
history stack, with this resulting order: A, C, D, B. This flag will
be ignored if FLAG_ACTIVITY_CLEAR_TOP is also specified.
SINGLET_TOP will prevent your activity from being launched again and REORDER_TO_FRONT prevents it from being recreated. Hope it helps.
This can be done by adding the launchMode for your activity in your manifest.xml For example
<activity android:launchMode = "singleTop" ../>
Here is an detail example

Intent Action isn't changing after recalling the activity

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.

Bring application to front after user clicks on home button

My application is in running mode[foreground] and user clicks on home button, which puts application to background[and still running]. I have alarm functionality in my application which fires up. I want is when my alarm goes off i want to bring my background running application in foreground and from last state in which it was.
<application
android:name="com.abc.android.state.management.MainEPGApp"
android:icon="#drawable/icon"
android:label="#string/app_name"
android:largeHeap="true"
android:logo="#drawable/app_logo" >
<activity
android:name=".SplashScreen"
android:label="#string/app_name"
android:launchMode="singleTop"
android:screenOrientation="nosensor"
android:theme="#style/Theme.Sherlock" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Starter"
android:configChanges="orientation|screenSize"
android:screenOrientation="behind"
android:launchMode="singleTop"
android:uiOptions="none"
android:windowSoftInputMode="adjustPan" />
</application>
Intent intent = new Intent(context, MyRootActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
You should use your starting or root activity for MyRootActivity.
This will bring an existing task to the foreground without actually creating a new Activity. If your application is not running, it will create an instance of MyRootActivity and start it.
EDIT
I added Intent.FLAG_ACTIVITY_SINGLE_TOP to Intent to make it really work!
Second EDIT
There is another way to do this. You can simulate the "launching" of the app the same way that Android launches the app when the user selects it from the list of available apps. If the user starts an application that is already running, Android just brings the existing task to the foreground (which is what you want). Do it like this:
Intent intent = new Intent(context, SplashScreen.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // You need this if starting
// the activity from a service
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(intent);
A combination that works for me is to use:
Intent bringToForegroundIntent = new Intent(context, RootActivity.class);
bringToForegroundIntent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(bringToForegroundIntent);
If you check the logs on the device whenever you start an activity from a launcher icon this is the intent that gets passed to launch the app or move it back to foreground if e.g. user clicked the Home button.
I find a new way to bring app to foreground,
It imitate click Icon to launch application.
PackageManager packageManager = context.getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage(pkName);
if (intent != null)
{
//模拟点击桌面图标的启动参数
intent.setPackage(null);
// intent.setSourceBounds(new Rect(804,378, 1068, 657));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
context.startActivity(intent);
}
it work for me, But who can tell me why package must be null
You can use the below code to bring the application to front:
private void bringApplicationToFront()
{
Log.d(TAG, "====Bringging Application to Front====");
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
try
{
pendingIntent.send();
}
catch (CanceledException e)
{
e.printStackTrace();
}
}
From my test, to mimic the Launcher behavior, the key is to:
intent.setPackage(null);
after
Intent intent = packageManager.getLaunchIntentForPackage(pkName);
Other methods in this thread doesn't work in my case.
Thanks to jiaqing's answer, I post this answer as I don't have the right to comment. I don't know the logic behind this either, I guess it's related with the task owner. Anyone knows, would be glad to know.
Using the ActivityManager class you can bring a running task to front
void bringToFront(){
ActivityManager activtyManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> runningTaskInfos = activtyManager.getRunningTasks(3);
for (ActivityManager.RunningTaskInfo runningTaskInfo : runningTaskInfos)
{
if (this.getPackageName().equals(runningTaskInfo.topActivity.getPackageName()))
{
activtyManager.moveTaskToFront(runningTaskInfo.id, ActivityManager.MOVE_TASK_WITH_HOME);
return;
}
}
}
For an alarm, you want to take a look at starting an Android Service
This service will be more resilient than your application which may be killed while in the background and can fire off an intent to bring your application to the front (or restart it if it was killed) when it is time for the alarm to go off.

Categories

Resources