Android: keep alert in front, so that the user must respond - java

My application shows an alert that the user must respond to before continuing to do other things. I'm trying to figure out the best way to implement this. Using an Activity for the alert isn't quite working.
In my current implementation, the alert is activity (A). When another activity from the same package is started and onStop is called, it starts itself again using FLAG_ACTIVITY_REORDER_TO_FRONT so that it's always at the top of the stack. This works as described, unless Activity A uses Theme.Dialog or Theme.Translucent.
Modified log:
Activity A created
Activity A started
Activity A resumed
Activity A paused
Activity B created
Activity B started
Activity B resumed
Activity B gains window focus
Activity A stopped
Top activity in stack is Activity B, so Activity A relaunches itself
Activity B paused
Activity A started
Activity A resumed
The top activity in the stack should be Activity A, however Activity B remains in the foreground.
Another implementation detail: my application is not for a phone, so I'm not concerned with a back button finishing the activity or interactions with other apps. Still, I agree that on principle I should prevent such problems anyway, so in my code I check whether the activity that has come in front is from the same package (i.e. from our code base). This should work around the theoretical problem of interfering with other apps.
Is there a way to bring Activity A into focus? I understand that this is unusual behavior, but it is necessary for Activity A to remain in the foreground until it is deliberately finished.
I'm also open to suggestions about a completely different and better approach!
FWIW, I'm running 2.2.
(Cross-posted from http://groups.google.com/group/android-developers/browse_thread/thread/d46fd7d59abe15a0, where we got no response.)

You can't do this. Please don't do this. The activity at the top of the stack is the one that has input focus. What you are trying to do fundamentally breaks the user interaction that is supposed to happen.
What you are doing is generally considered by the platform to be an abuse of it, and Android has increasingly been doing things to prevent applications like this from causing harm.

Well, here's what I had in mind:
public class ActivityA extends Activity
{
...
public void onStop() {
super.onStop();
finish();
Intent i = new Intent();
i.setClass(getApplicationContext(), ActivityA.class);
startActivity(i);
}
}
ActivityA is finished in onStop() and started again right away. You might have to check issues regarding device rotation, but this approach should work.

Having window focus means that activity B is still in its visible lifetime, since it has on top the activity A which has a translucent bg or is dialog-like.
Having the window focus doesn't mean that activity B is on the foreground. They are different things.
If you don't want this, then don't use those two themes.

Related

is the "MainActivity" special kind of activity, does it differ from any other activity?

Except being the first Activity upon the Start of the Application, is there anything else special about main activity?
From:https://developer.android.com/codelabs/android-training-create-an-activity#0
An app usually consists of multiple screens that are loosely bound to
each other. Each screen is an activity. Typically, one activity in an
app is specified as the "main" activity (MainActivity.java), which is
presented to the user when the app is launched. The main activity can
then start other activities to perform different actions.
From the quote above it seems to me that we have following hierarchy:
but then further is said:
Each time a new activity starts, the previous activity is stopped, but
the system preserves the activity in a stack (the "back stack"). When
a new activity starts, that new activity is pushed onto the back stack
and takes user focus. The back stack follows basic "last in, first
out" stack logic. When the user is done with the current activity and
presses the Back button, that activity is popped from the stack and
destroyed, and the previous activity resumes.
Does this also apply to the "MainActivity"? If the "MainActivity" is destroyed does that cause the App to crash i.e does the lifecycle of the MainActivity in any way differs from the lifecycle of any other activity? Is the MainActivity the last activity that is being stopped when the App is exited?
Why do I need this:
I would like to free some resources when the App is exited (in the onStop() Method (because post Honeycomb it is guaranteed that onStop will be called)), especially ExecutorServices, I've read here that even though App is exited there is no guarantee that an ExecutorService will be stopped and will make JVM continue working/running, even though App is closed/killed and will continue to use system resources.
Main Activity is the entry point for your app when user presses the icon for cold launch. You make any of your Activity a main activity in AndroidManifest.xml file via Intent Filter. Intent Filter tells the system which activity is main.
Although main activity is considered first entry point typically, but keep in mind, Main Activity is not always the first activity to be launched, for example there are various intent filters that can be assigned to your other Activity and that activity can be opened directly following the related action. Please read about the Intent-Filter here.
For example, your app is gallery app, typical first screen would be album list. from where you can view individual photo in PhotoActivity. This PhotoActivity can be opened directly via intent from outside apps to view a certain photo without needing to launch the main activity. (Check out google Photos app)
Regarding ExecutorServices or other services lifecycle, few options here:
implement a ownership machanism, ie the activity that starts the service is responsible for closing the service
You can monitor your app's activity stack and kill the service when your Activity stack is empty.
Leverage Application class lifecycle to monitor things.
Reasonable discussion here https://stackoverflow.com/a/5862048/593709

Any consequences of startActivity() after having already started the activity?

Let's say I have Activity A and Activity B.
In A, I have a button which when clicked runs the following code
startActivity(new Intent(this, ActivityB.class));
In B, I have a button which when clicked, runs the following code
startActivity(new Intent(this, ActivityA.class));
So when the app is started and the user does the following:
Clicks on button in Activity A (he goes to Activity B)
Clicks on button in Activity B (he comes back to Activity A)
Again clicks on button in Activity A (goes to Activity B again)
In this case, do the Activities A and B hog the memory since they get started each time or is Android smart enough to know that an activity has already started and simply needs to be put on top of the stack?
So in other words, if memory does indeed gets clogged up, is there something like:
if(activity has already started)
{
doNotStartActivityButSimplyPutItAtTop();
}
else
{
startActivity(new Intent.....);
}
yes, your assumption is right android is capable of managing these activities and they are maintains proper stack for it.
if you are having only two activities you can use singletop launcher mode so that only only two activities remain in stack.
The android system may release the resources from previous activity in the stack. To avoid losing the current state of the activity you may have to save it and then restore when the activity is recreated. See Recreating an Activity.

Activity can't be resumed

Over the past months I have been developing a versatile real-time game engine, and I have learned a lot, but I still feel very naive when it comes to the application life cycle. Specifically, I am trying to implement an Activity which can be shuffled in to the background by the user, then properly resumed.
My current architecture is as such: I have a an XML menu launcher activity which can create a real-time Game activity using intent. Relevant data in this Game activity is referenced through static data structures and variables. The Game activity creates worker threads in the onSurfaceCreate() callback of my SurfaceView object.
When a user presses the back button, the activity is destroyed, and they are sent back to the XML menu in the launcher activity. Fine, good for now. When the user presses the home button, the Activity is sent to the background. OK, great. My problems arise when the user tries to find their way back in the Game activity once is has been sent to the background. When the user touches the launcher icon, the Game is destroyed and the menu is re-launched. Also, when the user resumes the game through the task manager, the onSurfaceCreate() callback fires and the worker threads are started, but the game is frozen.
So, I have two questions. First, how do I resume my activity through the launcher icon, instead of re-launching the game? Second, when I resume my activity, what actions are necessary to restart my worker threads while persisting the game data?
Thanks!
EDIT: By request, I am including some code below. My onSurfaceCreate is a little bit complicated, it sends a message to another thread, which then implements the callback. I have verified that this implementation function fires when the Game activity is resumed.
protected void surfaceCreate()
{
Log.e(TAG, "surfaceCreate");
Thread gameThread = createGameThread();
gameThread.start();
}
protected final void onResume()
{
super.onResume();
resume();
}
protected final void onPause()
{
super.onPause();
pause();
}
These cryptic pause() and resume() methods simply set a boolean variable which prevents some game logic from being executed, they do nothing to hinder the worker threads, which should continue looping.
EDIT: Thanks Mohammad, for solving my first (although smaller) problem. It turns out that the launcher icon behaves differently when not connected by USB to the IDE. The second problem remains unresolved.
EDIT: All working! The second problem turned out to be an issue unique to my code, I apologize for that. I hope that this question can still be useful for those dealing with the launcher and IDEs.
There are multiple possible problems/solutions listed in this post. This includes misunderstanding of activity lifecycle and launchMode settings.
First, how do I resume my activity through the launcher icon, instead
of re-launching the game?
You are most likely missing definition for your onResume() and onPause() methods. Examples are here:
http://developer.android.com/training/basics/activity-lifecycle/pausing.html
Straight from the API:
onPause() Called as part of the activity lifecycle when an
activity is going into the background, but has not (yet) been killed.
onResume() Called after onRestoreInstanceState(Bundle),
onRestart(), or onPause(), for your activity to start interacting with
the user.
Check the Android Activity Lifecycle out:
http://developer.android.com/reference/android/app/Activity.html
One theory on your issue may be is that you're most likely going to onStop() (by hitting the home button) and the Android OS is looking for your onRestart(), but can't find it hence freezing/restarting.
Second, when I resume my activity, what actions are necessary to
restart my worker threads while persisting the game data?
Careful how you use the word restart. You want to pause and resume the application (not restart). Save data in onPause() (use a database or any other save feature you'd like). Load data in onResume(). Although, the activity should resume as normal if you just fill up these methods.
Now, if you want to save state for when you restart the application, you should save states in onStop() and/or onDestroy(). You should load states in onStart(). In order to save states, you can check this out:
https://stackoverflow.com/a/151940/2498729
For those using Eclipse (or any other IDE to run/test your application):
From what have you described you probably have overridden
android:launchMode in AndroidManifest.xml or if you are testing by
"run as" from Eclipse try exiting the application after installing and
auto-starting. Then start again from the emulator and test the Home
button behavior. I suppose this is because Android does not put
Activities on the OS stack when started from Eclipse and then the Home
button behavior is not as usual. If this does not solve your problem,
try reading
http://developer.android.com/guide/topics/fundamentals.html#lmodes.
I had launchMode set in my StartupActivity. THen I removed that (it
was set to "singleTask", it behaves like I want it; the app is
"resumed" to the Activity I expect, ie not StartupActivity but
MainActivity.
Source:
https://stackoverflow.com/a/1619461/2498729
http://developer.android.com/guide/topics/manifest/activity-element.html
According to this:
https://stackoverflow.com/a/3002890/2498729
You should change your andoird:launchMode to "Standard" (or "default").
Standard: A > B > HOME > B (what you want)
SingleTask: A > B > HOME > A (what you don't want)

android finish current activity causes app close

I am executing maswebview class and I would like to finish only this activity. I tried maswebview.this.finish() but when executed, app is been closed. Then if I set a new view for the tab content, it is loaded properly and webviewmas dissapears but just for a while, then appears again fitting fullscreen. How to finish maswebview completely? ThanK you
public void onClick(View arg0)
{
/*
Intent intent = getIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
startActivityForResult(intent, 1);
Intent intentmas = new Intent (maswebview.this, mas.class);
intentmas.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intentmas.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentmas.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentmas.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
View vista = getLocalActivityManager().startActivity("maswb", intentmas).getDecorView();
setContentView(vista); */
maswebview.this.finish();
Do you have any other activities of your app in the stack by the time you call finish()? If you don't, you'll want to start the desired activity instead of finishing the current one.
But actually it seems to me that you're trying to accomplish something that can be done simpler. Can you provide more info on the task at hand and your app structure you're trying to go about it with?
From what you said, it seems like you have tabbed UI and you're trying to show a webview in one of the tabs, then hide it.
First, I don't see why you want the webview in a separate activity. Seems to me you could just have it in layout of one of the tabs and just call setVisibility(GONE) to hide it.
Second - and this is important - looks like you're trying to implement tabs the old way - TabWidget, TabHost, etc. Since Honeycomb has been released, there's much more convenient way to implement tabbed UI - Action Bar Tabs and Fragments - that approach is much more convenient and will render your webview problems obsolete: there's a thing called WebViewFragment which is basically a WebView but smarter - it will handle its own lifecycle with minimum effort required from you (i.e. pause when removed, resume when added). It will take some effort to study up on Fragments, but it's well worth it. You can get Fragments API for pre-Honeycomb android sdks as a static library - it's called android-support-v4 and you can download it in Android SDK Manager.
Are you calling "maswebview.this.finish();" before the new Activity is started?
Also if you want to just start this new activity without having the old activity in existence then you can add android:nohistory="true" to your manifest.xml. This will cause the current activity to be the only activity in the queue.
See HERE
You need to provide a little more information for us to better understand what exactly is going on.
I hope this helps.
Try following code
#Override
public void onBackPressed() {
if(childView != null && parentLayout.getChildCount()==2){
childView.stopLoading();
parentLayout.removeViewAt(parentLayout.getChildCount()-1);
if(webView.getVisibility() == View.GONE)
webView.setVisibility(View.VISIBLE);
}else{
super.onBackPressed();
}
Do you have a parent activity which is launching this one? If yes make sure you don't call finish() after launching maswebview, that way the parent activity will remain in the stack. Once maswebview would exit, onResume would be called for your parent activity.

how to make the appliction exit when pressing back button in the main activity?

In my application when i click the back button it passes through all the activities that i open them previously , i used the public void onBackPressed() method to make the back button back to the activity that i want as follow
public void onBackPressed()
{
startActivity(new Intent("com.MyDiet.Main"));
Tracker.this.finish();
}
is that true and safe way to code the back button ? how i can prevent the application from passing through all the previous opened activities when the back button is pressed ? and how i can make the application exit when i click the back button in the main activity?
In your application, and in ALL android applications, unless it's critical not to pass through unneeded steps (such as login if you're already logged in), it's VERY important not to override Android standard behaviour. Users normally complain about Android apps not having a common behaviour or style guideline.
Anyway, yeah, you can just override onBackPressed on all your activities and do whatever you want. But just don't.
This approach isn't good, because you're polluting the activity stack of your application
Example:
current stack: MainAct -> Act2 -> Act3 (we're in activity 3)
With your code above, when you press back, the stack now looks as follows:
MainAct -> Act2 -> MainAct
Because you ended Act3 and launched a NEW main activity, which may be not what you wanted.
To achieve what you want (Get back to main when the current activity is over) you need to work on the intermediate activities: In the example above, when from Act2 you call startActivity("Act3"), you should call "this.finish()". Therefore you don't have to override "onBackPressed()" of activity 3: simply the default value will terminate Act3 and the next activity in the stack will be MainAct
MainAct -> A2 (A2 launches A3 and then calls this.finish())
MainAct -> A3 (user now press back)
MainAct (the mainactivity is now on top)
To summarize, you don't have to override onBackPressed, you just have to correctly manage the lifecycle of the activity between the main one and the current one.
Generally speaking it's not recommended to make things work like user doesn't expect and that is considered as very bad practice. That's why it is not good to start activity from overriden onBackPressed().
When user press back by default activity will finish and one from back stack will be displayed. You as developer will (by writing code) decide which one is that. You can do it this way but later (or for somebody else) it will be a bit messy and difficult to find this unusual place for code which is starting other activity.
So..It would be useful to read about activity lifecycle and back stack to get impression how it works and understand terminology better.
If you want one of your activity not to stay on back stack you can add in manifest file file android:noHistory="true" for that activity.
Same thing you can achieve from code by adding appropriate flag to intent when you start activity: Intent.FLAG_ACTIVITY_NO_HISTORY
When user "go away" from ActivityOne (started using intent or defined in manifest like described), to new ActivityTwo and then press back, it will not go to ActivityOne because it will not be on back stack. Using this you can precisely control navigation through your activities.
More flags are available for use, but I guess this is what you wanted to achieve.
Hope you will find my answer useful. Cheers..
You can use a lot of tricks to exit your complet application for my part i use this start a intent and then from the androidmanifest i choose the category to home and than close the current activity such would be your mainactivity !
public void onBackPressed() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}

Categories

Resources