Don't change View visibility during activity transitions.
Bug 16187776
Changing View visibility can change the View's focused item.
To prevent this, a backdoor is introduced into Transition
and Visiblity to set the target Visibility used in Activity Transitions.
Change-Id: Idfd2c6fba2cad80fecdfd086990ddc604f86ca68
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 1a03fe9..15be9b1 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -280,14 +280,6 @@
public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
- public static void setViewVisibility(Collection<View> views, int visibility) {
- if (views != null) {
- for (View view : views) {
- view.setVisibility(visibility);
- }
- }
- }
-
protected Transition setTargets(Transition transition, boolean add) {
if (transition == null || (add &&
(mTransitioningViews == null || mTransitioningViews.isEmpty()))) {
@@ -531,6 +523,13 @@
return getWindow().getTransitionBackgroundFadeDuration();
}
+ protected static void setTransitionAlpha(ArrayList<View> views, float alpha) {
+ int numSharedElements = views.size();
+ for (int i = 0; i < numSharedElements; i++) {
+ views.get(i).setTransitionAlpha(alpha);
+ }
+ }
+
/**
* Captures placement information for Views with a shared element name for
* Activity Transitions.
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 90f36b8..f50c93b 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -104,9 +104,9 @@
mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
send(MSG_SEND_SHARED_ELEMENT_DESTINATION, null);
}
- setViewVisibility(mSharedElements, View.INVISIBLE);
+ setTransitionAlpha(mSharedElements, 0);
if (getViewsTransition() != null) {
- setViewVisibility(mTransitioningViews, View.INVISIBLE);
+ setTransitionAlpha(mTransitioningViews, 0);
}
if (mSharedElementsBundle != null) {
onTakeSharedElements();
@@ -221,7 +221,7 @@
if (!mIsCanceled) {
mIsCanceled = true;
if (getViewsTransition() == null || mIsViewsTransitionStarted) {
- setViewVisibility(mSharedElements, View.VISIBLE);
+ setTransitionAlpha(mSharedElements, 1);
} else {
mTransitioningViews.addAll(mSharedElements);
}
@@ -281,7 +281,7 @@
// Now start shared element transition
ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
mSharedElementNames);
- setViewVisibility(mSharedElements, View.VISIBLE);
+ setTransitionAlpha(mSharedElements, 1);
ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
setSharedElementState(sharedElementState, sharedElementSnapshots);
requestLayoutForSharedElements();
@@ -314,13 +314,6 @@
}
}
- @Override
- protected void stripOffscreenViews() {
- setViewVisibility(mTransitioningViews, View.VISIBLE);
- super.stripOffscreenViews();
- setViewVisibility(mTransitioningViews, View.INVISIBLE);
- }
-
private void onTakeSharedElements() {
if (!mIsReadyForTransition || mSharedElementsBundle == null) {
return;
@@ -362,6 +355,8 @@
viewsTransition = configureTransition(getViewsTransition(), true);
if (viewsTransition != null) {
stripOffscreenViews();
+ viewsTransition.forceVisibility(View.INVISIBLE, true);
+ setTransitionAlpha(mTransitioningViews, 1);
}
}
mIsViewsTransitionStarted = mIsViewsTransitionStarted || startEnterTransition;
@@ -405,7 +400,6 @@
}
private void startEnterTransition(Transition transition) {
- setViewVisibility(mTransitioningViews, View.VISIBLE);
if (!mIsReturning) {
Drawable background = getDecor().getBackground();
if (background != null) {
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 0fbd685..3f3e00c 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -68,6 +68,8 @@
private boolean mIsExitStarted;
+ private boolean mSharedElementsHidden;
+
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
super(activity.getWindow(), names, getListener(activity, isReturning),
@@ -113,8 +115,8 @@
}
public void resetViews() {
- setViewVisibility(mTransitioningViews, View.VISIBLE);
- setViewVisibility(mSharedElements, View.VISIBLE);
+ setTransitionAlpha(mTransitioningViews, 1);
+ setTransitionAlpha(mSharedElements, 1);
mIsHidden = true;
clearState();
}
@@ -161,8 +163,9 @@
private void hideSharedElements() {
if (!mIsHidden) {
- setViewVisibility(mSharedElements, View.INVISIBLE);
+ setTransitionAlpha(mSharedElements, 0);
}
+ mSharedElementsHidden = true;
finishIfNecessary();
}
@@ -175,7 +178,6 @@
beginTransitions();
}
});
- setViewVisibility(mTransitioningViews, View.INVISIBLE);
}
}
@@ -219,7 +221,7 @@
Transition transition = getExitTransition();
if (transition != null) {
TransitionManager.beginDelayedTransition(getDecor(), transition);
- setViewVisibility(mTransitioningViews, View.INVISIBLE);
+ mTransitioningViews.get(0).invalidate();
}
}
@@ -241,6 +243,8 @@
});
mBackgroundAnimator.setDuration(getFadeDuration());
mBackgroundAnimator.start();
+ } else {
+ mIsBackgroundReady = true;
}
}
}
@@ -259,7 +263,7 @@
transition.removeListener(this);
exitTransitionComplete();
if (mIsHidden) {
- setViewVisibility(mTransitioningViews, View.VISIBLE);
+ setTransitionAlpha(mTransitioningViews, 1);
}
}
@@ -268,6 +272,7 @@
super.onTransitionCancel(transition);
}
});
+ viewsTransition.forceVisibility(View.INVISIBLE, false);
}
return viewsTransition;
}
@@ -286,7 +291,7 @@
transition.removeListener(this);
sharedElementTransitionComplete();
if (mIsHidden) {
- setViewVisibility(mSharedElements, View.VISIBLE);
+ setTransitionAlpha(mSharedElements, 1);
}
}
});
@@ -302,6 +307,7 @@
Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
if (transition != null) {
TransitionManager.beginDelayedTransition(getDecor(), transition);
+ getDecor().invalidate();
}
}
@@ -353,7 +359,7 @@
private void finishIfNecessary() {
if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty() ||
- mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
+ mSharedElementsHidden)) {
finish();
}
if (!mIsReturning && mExitNotified) {
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index 2591e24..3f5e8e8 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -310,7 +310,8 @@
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
final BitmapDrawable drawable = new BitmapDrawable(bitmap);
- view.setVisibility(View.INVISIBLE);
+ final float transitionAlpha = view.getTransitionAlpha();
+ view.setTransitionAlpha(0);
sceneRoot.getOverlay().add(drawable);
Path topLeftPath = getPathMotion().getPath(startX - tempLocation[0],
startY - tempLocation[1], endX - tempLocation[0], endY - tempLocation[1]);
@@ -321,7 +322,7 @@
@Override
public void onAnimationEnd(Animator animation) {
sceneRoot.getOverlay().remove(drawable);
- view.setVisibility(View.VISIBLE);
+ view.setTransitionAlpha(transitionAlpha);
}
});
return anim;
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index c14678a..1967213 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -2037,6 +2037,9 @@
return mNameOverrides;
}
+ /** @hide */
+ public void forceVisibility(int visibility, boolean isStartValue) {}
+
@Override
public String toString() {
return toString("");
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index 63957e9..c6005b9 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -293,6 +293,15 @@
}
}
+ /** @hide */
+ @Override
+ public void forceVisibility(int visibility, boolean isStartValue) {
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; i++) {
+ mTransitions.get(i).forceVisibility(visibility, isStartValue);
+ }
+ }
+
/**
* Removes the specified child transition from this set.
*
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 1629726..cf5ea4c 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -78,6 +78,9 @@
private int mMode = IN | OUT;
+ private int mForcedStartVisibility = -1;
+ private int mForcedEndVisibility = -1;
+
public Visibility() {}
public Visibility(Context context, AttributeSet attrs) {
@@ -121,8 +124,13 @@
return sTransitionProperties;
}
- private void captureValues(TransitionValues transitionValues) {
- int visibility = transitionValues.view.getVisibility();
+ private void captureValues(TransitionValues transitionValues, int forcedVisibility) {
+ int visibility;
+ if (forcedVisibility != -1) {
+ visibility = forcedVisibility;
+ } else {
+ visibility = transitionValues.view.getVisibility();
+ }
transitionValues.values.put(PROPNAME_VISIBILITY, visibility);
transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent());
int[] loc = new int[2];
@@ -132,12 +140,22 @@
@Override
public void captureStartValues(TransitionValues transitionValues) {
- captureValues(transitionValues);
+ captureValues(transitionValues, mForcedStartVisibility);
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
- captureValues(transitionValues);
+ captureValues(transitionValues, mForcedEndVisibility);
+ }
+
+ /** @hide */
+ @Override
+ public void forceVisibility(int visibility, boolean isStartValue) {
+ if (isStartValue) {
+ mForcedStartVisibility = visibility;
+ } else {
+ mForcedEndVisibility = visibility;
+ }
}
/**
@@ -402,26 +420,29 @@
}
if (viewToKeep != null) {
- int originalVisibility = viewToKeep.getVisibility();
- viewToKeep.setVisibility(View.VISIBLE);
+ int originalVisibility = -1;
+ final boolean isForcedVisibility = mForcedStartVisibility != -1 ||
+ mForcedEndVisibility != -1;
+ if (!isForcedVisibility) {
+ originalVisibility = viewToKeep.getVisibility();
+ viewToKeep.setVisibility(View.VISIBLE);
+ }
Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
- if (animator == null) {
- viewToKeep.setVisibility(originalVisibility);
- } else {
+ if (animator != null) {
final View finalViewToKeep = viewToKeep;
animator.addListener(new AnimatorListenerAdapter() {
boolean mCanceled = false;
@Override
public void onAnimationPause(Animator animation) {
- if (!mCanceled) {
+ if (!mCanceled && !isForcedVisibility) {
finalViewToKeep.setVisibility(finalVisibility);
}
}
@Override
public void onAnimationResume(Animator animation) {
- if (!mCanceled) {
+ if (!mCanceled && !isForcedVisibility) {
finalViewToKeep.setVisibility(View.VISIBLE);
}
}
@@ -434,10 +455,16 @@
@Override
public void onAnimationEnd(Animator animation) {
if (!mCanceled) {
- finalViewToKeep.setVisibility(finalVisibility);
+ if (isForcedVisibility) {
+ finalViewToKeep.setTransitionAlpha(0);
+ } else {
+ finalViewToKeep.setVisibility(finalVisibility);
+ }
}
}
});
+ } else if (!isForcedVisibility) {
+ viewToKeep.setVisibility(originalVisibility);
}
return animator;
}