Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-3134] Android: Alert Dialog appears behind activities and is invisible to user

GitHub Issuen/a
TypeBug
PriorityTrivial
StatusClosed
ResolutionFixed
Resolution Date2011-05-03T14:09:46.000+0000
Affected Version/sn/a
Fix Version/sRelease 1.7.0, Sprint 2011-16
ComponentsAndroid
Labelsandroid, enterprise, regression, release-1.7.0, reported-1.6.0, rplist
ReporterDawson Toth
AssigneeBill Dawson
Created2011-04-15T03:37:45.000+0000
Updated2011-05-03T14:09:46.000+0000

Description

Problem

When an alert dialog and a modal window are launched at the same time, they end up racing each other to see who displays first. This causes an exception to be thrown.

Regression

In 1.5.0, the alert dialog would show over the modal window, and no exception would be thrown. This has been introduced since then.

Sample Code

var tabGroup = Titanium.UI.createTabGroup();
 
// main window
var tmpWindow = Titanium.UI.createWindow({  
    backgroundColor: 'green',
});
var tmpTab = Titanium.UI.createTab({  
    window:tmpWindow,
    title:'first'
});
 
tabGroup.addTab(tmpTab);
tabGroup.open();
 
Login = {};
 
Login.loginDialog = Ti.UI.createWindow({
   backgroundColor: 'red',
});
 
Login.loginDialog.open({modal:true});
 
var alertDialog = Titanium.UI.createAlertDialog({
    title: 'Welcome',
    message: 'Stuff in here',
    buttonNames: ['red pill', 'blue pill']
});
alertDialog.show();

Trace Log

[TRACE] E/WindowManager( 586): Activity org.appcelerator.jss.JssActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@43ec9740 that was originally added here
[TRACE] E/WindowManager( 586): android.view.WindowLeaked: Activity org.appcelerator.jss.JssActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@43ec9740 that was originally added here
[TRACE] E/WindowManager( 586): at android.view.ViewRoot.<init>(ViewRoot.java:247)
[TRACE] E/WindowManager( 586): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:148)
[TRACE] E/WindowManager( 586): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
[TRACE] E/WindowManager( 586): at android.view.Window$LocalWindowManager.addView(Window.java:424)
[TRACE] E/WindowManager( 586): at android.app.Dialog.show(Dialog.java:241)
[TRACE] E/WindowManager( 586): at ti.modules.titanium.ui.widget.TiUIActivityIndicator.handleShow(TiUIActivityIndicator.java:200)
[TRACE] E/WindowManager( 586): at ti.modules.titanium.ui.widget.TiUIActivityIndicator.show(TiUIActivityIndicator.java:124)
[TRACE] E/WindowManager( 586): at ti.modules.titanium.ui.ActivityIndicatorProxy.handleShow(ActivityIndicatorProxy.java:45)
[TRACE] E/WindowManager( 586): at org.appcelerator.titanium.proxy.TiViewProxy.handleMessage(TiViewProxy.java:212)
[TRACE] E/WindowManager( 586): at android.os.Handler.dispatchMessage(Handler.java:95)
[TRACE] E/WindowManager( 586): at android.os.Looper.loop(Looper.java:123)
[TRACE] E/WindowManager( 586): at android.app.ActivityThread.main(ActivityThread.java:4627)
[TRACE] E/WindowManager( 586): at java.lang.reflect.Method.invokeNative(Native Method)
[TRACE] E/WindowManager( 586): at java.lang.reflect.Method.invoke(Method.java:521)
[TRACE] E/WindowManager( 586): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
[TRACE] E/WindowManager( 586): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
[TRACE] E/WindowManager( 586): at dalvik.system.NativeStart.main(Native Method)

Workaround

The workaround is to remove the race -- only open one, and when it is finished, launch the second. Therefore, the workaround for the sample code would be the following (thanks belong to Marshall Culpepper for this workaround):

Login.loginDialog.addEventListener("open", function(e) {
    var alertDialog = Titanium.UI.createAlertDialog({
        title: 'Welcome',
        message: 'Stuff in here',
        buttonNames: ['red pill', 'blue pill']
    });
    alertDialog.show();
});
Login.loginDialog.open({ modal: true });

Tested On

Titanium SDK version: 1.6.0 (02/10/11 14:34 9db0685...)
BROKEN on Android Simulator 2.2

Associated Helpdesk Ticket

http://developer.appcelerator.com/helpdesk/view/72761">http://developer.appcelerator.com/helpdesk/view/72761

Comments

  1. Bill Dawson 2011-04-15

    Technically it's not a race condition, and it's not really related to modality. That's not an exception either, it's a stack trace Android is giving us, basically saying "Hey you leaked a window, if you want to know which window is leaked, here's the code path that created that window." I.e., no exception is thrown.

    It's not a race condition because, in fact, the alert dialog is being shown -- it's the window being leaked. You just can't see it because it's behind everything. When you back out of the tabgroup, the application closes while that window (the alert) is still showing, thus that complaint by Android.

    So I'm gonna go ahead and change the title here, just to be anal. :)

    Here's a much simpler test case to show this behavior:

       Ti.UI.createWindow({exitOnClose:true,fullscreen:false, backgroundColor:'blue'}).open();
       Ti.UI.createAlertDialog({message:'.'}).show();
       
  2. Bill Dawson 2011-04-15

    Trace with some notes. It's from running the two-liner app.js I show above. I'm putting this here so I don't "lose it". :)

    https://img.skitch.com/20110406-mtb9dq7txs42f835dx88xe5231.png">https://img.skitch.com/20110406-mtb9dq7txs42f835dx88xe5231.png

  3. Bill Dawson 2011-04-25

    I got the failcase to work by adding code that notices if the activity stack is in flux and, if so, delays the call to the alert dialog. However, I *strongly* recommend against using code like that in the fail case. In a sense, there was some luck involved that this ever worked in 1.5.x. The reason is because of the way Android activities work. For an alert dialog to be visible, it is important that it appear "above" the topmost Activity in the Android activity stack. What is the topmost Activity when alertDialog.show() is called in the fail case app.js? Is it the "root" Activity, which is the Activity that runs when the application launches and executes app.js? Or is it the Activity that is created when the TabGroup opens? ( TabGroup s have their own Activity). Or is it the Activity created by the modal window being opened (a modal window causes its own Activity)? In other words, there are three Activities involved in that app.js code sequence. The launching of the TabGroup and the modal Window create two of those Activities, and their launches are _asynchronous_. When alertDialog.show() comes around, which of those three Activities is going to be "current"? It's dangerous to assume that the modal window's activity will be current. It's better to be sure that the modal window (atop of which you want the dialog to show, else it won't be visible) has opened by putting the call to alertDialog.show() in an open event listener for that modal window:
       var alertDialog = Titanium.UI.createAlertDialog({
           title: 'Welcome',
           message: 'Stuff in here',
           buttonNames: ['red pill', 'blue pill']
       });
       Login = {};
        
       Login.loginDialog = Ti.UI.createWindow({
          backgroundColor: 'red',
       });
       
       Login.loginDialog.addEventListener('open', function(){
          alertDialog.show();
       });
       Login.loginDialog.open({modal:true});
       
       

JSON Source