Skip to content

Commit

Permalink
fix(android)(8_1_X): Window.open() wrongly does fade-in animation by …
Browse files Browse the repository at this point in the history
…default as of 8.0.1 (#10933)

* [TIMOB-27101] Android: Fixed bug where Window.open() does a shared-element fade-in animation by default as of 8.0.1

* Android: Improved transition animation to not overlap status bar and navigation bar for [TIMOB-27101]

* Android: Added shared-element transition unit test for [TIMOB-27101]

* Increased timeouts in unit tests for [TIMOB-27101]
  • Loading branch information
jquick-axway authored and lokeshchdhry committed Jun 5, 2019
1 parent ddf8e7a commit 41af65a
Show file tree
Hide file tree
Showing 4 changed files with 249 additions and 74 deletions.
115 changes: 57 additions & 58 deletions android/modules/ui/src/java/ti/modules/titanium/ui/WindowProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,10 @@ protected void handleOpen(KrollDict options)
int enterAnimation = TiConvert.toInt(options.get(TiC.PROPERTY_ACTIVITY_ENTER_ANIMATION), 0);
int exitAnimation = TiConvert.toInt(options.get(TiC.PROPERTY_ACTIVITY_EXIT_ANIMATION), 0);
topActivity.overridePendingTransition(enterAnimation, exitAnimation);
} else if (hasActivityTransitions()) {
topActivity.startActivity(intent, createActivityOptionsBundle(topActivity));
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
topActivity.startActivity(intent, createActivityOptionsBundle(topActivity));
} else {
topActivity.startActivity(intent);
}
topActivity.startActivity(intent);
}

if (options.containsKey(TiC.PROPERTY_SUSTAINED_PERFORMANCE_MODE)) {
Expand Down Expand Up @@ -579,61 +577,62 @@ private void applyActivityTransitions(Window win, KrollDict props)
@Nullable
private Transition createTransition(KrollDict props, String key)
{
if (LOLLIPOP_OR_GREATER) {
Transition t = null;
final int transitionType = props.getInt(key);
switch (transitionType) {
case TiUIView.TRANSITION_EXPLODE:
t = new Explode();
break;

case TiUIView.TRANSITION_FADE_IN:
t = new Fade(Fade.IN);
break;

case TiUIView.TRANSITION_FADE_OUT:
t = new Fade(Fade.OUT);
break;

case TiUIView.TRANSITION_SLIDE_TOP:
t = new Slide(Gravity.TOP);
break;

case TiUIView.TRANSITION_SLIDE_RIGHT:
t = new Slide(Gravity.RIGHT);
break;

case TiUIView.TRANSITION_SLIDE_BOTTOM:
t = new Slide(Gravity.BOTTOM);
break;

case TiUIView.TRANSITION_SLIDE_LEFT:
t = new Slide(Gravity.LEFT);
break;

case TiUIView.TRANSITION_CHANGE_BOUNDS:
t = new ChangeBounds();
break;

case TiUIView.TRANSITION_CHANGE_CLIP_BOUNDS:
t = new ChangeClipBounds();
break;

case TiUIView.TRANSITION_CHANGE_TRANSFORM:
t = new ChangeTransform();
break;

case TiUIView.TRANSITION_CHANGE_IMAGE_TRANSFORM:
t = new ChangeImageTransform();
break;

default:
break;
}
return t;
} else {
// Validate arguments.
if ((props == null) || (key == null)) {
return null;
}

// This feature is only supported on Android 5.0 and higher.
if (!LOLLIPOP_OR_GREATER) {
return null;
}

// Create the requested transition.
Transition transition = null;
final int transitionType = props.getInt(key);
switch (transitionType) {
case TiUIView.TRANSITION_EXPLODE:
transition = new Explode();
break;
case TiUIView.TRANSITION_FADE_IN:
transition = new Fade(Fade.IN);
break;
case TiUIView.TRANSITION_FADE_OUT:
transition = new Fade(Fade.OUT);
break;
case TiUIView.TRANSITION_SLIDE_TOP:
transition = new Slide(Gravity.TOP);
break;
case TiUIView.TRANSITION_SLIDE_RIGHT:
transition = new Slide(Gravity.RIGHT);
break;
case TiUIView.TRANSITION_SLIDE_BOTTOM:
transition = new Slide(Gravity.BOTTOM);
break;
case TiUIView.TRANSITION_SLIDE_LEFT:
transition = new Slide(Gravity.LEFT);
break;
case TiUIView.TRANSITION_CHANGE_BOUNDS:
transition = new ChangeBounds();
break;
case TiUIView.TRANSITION_CHANGE_CLIP_BOUNDS:
transition = new ChangeClipBounds();
break;
case TiUIView.TRANSITION_CHANGE_TRANSFORM:
transition = new ChangeTransform();
break;
case TiUIView.TRANSITION_CHANGE_IMAGE_TRANSFORM:
transition = new ChangeImageTransform();
break;
default:
return null;
}

// Exclude the top status bar and bottom navigation bar from the transition animation.
// This prevents the activity window's animation from overlapping it, which looks bad.
transition.excludeTarget(android.R.id.statusBarBackground, true);
transition.excludeTarget(android.R.id.navigationBarBackground, true);
return transition;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -656,15 +656,44 @@ protected Bundle createActivityOptionsBundle(Activity activity)
} else {
options = ActivityOptionsCompat.makeBasic();
}
return options.toBundle();
return (options != null) ? options.toBundle() : null;
}

/**
* @return true if this window has activity transitions
*/
protected boolean hasActivityTransitions()
{
final boolean animated = TiConvert.toBoolean(getProperties(), TiC.PROPERTY_ANIMATED, true);
return LOLLIPOP_OR_GREATER && animated;
// This feature is only supported on Android 5.0 and higher.
if (!LOLLIPOP_OR_GREATER) {
return false;
}

// Don't do transition if "animated" property was set false.
boolean isAnimated = TiConvert.toBoolean(getProperty(TiC.PROPERTY_ANIMATED), true);
if (!isAnimated) {
return false;
}

// Do activity transition if at least 1 shared element has been configured.
// Note: It doesn't matter if transition animation properties were assigned.
// System will do default transition animation if not assign to window proxy.
if ((this.sharedElementPairs != null) && (this.sharedElementPairs.size() > 0)) {
return true;
}

// Do activity transition if at least 1 transition property was assigned to proxy.
if (hasPropertyAndNotNull(TiC.PROPERTY_ENTER_TRANSITION) || hasPropertyAndNotNull(TiC.PROPERTY_EXIT_TRANSITION)
|| hasPropertyAndNotNull(TiC.PROPERTY_RETURN_TRANSITION)
|| hasPropertyAndNotNull(TiC.PROPERTY_REENTER_TRANSITION)
|| hasPropertyAndNotNull(TiC.PROPERTY_SHARED_ELEMENT_ENTER_TRANSITION)
|| hasPropertyAndNotNull(TiC.PROPERTY_SHARED_ELEMENT_EXIT_TRANSITION)
|| hasPropertyAndNotNull(TiC.PROPERTY_SHARED_ELEMENT_REENTER_TRANSITION)
|| hasPropertyAndNotNull(TiC.PROPERTY_SHARED_ELEMENT_RETURN_TRANSITION)) {
return true;
}

// Don't do activity transition.
return false;
}
}
34 changes: 21 additions & 13 deletions apidoc/Titanium/UI/Window.yml
Original file line number Diff line number Diff line change
Expand Up @@ -400,11 +400,11 @@ description: |
windowB.addSharedElement(titleInWinA, "title");
windowB.open();
Further you can use `activityEnterTransition`, `activityExitTransition`, `activityReenterTransition` and `activityReturnTransition` to customize
the way activities transition into the scene. Currently activity transition will work only when atleast a shared element is used between the participating
activities.
Note that specifying the transitions is not mandatory. If not specified it defaults to Material theme transition.
Further you can use `activityEnterTransition`, `activityExitTransition`, `activityReenterTransition`
and `activityReturnTransition` to customize the way activities transition into the scene. These are intended
to be used with views set up as "shared elements" via the `addSharedElement()` method where these views
will be moved from window to the other. As of Titanium 8.0.1, you don't have to add views as shared elements
to use these transition animations, while in older version of Titanium that was required.
See the official Android [Activity Transitions](https://developer.android.com/training/material/animations.html#Transitions)
documentation for more information and supported transitons.
Expand Down Expand Up @@ -1632,8 +1632,10 @@ properties:
summary: The type of transition used when activity is exiting.
description: |
Activity A's exit transition determines how views in A are animated when A starts B.
Applicable for Android 5.0 and above. This value will be ignored if `animated` is set to false or
there is no shared element between the participating activities.
Applicable for Android 5.0 and above. This transition property will be ignored if `animated` is set to false.
Will also be ignored unless at least 1 view has been assigned to the `addSharedElement()` method,
except on Titanium 8.0.1 and higher where shared elements are no longer required to do transitions.
See "Material design activity transitions in Android" in the main description of Titanium.UI.Window
for more information.
type: Number
Expand All @@ -1649,8 +1651,10 @@ properties:
summary: The type of transition used when activity is entering.
description: |
Activity B's enter transition determines how views in B are animated when A starts B.
Applicable for Android 5.0 and above. This value will be ignored if `animated` is set to false or
there is no shared element between the participating activities.
Applicable for Android 5.0 and above. This transition property will be ignored if `animated` is set to false.
Will also be ignored unless at least 1 view has been assigned to the `addSharedElement()` method,
except on Titanium 8.0.1 and higher where shared elements are no longer required to do transitions.
See "Material design activity transitions in Android" in the main description of Titanium.UI.Window
for more information.
type: Number
Expand All @@ -1666,8 +1670,10 @@ properties:
summary: The type of transition used when returning from a previously started activity.
description: |
Activity B's return transition determines how views in B are animated when B returns to A.
Applicable for Android 5.0 and above. This value will be ignored if `animated` is set to false or
there is no shared element between the participating activities.
Applicable for Android 5.0 and above. This transition property will be ignored if `animated` is set to false.
Will also be ignored unless at least 1 view has been assigned to the `addSharedElement()` method,
except on Titanium 8.0.1 and higher where shared elements are no longer required to do transitions.
See "Material design activity transitions in Android" in the main description of Titanium.UI.Window
for more information.
type: Number
Expand All @@ -1683,8 +1689,10 @@ properties:
summary: The type of transition used when reentering to a previously started activity.
description: |
Activity A's reenter transition determines how views in A are animated when B returns to A.
Applicable for Android 5.0 and above. This value will be ignored if `animated` is set to false or
there is no shared element between the participating activities.
Applicable for Android 5.0 and above. This transition property will be ignored if `animated` is set to false.
Will also be ignored unless at least 1 view has been assigned to the `addSharedElement()` method,
except on Titanium 8.0.1 and higher where shared elements are no longer required to do transitions.
See "Material design activity transitions in Android" in the main description of Titanium.UI.Window
for more information.
type: Number
Expand Down
139 changes: 139 additions & 0 deletions tests/Resources/ti.ui.window.addontest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Appcelerator Titanium Mobile
* Copyright (c) 2011-Present by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Apache Public License
* Please see the LICENSE included with this distribution for details.
*/
/* eslint-env mocha */
/* global Ti */
/* eslint no-unused-expressions: "off" */
'use strict';

describe('Titanium.UI.Window', function () {
var win;

this.timeout(5000);

afterEach(function () {
if (win) {
win.close();
}
win = null;
});

// Performs an Android shared-element transition animation between 2 windows.
// Labels from parent window will move to child window's label positions during open animation.
it.android('#addSharedElement()', function (finish) {
this.slow(3000);
this.timeout(5000);

win = Ti.UI.createWindow({
backgroundColor: 'blue'
});
const sourceLabel1 = Ti.UI.createLabel({
text: 'Transition Label 1',
top: '10dp',
left: '10dp'
});
win.add(sourceLabel1);
const sourceLabel2 = Ti.UI.createLabel({
text: 'Transition Label 2',
bottom: '10dp',
right: '10dp'
});
win.add(sourceLabel2);
win.addEventListener('postlayout', function eventHandler() {
win.removeEventListener('postlayout', eventHandler);
const childWindow = Ti.UI.createWindow({
backgroundColor: 'purple'
});
const targetLabel1 = Ti.UI.createLabel({
text: 'Transition Label 1',
transitionName: 'label1Transition',
bottom: '10dp',
left: '10dp'
});
childWindow.add(targetLabel1);
childWindow.addSharedElement(sourceLabel1, targetLabel1.transitionName);
const targetLabel2 = Ti.UI.createLabel({
text: 'Transition Label 2',
transitionName: 'label2Transition',
top: '10dp',
right: '10dp'
});
childWindow.add(targetLabel2);
childWindow.addSharedElement(sourceLabel2, targetLabel2.transitionName);
childWindow.addEventListener('open', function () {
// Wait for transition animation to end before closing. (We don't have an event for this.)
setTimeout(function () {
childWindow.close();
}, 750);
});
childWindow.addEventListener('close', function () {
// The exit animation has finished. We're done.
finish();
});
childWindow.open();
});
win.open();
});

describe.android('activity transitions', function () {
this.slow(3000);
this.timeout(5000);

function doTransitionTest(windowSettings, finish) {
windowSettings.title = 'Child Window';
windowSettings.backgroundColor = 'blue';
win = Ti.UI.createWindow(windowSettings);
win.addEventListener('open', function () {
setTimeout(function () {
win.close();
win = null;
}, 750);
});
win.addEventListener('close', function () {
finish();
});
win.open();
}

it.android('TRANSITION_FADE_IN/TRANSITION_FADE_OUT', function (finish) {
const windowSettings = {
activityEnterTransition: Ti.UI.Android.TRANSITION_FADE_IN,
activityReenterTransition: Ti.UI.Android.TRANSITION_FADE_IN,
activitySharedElementEnterTransition: Ti.UI.Android.TRANSITION_NONE,
activitySharedElementReenterTransition: Ti.UI.Android.TRANSITION_NONE,
activityExitTransition: Ti.UI.Android.TRANSITION_FADE_OUT,
activityReturnTransition: Ti.UI.Android.TRANSITION_FADE_OUT,
activitySharedElementExitTransition: Ti.UI.Android.TRANSITION_NONE,
activitySharedElementReturnTransition: Ti.UI.Android.TRANSITION_NONE
};
doTransitionTest(windowSettings, finish);
});

it.android('TRANSITION_SLIDE_RIGHT', function (finish) {
doTransitionTest({ activityEnterTransition: Ti.UI.Android.TRANSITION_SLIDE_RIGHT }, finish);
});

it.android('TRANSITION_SLIDE_LEFT', function (finish) {
doTransitionTest({ activityEnterTransition: Ti.UI.Android.TRANSITION_SLIDE_LEFT }, finish);
});

it.android('TRANSITION_SLIDE_TOP', function (finish) {
doTransitionTest({ activityEnterTransition: Ti.UI.Android.TRANSITION_SLIDE_TOP }, finish);
});

it.android('TRANSITION_SLIDE_BOTTOM', function (finish) {
doTransitionTest({ activityEnterTransition: Ti.UI.Android.TRANSITION_SLIDE_BOTTOM }, finish);
});

it.android('TRANSITION_EXPLODE', function (finish) {
doTransitionTest({ activityEnterTransition: Ti.UI.Android.TRANSITION_EXPLODE }, finish);
});

it.android('TRANSITION_NONE', function (finish) {
doTransitionTest({ activityEnterTransition: Ti.UI.Android.TRANSITION_NONE }, finish);
});
});
});

0 comments on commit 41af65a

Please sign in to comment.