DO NOT MERGE Fix android:noHistory for Wear
Modifies swipe-to-close activities to be opaque by default (instead of translucent by default). Previously, android:noHistory properties on most activities in Wear were being ignored because they were usually transitioning to a swipe-to-close activity that was marked as translucent. This meant that the noHistory activity was still technically visible, and so would never be culled from the task history. Now, we convert a swiped activity to translucent as soon as a swipe begins, and convert it back after the swipe finishes. The previous version of SDL tries to do this, but fails in the case where the context is a ContextWrapper. This approach is hacky and isn't merge-able into master. We leave it DO NOT MERGE and will do a long-term fix after the holidays. Test: Built a test app to verify that noHistory is now being correctly respected. Manually verified that new activities start out opaque and not translucent. Manually verified that Home correctly starts/stops when it's revealed from underneath a partially swiped activity. Tested general swipe behavior on Settings, Contacts, Flashlight, Fit. Bug: 33252029 Change-Id: Ib2e7f21ea1e0d52db03e78d25676501e5f73b31f
This commit is contained in:
@@ -23,6 +23,7 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
@@ -33,7 +34,6 @@ import android.view.VelocityTracker;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
|
||||||
import android.view.animation.DecelerateInterpolator;
|
import android.view.animation.DecelerateInterpolator;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
@@ -44,7 +44,6 @@ public class SwipeDismissLayout extends FrameLayout {
|
|||||||
private static final String TAG = "SwipeDismissLayout";
|
private static final String TAG = "SwipeDismissLayout";
|
||||||
|
|
||||||
private static final float DISMISS_MIN_DRAG_WIDTH_RATIO = .33f;
|
private static final float DISMISS_MIN_DRAG_WIDTH_RATIO = .33f;
|
||||||
private boolean mUseDynamicTranslucency = true;
|
|
||||||
|
|
||||||
public interface OnDismissedListener {
|
public interface OnDismissedListener {
|
||||||
void onDismissed(SwipeDismissLayout layout);
|
void onDismissed(SwipeDismissLayout layout);
|
||||||
@@ -64,6 +63,8 @@ public class SwipeDismissLayout extends FrameLayout {
|
|||||||
void onSwipeCancelled(SwipeDismissLayout layout);
|
void onSwipeCancelled(SwipeDismissLayout layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean mIsWindowNativelyTranslucent;
|
||||||
|
|
||||||
// Cached ViewConfiguration and system-wide constant values
|
// Cached ViewConfiguration and system-wide constant values
|
||||||
private int mSlop;
|
private int mSlop;
|
||||||
private int mMinFlingVelocity;
|
private int mMinFlingVelocity;
|
||||||
@@ -83,19 +84,6 @@ public class SwipeDismissLayout extends FrameLayout {
|
|||||||
|
|
||||||
private OnDismissedListener mDismissedListener;
|
private OnDismissedListener mDismissedListener;
|
||||||
private OnSwipeProgressChangedListener mProgressListener;
|
private OnSwipeProgressChangedListener mProgressListener;
|
||||||
private ViewTreeObserver.OnEnterAnimationCompleteListener mOnEnterAnimationCompleteListener =
|
|
||||||
new ViewTreeObserver.OnEnterAnimationCompleteListener() {
|
|
||||||
@Override
|
|
||||||
public void onEnterAnimationComplete() {
|
|
||||||
// SwipeDismissLayout assumes that the host Activity is translucent
|
|
||||||
// and temporarily disables translucency when it is fully visible.
|
|
||||||
// As soon as the user starts swiping, we will re-enable
|
|
||||||
// translucency.
|
|
||||||
if (mUseDynamicTranslucency && getContext() instanceof Activity) {
|
|
||||||
((Activity) getContext()).convertFromTranslucent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
|
private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
|
||||||
private Runnable mRunnable = new Runnable() {
|
private Runnable mRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -141,8 +129,8 @@ public class SwipeDismissLayout extends FrameLayout {
|
|||||||
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
|
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
|
||||||
TypedArray a = context.getTheme().obtainStyledAttributes(
|
TypedArray a = context.getTheme().obtainStyledAttributes(
|
||||||
com.android.internal.R.styleable.Theme);
|
com.android.internal.R.styleable.Theme);
|
||||||
mUseDynamicTranslucency = !a.hasValue(
|
mIsWindowNativelyTranslucent = a.getBoolean(
|
||||||
com.android.internal.R.styleable.Window_windowIsTranslucent);
|
com.android.internal.R.styleable.Window_windowIsTranslucent, false);
|
||||||
a.recycle();
|
a.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,20 +145,12 @@ public class SwipeDismissLayout extends FrameLayout {
|
|||||||
@Override
|
@Override
|
||||||
protected void onAttachedToWindow() {
|
protected void onAttachedToWindow() {
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
if (getContext() instanceof Activity) {
|
|
||||||
getViewTreeObserver().addOnEnterAnimationCompleteListener(
|
|
||||||
mOnEnterAnimationCompleteListener);
|
|
||||||
}
|
|
||||||
getContext().registerReceiver(mScreenOffReceiver, mScreenOffFilter);
|
getContext().registerReceiver(mScreenOffReceiver, mScreenOffFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDetachedFromWindow() {
|
protected void onDetachedFromWindow() {
|
||||||
getContext().unregisterReceiver(mScreenOffReceiver);
|
getContext().unregisterReceiver(mScreenOffReceiver);
|
||||||
if (getContext() instanceof Activity) {
|
|
||||||
getViewTreeObserver().removeOnEnterAnimationCompleteListener(
|
|
||||||
mOnEnterAnimationCompleteListener);
|
|
||||||
}
|
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,9 +253,6 @@ public class SwipeDismissLayout extends FrameLayout {
|
|||||||
mLastX = ev.getRawX();
|
mLastX = ev.getRawX();
|
||||||
updateSwiping(ev);
|
updateSwiping(ev);
|
||||||
if (mSwiping) {
|
if (mSwiping) {
|
||||||
if (mUseDynamicTranslucency && getContext() instanceof Activity) {
|
|
||||||
((Activity) getContext()).convertToTranslucent(null, null);
|
|
||||||
}
|
|
||||||
setProgress(ev.getRawX() - mDownX);
|
setProgress(ev.getRawX() - mDownX);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -298,8 +275,11 @@ public class SwipeDismissLayout extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void cancel() {
|
protected void cancel() {
|
||||||
if (mUseDynamicTranslucency && getContext() instanceof Activity) {
|
if (!mIsWindowNativelyTranslucent) {
|
||||||
((Activity) getContext()).convertFromTranslucent();
|
Activity activity = findActivity();
|
||||||
|
if (activity != null) {
|
||||||
|
activity.convertFromTranslucent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mProgressListener != null) {
|
if (mProgressListener != null) {
|
||||||
mProgressListener.onSwipeCancelled(this);
|
mProgressListener.onSwipeCancelled(this);
|
||||||
@@ -323,6 +303,7 @@ public class SwipeDismissLayout extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwiping(MotionEvent ev) {
|
private void updateSwiping(MotionEvent ev) {
|
||||||
|
boolean oldSwiping = mSwiping;
|
||||||
if (!mSwiping) {
|
if (!mSwiping) {
|
||||||
float deltaX = ev.getRawX() - mDownX;
|
float deltaX = ev.getRawX() - mDownX;
|
||||||
float deltaY = ev.getRawY() - mDownY;
|
float deltaY = ev.getRawY() - mDownY;
|
||||||
@@ -332,6 +313,16 @@ public class SwipeDismissLayout extends FrameLayout {
|
|||||||
mSwiping = false;
|
mSwiping = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mSwiping && !oldSwiping) {
|
||||||
|
// Swiping has started
|
||||||
|
if (!mIsWindowNativelyTranslucent) {
|
||||||
|
Activity activity = findActivity();
|
||||||
|
if (activity != null) {
|
||||||
|
activity.convertToTranslucent(null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDismiss(MotionEvent ev) {
|
private void updateDismiss(MotionEvent ev) {
|
||||||
@@ -406,6 +397,17 @@ public class SwipeDismissLayout extends FrameLayout {
|
|||||||
return 1 - progress * progress * progress;
|
return 1 - progress * progress * progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Activity findActivity() {
|
||||||
|
Context context = getContext();
|
||||||
|
while (context instanceof ContextWrapper) {
|
||||||
|
if (context instanceof Activity) {
|
||||||
|
return (Activity) context;
|
||||||
|
}
|
||||||
|
context = ((ContextWrapper) context).getBaseContext();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private class DismissAnimator implements AnimatorUpdateListener, Animator.AnimatorListener {
|
private class DismissAnimator implements AnimatorUpdateListener, Animator.AnimatorListener {
|
||||||
private final TimeInterpolator DISMISS_INTERPOLATOR = new DecelerateInterpolator(1.5f);
|
private final TimeInterpolator DISMISS_INTERPOLATOR = new DecelerateInterpolator(1.5f);
|
||||||
private final long DISMISS_DURATION = 250;
|
private final long DISMISS_DURATION = 250;
|
||||||
|
|||||||
@@ -710,12 +710,7 @@ final class ActivityRecord {
|
|||||||
AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
|
AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
|
||||||
realTheme, com.android.internal.R.styleable.Window, userId);
|
realTheme, com.android.internal.R.styleable.Window, userId);
|
||||||
final boolean translucent = ent != null && (ent.array.getBoolean(
|
final boolean translucent = ent != null && (ent.array.getBoolean(
|
||||||
com.android.internal.R.styleable.Window_windowIsTranslucent, false)
|
com.android.internal.R.styleable.Window_windowIsTranslucent, false));
|
||||||
|| (!ent.array.hasValue(
|
|
||||||
com.android.internal.R.styleable.Window_windowIsTranslucent)
|
|
||||||
&& ent.array.getBoolean(
|
|
||||||
com.android.internal.R.styleable.Window_windowSwipeToDismiss,
|
|
||||||
false)));
|
|
||||||
fullscreen = ent != null && !ent.array.getBoolean(
|
fullscreen = ent != null && !ent.array.getBoolean(
|
||||||
com.android.internal.R.styleable.Window_windowIsFloating, false)
|
com.android.internal.R.styleable.Window_windowIsFloating, false)
|
||||||
&& !translucent;
|
&& !translucent;
|
||||||
|
|||||||
Reference in New Issue
Block a user