Displaying a ProgressDialog while Android GL context reloads - android

I am working on a game that takes a bit of time to reload its textures when the app loses focus. During this time, the app is unresponsive to the user.
I have setup a handler to tell the main Activity class when the textures are done loading so it can hide a ProgressDialog that I start when the user leaves the app as shown below:
#Override
public void onWindowFocusChanged(final boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus) {
if (this.windowLocked && this.gamePaused) {
this.gamePaused = false;
Cocos2dxHelper.onResume();
this.mGLSurfaceView.onResume();
}
this.windowLocked = false;
} else {
this.windowLocked = true;
}
}
#Override
protected void onResume() {
super.onResume();
if (!this.windowLocked && this.gamePaused) {
this.gamePaused = false;
Cocos2dxHelper.onResume();
this.mGLSurfaceView.onResume();
}
if (!this.dialogShowing && this.gamePaused) {
displayResumingDialog();
}
}
#Override
protected void onPause() {
super.onPause();
this.gamePaused = true;
Cocos2dxHelper.onPause();
this.mGLSurfaceView.onPause();
}
The functions to show and hide the dialog look like this:
static public void displayResumingDialog() {
dialogShowing = true;
resumingDialog = ProgressDialog.show(sContext, "Re-Initializing", "Please Wait...", true);
}
static public void hideResumingDialog() {
if (resumingDialog != null && resumingDialog.isShowing()) {
resumingDialog.dismiss();
dialogShowing = false;
}
}
This works absolutely fine if the user leaves the app via the home button and then comes back by opening the apps drawer and clicking the icon again. If they lock the screen and return, or hold down the home button and return from the list of running apps however, the dialog never gets hidden. In fact, everything except for the dialog stops running. If I setup the dialog to be dismissed when it is touched outside, and I dismiss it, the app is still hung until I press the Back button. Then after a few seconds the textures are reloaded and the game begins.
I've done a fair bit of logging trying to figure it out. All of the calls seem to be the same, except sometimes when the screen is locked, onResume gets called twice. I think my code accounts for that though. If I don't allow it to display when the screen is locked the code run through to the end with no problem. With the dialog up and the screen returns from being locked, the code stops once the dialog shows up so onSurfaceCreated never get called and the textures never have a chance to load. As I said, it doesn't resume until I dismiss the dialog and press the back button.
What would cause the ProgessDialog to completely take over like that? Perhaps more importantly, why does it do it when returning from a locked screen or running apps list but not when clicking on the app icon?

Well, I hate to answer my own question, but I figured out the problem and figured it might help someone else.
The dialog comes into focus when it is created, so the onWindowFocusChanged code never gets called, so the textures never get reloaded. I fixed it by making my dialog not focusable, like so:
resumingDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
Although the dialog locks up with the rest of the UI if the user returns from a locked screen at least it is there. I tried showing the dialog in an asynctask to get it to spin while the rest of the UI was locked up, but no such luck. If anyone can tell me how to get the dialog to spin I'd be glad to award the answer to that person. It spins if they come back to the app via the running apps list or the app's icon, just not from a locked screen.

Related

IllegalStateException when adding fragment in response to a screen lock

Firstly, I believe this is not a duplicate question, although the solution for the error has been asked a lot of times. I have tried atleast 5 different solutions but they either don't change anything or make things worse. I want to pause the activity when the screen is locked/focus changed and allow the user to unpause the activity when the app is opened again.
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (!hasFocus && pauseFragment == null) {
exercisePlayPause(isPaused, workoutExerciseNum);
isPaused = !isPaused;
}
}
exercisePlayPause calls the following method
public void PassExerciseNum(int exerciseNum, Boolean isPaused) {
if (!isPaused) {
pauseFragment = new PauseFragment();
pauseFragment.getExNum(exerciseNum);
getFragmentManager().beginTransaction().add(R.id.aworkout_layout, pauseFragment, "pause").commit();
} else {
getFragmentManager().beginTransaction().remove(pauseFragment).commit();
pauseFragment = null;
exercisePlayPause(true, exerciseNum);
}
}
This works fine when the home button is pressed or some other app is activated or the notification bar is clicked. But when the screen is locked then I get the following error
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1411)
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1429)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:687)
at android.app.BackStackRecord.commit(BackStackRecord.java:663)
The offending line is getFragmentManager().beginTransaction().add(R.id.aworkout_layout. I am pausing few processes when any event that makes the user move away from the app. And when the user comes back to the app, he/she can resume from there. Hence, I am not destroying the activity. I have tried using commitAllowingStateLoss() but that makes me lose data for the paused processes and becomes a bit messy.
So in short the question is how to make a fragmenttransaction happen before onSaveInstanceState is triggered when the screen lock button is pressed?
The activity's orientation was sensorLandscape. When a screen locks it converts the screen to portrait and when the screen is unlocked it starts from a portrait mode and then becomes landscape. Hence, onDestroy was called when screen is locked. And when the screen is unlocked, onCreate gets called. This orientation change overrides the activity's orientation set in manifest. What happens when this forceful orientation happens can be controlled by adding android:configChanges="orientation|screenSize|keyboardHidden"/> to the manifest. This prevents onDestroy being called and the "illegalStateException" can be avaoided.

android: dim the screen when application is in background

I am developing a small app which shows passwords of the user through a Dialog screen.
When home button is pressed, I need to dim the screen (on the multi tasking window) so that any other person cannot see the password.
When user re-opens the app, it asks an application lock. But if the user leaves the password Dialog open and presses the home button, dialog and the password which user last looked at stays visible (on the multi tasking window) for a while (3-4 seconds!!) until a new dialog asks the lock.
So far I tried ever possible dialog.dissmiss() options. Dialog dismisses only when app is opened again (until a new lock dialog appears) even I put dismiss() in onPause, onStop etc.
Any idea appreciated.
I also tried,
android.os.Process.killProcess(android.os.Process.myPid());
this.finish();
System.exit(0);
none of them actually worked.
Suggestion 1: Double-check your implementation. Tying your dialog to the activity lifecycle seems like a good idea (especially to avoid leaked window errors as described here)
The following example works out well for me (with coachMark being derived from Dialog)
#Override
protected void onResume()
{
log.debug("onResume");
super.onResume();
// Show the coachMark depending on saved preference values
coachMark.mayBeShow();
}
#Override
protected void onPause()
{
log.debug("onPause");
// Hide the coachMark if it is showing to avoid leakedWindow errors
coachMark.maybeHide();
super.onPause();
}
onPause definately gets called when you press the home button, so if this approach does not work for you, try not recreating the dialog in the restarting part of the acitivty lifecycle (onRestart(), onStart() and onResume()) and see, if it gets dismissed correctly.
Suggestion 2: Should all of the above fail, you might consider overriding the home button as described here. I highly advise against it though, since this may cause the app to work in an way that the user does not expect it to.

onPause won't stop my vibrator

I declare my variable 'shake' as a global variable, then I created new object inside the oncreate, then I call this :
#Override
public void onPause() {
super.onPause();
shake.cancel();
}
my phone can still vibrate although home button is pressed! I tried onStop(), same doesn't work..
my app is like this : countdown 10 sec, after that vibrate.. but the problem is onPause cannot be call so the user may feel where's the vibrate come from if it's set 2 minutes on the countdown ticker.. help!
Since I can't see the rest of your code, I'm gonna assume a few things.
Assumption #1
If you have your activity open, and the countdown starts and expires after 10 seconds, your phone vibrates (with your activity still open). If you go to home screen, the vibration stops.
Assumption #2
You have your activity open, and the countdown starts. Before the 10 second expires, you go to home screen. Your activity is not visible, but the phone starts vibrating soon.
If this is what you are seeing, it's the correct behavior. The problem is that in the 2nd case, your shake.cancel() from onPause() is called when you go to the home screen, before it actually starts vibrating. shake.cancel() can only cancel if it's already vibrating.
If that's what you are trying to fix (I can only assume since I can't see the rest of your code), you can try this:
private boolean mAllowShake = false;
#Override
public void onResume() {
super.onResume();
mAllowShake = true;
}
#Override
pulic void onPause() {
super.onPause();
mAllowShake = false;
shake.cancel();
}
// wherever you are calling the shake.vibrate()
if (mAllowShake)
shake.vibrate();
This way, when your activity is not visible and your timer goes off, since mAllowShake is false, it won't actually vibrate.
If that's not what you are trying to fix, please update your question with more code and description of your exact use case. Hope it helps!

Android bug? Popup dialogs and spinners invisible; screen dims

I have an app that has a few Spinners and a couple of Buttons that pop up Dialogs. Other Buttons start new Activities.
On startup, these all work fine. However, when a new Activity is started and then we return, all pop-up elements stop working properly - the screen dims, but no dialog appears. In this state:
if you tap where the dialog's buttons should be (such as an "OK" button), their callbacks are called and everything works fine
rotating the phone will cause a redraw and the dialog will be visible again
This seems to me to be an Android bug. There seem to be very few references to it anywhere, and I'm at a loss as to what triggers it, and how I can work around it. As requested by #ethan, code fragment for one possible path is below, but there's little to it; I don't need to return any results, and the problem is exhibited when the user simply presses the Back button (which just has the default binding).
private OnClickListener button_click = new OnClickListener()
{
public void onClick(View v) {
int lId = v.getId();
...
if ( lId == R.id.cancel_button ) {
finish();
}
...
}
}
This is probably not helpful - I'm hoping someone will recognize the symptom. In the meantime, I'm trying to work up a mock example that exhibits the problem.
It does not occur in 1.5 or 1.6, but does in 2.2.

onPause not called when back button pressed?

It was my understanding, obviously wrong, that onPause() is called whenever the back button is pressed? Now in my code I've put this onPause() event:
#Override
protected void onPause(){
super.onPause();
if(!_END_GAME){
Builder _alert = new AlertDialog.Builder(this)
.setMessage("onPause, with game NOT over!");
_alert.setNeutralButton("OK.",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
arg0.dismiss(); // Kills the interface
System.runFinalizersOnExit(true);
finish();
}
});
_alert.setTitle("Your Score!");
_alert.show();
}
}
Now the problem is, the dialog does not launch what-so-ever, and then the code errors out. I put the dialog there to try to visualize where the onPause() was called and help me debug some other variables and such. Yet like I said it never even gets shown. Any ideas why this would be? Is there a function that is launched prior to onPause() when the back button is pressed? Thank you in advance for any info.
onPause will always be called when your activity is no longer in the foreground, that's guaranteed. Maybe your _END_GAME is not false? Add a debug log output to your onPause method, you'll see that it always gets called.
I should note though that displaying a dialog during onPause is extremely bad form - the user is trying to get rid of your app (could even be because of an incoming phone call). You DO NOT want a dialog then. That goes against the Android design.
In fact, the Android OS will simply short-circuit your app if you try to do lengthy shenanigans in onDestroy or onPause. Basically, if those get called, you're supposed to disappear quietly.
If you really want to intercept the back button, you can check for the button like Ted suggested, but keep in mind that your app can go to the background in many other ways - home button, selected notification, incoming phone call, etc.
You should check for the back button by overriding onKeyDown, not testing in onPause. onPause gets called whenever your activity is no longer in the background leaves the foreground; it is not necessarily finishing. (You can check isFinishing() for that.) See here for more info on handling the back key.
onPause is getting called, and your dialog is showing, just for a tiny split-second before Android finishes your app. Put log statements in there if you want to watch what is going on.
If you want to show a dialog when the back button is pressed then the easiest way (works on Android 2.1+) is to override the onBackPressed method in your activity
#Override
public void onBackPressed() {
if (gameRunning) {
// show dialog
} else {
// exit
super.onBackPressed();
}
}

Categories

Resources