There are three different cases:
1) A user launches an app, navigates in it, pressed home and click on the app icon again to launch our app again.
2) A user launches an app, navigates in it, presses home, chooses recent and click on the app to launch our app again.
3) A user launches an app, navigates in it, click something in the app (TextView with a link), which calls another app (as example Email) and user clicks back button, which bring us back to our app.
I know about flag "clearTaskOnLaunch" flag, it solves case #1.
I know about about flag "excludeFromRecents", it solves case #2 (may be not the most user friendly solution, but it works).
What about case #3? I have a workaround right now. However, I will have to put it on all activities which can be lead to another app. I wonder, whether there is better way to solve it (without handling it in all such activities).
This should be handled on the Application level.
For API level 14, you can register an ActivityLifeCycleCallback in your Application class
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
You can use it, to know on an Application level, which activities are destroyed, paused, resumed etc etc. Whenever, an activity is paused, without a new activity being created/resumed, you should clear the Activity stack, and re-launch your startActivity
If you target SDK versions < 14, you should implement your own method, to know which activities are created/resumed and paused, and do the same whenever an activity is paused, without a new activity being created/resumed
1) define a public static normalPause = true variable in a Class.
2) in onPause method of all of your activities set it false (I am worry. We might not be in a normal pause)
2) in onCreate method of all of your activities set it true (Do not worry. We are in a normal pause)
3) in onResume of all of your Activities:
if(!Utilities.normalPause)
{
this.finish()
}
Enjoy!
It seems a similar question has already been asked. It sounds like the OP came up with a working solution. How do I collapse "child activities"?
EDIT:
Instead of using a button you can use a boolean to tell whether or not you need to collapse back to the main activity. Have your root activity extend from Activity and the child activities extend from CollapsableActivity. To get this to work in all cases I added startOutsideActivity() and startOutsideActivityForResult().
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class CollapsableActivity extends Activity {
private boolean returnToRoot;
public static final int COLLAPSE_BACK = -1; // something other than RESULT_CANEL (0)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
returnToRoot = true;
}
#Override
protected void onStart() {
super.onStart();
returnToRoot = true;
}
#Override
protected void onRestart() {
super.onRestart();
// start collapsing the stack
if (returnToRoot) {
setResult(COLLAPSE_BACK);
finish();
}
}
#Override
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
returnToRoot = false;
}
public void startOutsideActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
returnToRoot = true;
}
#Override
public void startActivity(Intent intent) {
// call startActivityForResult to make sure and catch the collapse condition
super.startActivityForResult(intent, 0);
returnToRoot = false;
}
public void startOutsideActivity(Intent intent) {
super.startActivity(intent);
returnToRoot = true;
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == COLLAPSE_BACK) {
returnToRoot = true;
}
}
}
This worked properly for me in all cases you listed. The only difference is you need to call startOutsideActivity() or startOutsideActivityForResult() when you navigate away from you app. Personally, I think this adds clarity to your intentions. Hope it helps!
I know you don't want to manage it in all activities but you can do this and still handle the code in one place with a super activity
public abstract class BlundellActivity extends Activity {
#Override
public void onPause(){
// Whatever strategy you want
}
}
public class SomeActivity extends BlundellActivity {
// Do whatever you normally want to do
}
public class SomeActivity extends BlundellActivity {
// Do whatever you normally want to do here as well
}
Perhaps, android:noHistory is what you're looking for. If you declare all your activities except StartupActivity with this attribute, then they will be finished as the user navigates away from them and only StartupActivity will appear.
You can try this steps:
use one boolean static flag isFinish in StartupActivity with default false value.
in onCreate() of StartupActivity set isFinish value to false.
write below code in onResume() method of all activities in your project.
if(isFinish)
{
finish();
}
set isFinish value to true when you open any native app like email, browser etc.
or
5 . set isFinish value to true in onBackPress() method whenever you want to close application on back press.
Case 6: if android browser open on clicking on any link then use below code is onPause() method
if(isBrowserRunning("com.android.browser"))
{
isFinish = true;
finish();
}
////////////////
private boolean isBrowserRunning(String processName)
{
ActivityManager manager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
String packageName = manager.getRunningTasks(1).get(0).topActivity.getPackageName();
Log.i("LogTest", "Current process package name: " + packageName);
return processName.equalsIgnoreCase(packageName);
}
You can create a sample project to know other browser package name like opera mini, US browser etc.
add below permission in manifest:
<uses-permission
android:name="android.permission.GET_TASKS" />
You can call this.finish() on the onPause() of your Activity, that way the activity will be closed in the three cases.
You need to use bundle and pass appropriate parameter/or parameters from the calling app (i.e. click something in the app (TextView with a link)).
Retrieve the parameter in the called app (Email app).
You can send the name of the activity in the parameter.
Now being in Email app(the called app) Click of back button navigate back to your calling application.
Optionally you can save the state of activity from the caller program, as required.
You need to use Bundle, and Intent to implement this logic.
Code snippet:
In the calling program, we need to store parameters/data required for back button functionality in the called program.
Bundle bndleData = new Bundle();
Use putString(), putInt() methods of Bundle class.
String prefix = getPackageName().toString();
(this prefix can be stored in application level constants.java file as applicable)
bndleData.putString("ParentActivity", this.getLocalClassName());
Also store additional parameters if required
bndleData.putString("paramName", valueofParamName);
bndleData.putInt("IntChannelImage", chImageInt);
Intent intent = new Intent(v.getContext(), AMRChannelPlayer.class);
intent.putExtra(prefix + "bndleChnlData", bndleData);
startActivity(intent);
Caller Program:
Retrive the data, activity nae from bundle and use it in back button implementation:
prefix = getPackageName().toString();
Bundle extras = getIntent().getBundleExtra(prefix + "bndleData");
String parentActivity = extras.getString("ParentActivity");
extras.getString("paramName");
I hope this helps you.
Instead of using multiple solutions you can use a single one that solves all the problems.
Check this answer:
https://stackoverflow.com/a/8576529/327011
With a Broadcast and BroadcastReceivers in each activities of your application you can kill all activities whenever your application goes to background.
UPDATE:
To detect if your application when to background you can use onStop, check this to understand the theory: Activity side-by-side lifecycle
And this is the implementation: https://stackoverflow.com/a/5862048/327011
I think this is all you need :-)
Related
I have a lot of activities in my app, and I want that if the user closes the app in activity 13 for example, when opening the app at another time the activity returns in activitty n° 13. how can I do this? thank in advance
You could use SharedPreferences to keep track of the last used activity.
Then you can redirect the user in the onCreate of your main activity to the correct activity, and call finish on your main activity.
This could look something like this:
#Override
protected void onCreate(Bundle savedInstanceState)
(...)
int last_activity = getLastActivityIdFromSharedPreferences();
if (last_activity == 1)
{
this.startActivity(new Intent(this, ActivityOne.class));
finish();
}
(...)
}
I have an application that contains four activities, within the application the user will find himself navigating through the 4 activities constantly. The application also has an ongoing service in the background, the service displays a statusbar notifications, and listens for changes to the content that will then appear on the notification.
Currently, the service displays the notification whenever the user starts an action that required the notification to show, therefore, the notification is shown even when you are still using the application. The desired scenario is to show the notification only when the user has navigated out of the application.
I attempted to override lifecycle methods like this:
#Override
protected void onPause() {
Intent intent = new Intent();
intent.setAction(MyService.ACTION_DISPLAY_PENDING_NOTIFICATION);
sendBroadcast(intent);
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
Intent intent = new Intent();
intent.setAction(MyService.ACTION_CANCEL_NOTIFICATION);
sendBroadcast(intent);
}
And the service goes like this:
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(ACTION_DISPLAY_PENDING_NOTIFICATION)) {
showNotification();
}
else if (action.equals(ACTION_CANCEL_NOTIFICATION)) {
mNotificationManager.cancel(mNotId);
}
}
This, works. But, since the intent is sent anytime the user navigates away from an activity , I am experiencing an undesired behaviour and slight performance hit when the user navigates through the 4 activities. The service will attempt to display the notification even when going from Activity A to Activiy B, or any combination within the 4 activities.
The notification is immediately cancelled, as when a new Activity B starts it will call mNotificationManager.cancel(mNotifId) during onResume, but the notification was built and shown for a fraction of a second as the service was told to do so when leaving Activity A. That is the behavior I want to address, rather than building and showing this notifications unnecessarily,
Is there any way I can know when the user is leaving the activity to another application, i.e the homepage, etc; but not within the application itself?
EDIT:
Just to clarify, there are two things the activity would have to check for during the onPause method,
a) Is there any previous activity on the foreground? Why? Because the user could navigate out of the activity by pressing back, meaning the last activity on the stack would be displayed. In order to check for this, the answer from DennisDrew would work, we can check like this:
if(!ForegroundHelper.activityExistsInForeground()){
//show your notification
}
But that is not the only way the user could navigate out of the activity, the user could also press the HomeKey, in which case, whether activityExistsInForeground() evaluates to true or false, the notification should be displayed.
b) Is the user going to another activity in the application? For instance, user is on Activity A, A is the only activity on the foreground for now, user clicks on an UI element that launches Activity B. Despite of activityExistsInForeground() evaluating to false, user is not leaving the application, he is launching a new instance of an activity that was previously not on the freground.
I've tried to add flags such as private boolean launchingNewActivity = false as default, and setting the flag to true when I know I am going to another activity, say for instance during the click of an item on my listview:
litview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
launchingNewActivity = true
startActivity2(arg2);
}
});
and then checking for that during onPause:
#Override
protected void onPause() {
if(!ForegroundHelper.activityExistsInForeground() && launchingNewActivity){
//show your notification
}
But doing this, it never shows the notification, somehow the double check always defaults to false.
What if you used a singleton reference? You could make a class like:
public static class ForegroundHelper {
public static boolean[] activityStates = new boolean{false, false, false, false};
public static final int ACTIVITY_A = 0;
public static final int ACTIVITY_B = 1;
public static final int ACTIVITY_C = 2;
public static final int ACTIVITY_D = 3;
public static boolean activityExistsInForeground(){
for(boolean b : activityStates){
if(b)
return true;
}
return false;
}
}
Then, in each activity's onResume() do:
//For your first activity (Activity A)
ForegroundHelper.activityStates[ForegroundHelper.ACTIVITY_A] = true;
And in each activity's onPause() do:
ForegroundHelper.activityStates[ForegroundHelper.ACTIVITY_A] = false;
And then in the onStop() of each activity do:
if(!ForegroundHelper.activityExistsInForeground()){
//show your notification
}
By the way, I haven't ever done something like this, so I have no idea if it will work exactly as I've coded it...but let me know if this helps.
What I did for that is that I extended the application class and kept there the "topActivity" that I would set in each onResume /onPause method like so:
public class myApp extends Application{
private Activity topActivity;
}
Now in each activity
#Override
protected void onResume() {
((myApp) getApplication()).setOnTopActivity(this);
}
#Override
protected void onPause() {
((myApp) getApplication()).setOnTopActivity(null);
super.onPause();
}
This way, when the app is not visible, topActivity is null. So, before showing your notification, just check whether or not topActivity==null. If it is, your app is not in the foreground, you can show your notification.
If you don't have access to your application class, you can always store this value in a static way :)
I'm trying to make a certain function to start only when a user,
Opens the app for the first time,
Goes back to an app from home.
But not start if the user switches between activities within the app.
I have looked through this topic,and the best answer is to use singleTask with onNewIntent(). So, if a user is goes back to the app from Home, a onNewIntent call with the launcher intent passed to it can be used.
However, here is my code:
public class AdMobSDK_DFP_Interstitial extends Activity implements AdListener {
private static final String MOBMAX_INTERSTITIAL_AD_UNIT_ID = "/7732/test_portal7/android_app1_test_portal7/splash_banner_android_app1_test_portal7";
private DfpInterstitialAd interstitialAd;
private int num = 0;
public void onNewIntent(Intent intent){
super.onNewIntent(intent);
Log.d("flow", "onNewIntent");
}
If I switch between different activities in the app, onNewIntent() is always called, which is the same as I go back to the app from Home.
First thing you can do is to implement your own "Application" object and have it run the needed function when it is created.
public class MyApplication extends Application {
#Override
public void onCreate() {
// Call your function
}
}
Your application object will be live as long as your app is alive (any activity/service is still running), but note that the Application object is not destroyed immediately when the user presses "Home", and might stay alive for a while and a user can return to it without the function being called.
If you need this function to run as part of your main activity, just save a flag in your Application context :
public boolean alreadyDisplayed = false; and then in your activity's onStart you can just call
if ((MyApplication)getApplication().alreadyDisplayed ) {
// Call your function
(MyApplication)getApplication().alreadyDisplayed = true;
}
** If this solution is not enough for you and you need to call your function every time your main activity is displayed from the home page you'll need to do something not as nice... one suggestion I can give you is to implement the same Application object but this time with an "open activity" counter:
public class MyApplication extends Application {
public int mActivityCounter = 0;
}
Then you can increment this counter on every onStart of activity in your app and decrement on every onStop (of course this can be done by implementing a class MyActivity and make all your relevant activities inherit it. Then you can use this counter to know if there are any other activities opened. Note that you'll have to make sure the access to this counter is synchronized and work your way with it as you need.
I hope this helps...
There are some applications in which you can logout at any point within. e.g you login and then your browse around. you use the action bar or the menu button to logout. I can call finish() at that very point but then it will just pop the last activity. Even if i move the user forward to the Home Activity, still the stack remains in memory. Is there any way to destroy the remaining stack?
The easiest way to do this is to clear the stack back to your home or first activity, and pass an identifier saying to exit the app. For example:
public class ActivityOne extends Activity {
public static final String FINISH_THIS = "FINISH_THIS";
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if(intent.hasExtra(FINISH_THIS)) {
finish();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getIntent().hasExtra(FINISH_THIS)) {
finish();
}
}
}
public class ActivityTwo extends Activity {
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == R.id.logout) {
Intent intent = new Intent(this, ActivityOne.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(ActivityOne.FINISH_THIS, true);
startActivity(intent);
}
return true;
}
}
If ActivityOne is the root of your stack and ActivityTwo is where the user selected the option to logout, starting an intent that clears back to ActivityOne will get rid of the backstack. If you want the app to exit when logging out, you can pass an extra like I did with FINISH_THIS to signal the root/home activity to finish.
Another way to do this would be to call setResult(FINISH_THIS) where FINISH_THIS is an int identifier before calling finish(). Then in all other activities in the stack, you'd override onActivityResult and check the result to see if that activity needs to be finished. If it does, you set the result again and keep passing it down the line.
Using the intent method I outlined in the beginning is the preferred method for clearing the stack as it doesn't rely on daisy chaining results together but both options work well enough.
A quick and dirty way to do it would be onResume verify you are still logged in, if not then finish it. That way if they hit back, it will close each activity as it tries to open them. This would also prevent someone from using the app manager to re-enter your activity when you expect it to be closed.
Another idea would be read Android: Clear the back stack
I have tried almost all the solutions from SO but no success :(.
I have a simple myJavaClass.java with a couple of functions.
One of the functions in myJavaClass : startActivity() starts MyCustomActivity
public startActivity(Context context)
{
Intent intent = new Intent(context, MyCustomActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
This launches MyCustomActivity() as expected.
Now I have another function in myJavaClass.java to close/finish MyCustomActivity but it is not able to do so!
I have tried
Making MyCustomActivity SingleTop in manifest and creating the activity via an intent as above
Passing an activity instance to "this" in onCreate() of MyCustomActivity and calling MyCustomActivity.activity.finish() from myJava.class but that doesnt work as well
Please help me. I have been stuck here for hours now. I know the solution is very simple and conceptual but I am a newbie. Just building Java/Android concepts!
EDIT
MyCustomActivity
public Activity activity;
OnCreate()
{
...
this = activity;
}
MyJavaClass
public closeActivity(Context context)
{
Activity customActivity = MyCustomActivity.activity;
customActivity.finish();
}
I think that what you are trying to do is fundamentally bad. For a start, outside of the Activity code, there are no guarantees that the activity still exists - the memory manager may have cleaned it up, the user may have pressed Back etc. Think of Activities as independent entities - you can start them, and you can optionally get a result back when they finish what they're doing, but that's it.
Think about whether you really have to programmatically close the activity from outside it - I'd say this is an unusual design, but there are circumstances where it may be appropriate.
If so, what I think you want is a publish/subscribe system whereby MyCustomActivity can register a listener with MyJavaClass, and then receive a callback whereupon it can 'finish' itself.
public Activity activity implements FinishListener
{
public void onCreate(...)
{
//where does MyJavaClass come from? see in a minute
MyJavaClass myjava = getMyJavaclass();
myJava.addFinishListener( this );
}
public void onFinishCallback()
{
this.finish();
}
}
and
public class MyJavaClass
{
private List<FinishListener> finishListeners = ...;
public void addFinishListener( FinishListener fl )
{
this.finishListeners.add(fl);
}
public closeActivity(Context context)
{
for ( FinishListener fl : finishListeners )
{
fl.onFinishCallback();
}
}
}
and
public interface FinishListener
{
void onFinishCallback();
}
Now the only remaining issue is how to get MyJavaClass from the Activity. That's up to you - you may already know how, you may be able to put it in your Application implementation, it could be a singleton (bad), the listeners could be static (bad) or various other options.
Oh, and don't forget to remove the listener again in the Activity's onDestroy() method!
Just try this....
public closeActivity(Activity _activity)
{
_activity.finish();
}
you can't finish activity from other class until you have the reference of instance of Activity in that class, give the reference in that class and call finish() method to stop the activity.
activity.finish();