Skip to content

Commit

Permalink
fix(android): Refactored activity/fragment restore handling (#10829)
Browse files Browse the repository at this point in the history
* [TIMOB-26966] Android: Refactored activity/fragment restore handling

- [TIMOB-26966] Fixed bug where back navigating from child window causes app to exit when "Don't keep activities" is enabled as of 7.2.1.
- [TIMOB-26914] Fixed 8.0.0 regression where an OS forced quit app displays a blank window upon relaunch. (Was incorrectly restoring child activity.)
- [TIMOB-17089] Resolved view ID conflicts causes app crashes when activity/fragment is restored.
- [TIMOB-26964] Fixed bug where TabGroup would be blank (no tabs) when calling child window's close() method while "Don't keep activities" is enabled.
- [TIMOB-26890] Fixed bug where tapping 3rd tab in TabGroup can cause a crash due to view ID conflict during fragment restore.
- Increased TabGroup's ViewPager offscreen page limit from 1 to 128 to avoid tab fragment from being destroyed/restored.
- Added several safety mechanisms to AlertDialog and ProgressIndicator dialog code to avoid crashes.

* Android: Updated code formatting for [TIMOB-26966]

* Android: Fixed [TIMOB-26966] to not crash when putting a fragment within a fragment such as "ti.map" view

- Re-added ID assigned to TiUIFragment's view, but it's now guaranteed to be assigned a unique ID.

* [TIMOB-26975] Android: Fixed bug where LiveView does not reload app if "Don't keep activities" is enabled as of 8.0.0

* Android: Added additional error checking to dialog code for [TIMOB-26966]

* Android: Added additional dialog error checking to [TIMOB-26966]

- [TIMOB-15829] Fixed crash caused by progress indicator set to status bar that happens after hiding twice and then opening.

* Android: More improvements made to [TIMOB-26966]

- [TIMOB-26966] Fixed bug where Window/TabGroup close()method call is ignored until "open" event has been fired.
  * Resolved by modifying proxy's handleClose() to call TiActivityWindows.remove() immediately.
- Fixed bug where dynamically changing "exitOnClose" would be ignored.
  * Flag was only being read by activity's "launchIntent" in 8.0.0.
  * Now reads directly from proxy, which is the best solution. Especially if onNewIntent() was called. (Old code would have had a problem too.)
- Re-added support for "exitOnClose" in child windows. (Was only supported by root window.)
- Removed window exit animation if its "exitOnClose" was set true. (Prevents parent window briefly appearing upon exit.)
- Added more dialog safe-guards.
  * Would crash if failed to create dialog builder if there are no activities available.
  * Can happen if "Don't keep activities" is enabled, you press the Home button, and code attempts to show dialog in the background.

* Android: Updated code formatting for [TIMOB-26966]

* Android: Modified [TIMOB-26966] to not exit app upon relaunch/restore after OS force quit it
  • Loading branch information
jquick-axway authored and lokeshchdhry committed Apr 26, 2019
1 parent f3061dc commit c4a01b3
Show file tree
Hide file tree
Showing 22 changed files with 601 additions and 327 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -323,21 +323,6 @@ public void handleCreationDict(KrollDict options)
}
}

@Override
public void onPropertyChanged(String name, Object value)
{
if (opening || opened) {
if (TiC.PROPERTY_EXIT_ON_CLOSE.equals(name)) {
Activity activity = getWindowActivity();
if (activity != null) {
Intent intent = activity.getIntent();
intent.putExtra(TiC.INTENT_PROPERTY_FINISH_ROOT, TiConvert.toBoolean(value));
}
}
}
super.onPropertyChanged(name, value);
}

@Kroll.method
public TabProxy getActiveTab()
{
Expand Down Expand Up @@ -405,7 +390,6 @@ protected void handleOpen(KrollDict options)
fillIntent(topActivity, intent);

int windowId = TiActivityWindows.addWindow(this);
intent.putExtra(TiC.INTENT_PROPERTY_USE_ACTIVITY_WINDOW, true);
intent.putExtra(TiC.INTENT_PROPERTY_WINDOW_ID, windowId);

boolean animated = TiConvert.toBoolean(options, TiC.PROPERTY_ANIMATED, true);
Expand Down Expand Up @@ -445,28 +429,35 @@ public void windowCreated(TiBaseActivity activity, Bundle savedInstanceState)
}
setModelListener(view);

handlePostOpen();

// Push the tab group onto the window stack. It needs to intercept
// stack changes to properly dispatch tab focus and blur events
// when windows open and close on top of it.
activity.addWindowToStack(this);

// Need to handle the cached activity proxy properties in the JS side.
callPropertySync(PROPERTY_POST_TAB_GROUP_CREATED, null);
}

@Override
protected void handlePostOpen()
public void onWindowActivityCreated()
{
super.handlePostOpen();

// Flag that this tab group has been opened.
opened = true;
opening = false;

// First open before we load and focus our first tab.
// Fire open event before we load and focus on first tab.
fireEvent(TiC.EVENT_OPEN, null);

// Finish open handling by loading proxy settings.
handlePostOpen();

super.onWindowActivityCreated();
}

@Override
protected void handlePostOpen()
{
super.handlePostOpen();

if (view == null) {
return;
}

// Load any tabs added before the tab group opened.
TiUIAbstractTabGroup tg = (TiUIAbstractTabGroup) view;
for (TabProxy tab : tabs) {
Expand Down Expand Up @@ -494,14 +485,23 @@ protected void handleClose(KrollDict options)
{
Log.d(TAG, "handleClose: " + options, Log.DEBUG_MODE);

// Remove this TabGroup proxy from the active/open collection.
// Note: If the activity's onCreate() can't find this proxy, then it'll automatically destroy itself.
// This is needed in case the proxy's close() method was called before the activity was created.
TiActivityWindows.removeWindow(this);

// Fire a "close" event.
fireEvent(TiC.EVENT_CLOSE, null);

// Release views/resources.
modelListener = null;
releaseViews();
view = null;

AppCompatActivity activity = getWindowActivity();
if (activity != null && !activity.isFinishing()) {
// Destroy this proxy's activity.
AppCompatActivity activity = (tabGroupActivity != null) ? tabGroupActivity.get() : null;
tabGroupActivity = null;
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
activity.finish();
}
}
Expand All @@ -513,7 +513,7 @@ public void closeFromActivity(boolean activityIsFinishing)
for (TabProxy tab : tabs) {
tab.close(activityIsFinishing);
}
tabs.clear();

// Call super to fire the close event on the tab group.
// This event must fire after each tab has been closed.
super.closeFromActivity(activityIsFinishing);
Expand Down Expand Up @@ -597,11 +597,8 @@ public void releaseViews()
{
super.releaseViews();
if (tabs != null) {
synchronized (tabs)
{
for (TabProxy t : tabs) {
t.releaseViews();
}
for (TabProxy t : tabs) {
t.releaseViews();
}
}
}
Expand All @@ -611,13 +608,8 @@ public void releaseViewsForActivityForcedToDestroy()
{
super.releaseViews();
if (tabs != null) {
synchronized (tabs)
{
for (TabProxy t : tabs) {
// Need to keep the relationship between tabgroup and tabs, window and tab, window and tabgroup,
// in order to recover from forced-destroy activity.
t.releaseViewsForActivityForcedToDestroy();
}
for (TabProxy t : tabs) {
t.releaseViewsForActivityForcedToDestroy();
}
}
}
Expand All @@ -635,11 +627,7 @@ public void fireSafeAreaChangedEvent()

// Create a shallow copy of the tab proxy collection owned by this TabGroup.
// We need to do this since a tab's event handler can remove a tab, which would break iteration.
ArrayList<TabProxy> clonedTabList = null;
synchronized (this.tabs)
{
clonedTabList = (ArrayList<TabProxy>) this.tabs.clone();
}
ArrayList<TabProxy> clonedTabList = (ArrayList<TabProxy>) this.tabs.clone();
if (clonedTabList == null) {
return;
}
Expand Down
62 changes: 36 additions & 26 deletions android/modules/ui/src/java/ti/modules/titanium/ui/WindowProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ protected void handleOpen(KrollDict options)
fillIntent(topActivity, intent);

int windowId = TiActivityWindows.addWindow(this);
intent.putExtra(TiC.INTENT_PROPERTY_USE_ACTIVITY_WINDOW, true);
intent.putExtra(TiC.INTENT_PROPERTY_WINDOW_ID, windowId);

boolean animated = TiConvert.toBoolean(options, TiC.PROPERTY_ANIMATED, true);
Expand Down Expand Up @@ -181,26 +180,41 @@ protected void handleOpen(KrollDict options)
@Override
protected void handleClose(KrollDict options)
{
// Fetch this window's "exitOnClose" property setting.
boolean exitOnClose = (TiActivityWindows.getWindowCount() <= 1);
exitOnClose = TiConvert.toBoolean(getProperty(TiC.PROPERTY_EXIT_ON_CLOSE), exitOnClose);

// Remove this window proxy from the active/open collection.
// Note: If the activity's onCreate() can't find this proxy, then it'll automatically destroy itself.
// This is needed in case the proxy's close() method was called before the activity was created.
TiActivityWindows.removeWindow(this);

// Fetch this proxy's assigned activity, if opened.
boolean animated = TiConvert.toBoolean(options, TiC.PROPERTY_ANIMATED, true);
TiBaseActivity activity = (windowActivity != null) ? windowActivity.get() : null;
if (activity != null && !activity.isFinishing()) {
if (super.hasActivityTransitions()) {
activity.finishAfterTransition();
} else {
activity.finish();
}
if (!animated) {
activity.overridePendingTransition(0, 0);
} else if (options.containsKey(TiC.PROPERTY_ACTIVITY_ENTER_ANIMATION)
|| options.containsKey(TiC.PROPERTY_ACTIVITY_EXIT_ANIMATION)) {
int enterAnimation = TiConvert.toInt(options.get(TiC.PROPERTY_ACTIVITY_ENTER_ANIMATION), 0);
int exitAnimation = TiConvert.toInt(options.get(TiC.PROPERTY_ACTIVITY_EXIT_ANIMATION), 0);
activity.overridePendingTransition(enterAnimation, exitAnimation);
}
windowActivity = null;
if (activity == null) {
return;
}

// Finishing an activity is not synchronous, so we remove the activity from the activity stack here
TiApplication.removeFromActivityStack(activity);
windowActivity = null;
// Do not continue if the activity is already being destroyed.
if (activity.isFinishing() || activity.isDestroyed()) {
return;
}

// Destroy the activity and apply exit animations if configured.
if (!exitOnClose && super.hasActivityTransitions()) {
activity.finishAfterTransition();
} else {
activity.finish();
}
if (!animated) {
activity.overridePendingTransition(0, 0);
} else if (options.containsKey(TiC.PROPERTY_ACTIVITY_ENTER_ANIMATION)
|| options.containsKey(TiC.PROPERTY_ACTIVITY_EXIT_ANIMATION)) {
int enterAnimation = TiConvert.toInt(options.get(TiC.PROPERTY_ACTIVITY_ENTER_ANIMATION), 0);
int exitAnimation = TiConvert.toInt(options.get(TiC.PROPERTY_ACTIVITY_EXIT_ANIMATION), 0);
activity.overridePendingTransition(enterAnimation, exitAnimation);
}
}

Expand Down Expand Up @@ -301,7 +315,6 @@ public void windowCreated(TiBaseActivity activity, Bundle savedInstanceState)
activity.getSupportActionBar().setBackgroundDrawable(new ColorDrawable(colorInt));
}
activity.getActivityProxy().getDecorView().add(this);
activity.addWindowToStack(this);

// Need to handle the cached activity proxy properties and url window in the JS side.
callPropertySync(PROPERTY_POST_WINDOW_CREATED, null);
Expand Down Expand Up @@ -372,12 +385,6 @@ public void onPropertyChanged(String name, Object value)
|| TiC.PROPERTY_LEFT.equals(name) || TiC.PROPERTY_RIGHT.equals(name)) {
// The "top", "bottom", "left" and "right" properties do not work for heavyweight windows.
return;
} else if (TiC.PROPERTY_EXIT_ON_CLOSE.equals(name)) {
Activity activity = (windowActivity != null) ? (Activity) (windowActivity.get()) : null;
if (activity != null) {
Intent intent = activity.getIntent();
intent.putExtra(TiC.INTENT_PROPERTY_FINISH_ROOT, TiConvert.toBoolean(value));
}
} else if (TiC.PROPERTY_HIDES_BACK_BUTTON.equals(name)) {
if (windowActivity != null && windowActivity.get() != null
&& windowActivity.get().getSupportActionBar() != null) {
Expand Down Expand Up @@ -411,7 +418,10 @@ public void setSustainedPerformanceMode(boolean mode)
// clang-format on
{
setProperty(TiC.PROPERTY_SUSTAINED_PERFORMANCE_MODE, mode);
windowActivity.get().setSustainMode(mode);
Activity activity = getWindowActivity();
if (activity instanceof TiBaseActivity) {
((TiBaseActivity) activity).setSustainMode(mode);
}
}

// clang-format off
Expand Down

0 comments on commit c4a01b3

Please sign in to comment.