[TIMOB-3134] Android: Alert Dialog appears behind activities and is invisible to user
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | Trivial |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2011-05-03T14:09:46.000+0000 |
Affected Version/s | n/a |
Fix Version/s | Release 1.7.0, Sprint 2011-16 |
Components | Android |
Labels | android, enterprise, regression, release-1.7.0, reported-1.6.0, rplist |
Reporter | Dawson Toth |
Assignee | Bill Dawson |
Created | 2011-04-15T03:37:45.000+0000 |
Updated | 2011-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
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:
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
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 theTabGroup
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 theTabGroup
and the modalWindow
create two of those Activities, and their launches are _asynchronous_. WhenalertDialog.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 toalertDialog.show()
in anopen
event listener for that modal window: