Remove FEATURE_SWIPE_TO_DISMISS functionality
Not used anymore by any product. Bug: 143346248 Test: Boots Change-Id: I21286e62f73ec34f7d5ca040cd614838c9cd4276
This commit is contained in:
@@ -1609,7 +1609,7 @@ package android {
|
||||
field public static final int windowShowWallpaper = 16843410; // 0x1010292
|
||||
field public static final int windowSoftInputMode = 16843307; // 0x101022b
|
||||
field public static final int windowSplashscreenContent = 16844132; // 0x1010564
|
||||
field public static final int windowSwipeToDismiss = 16843763; // 0x10103f3
|
||||
field @Deprecated public static final int windowSwipeToDismiss = 16843763; // 0x10103f3
|
||||
field public static final int windowTitleBackgroundStyle = 16842844; // 0x101005c
|
||||
field public static final int windowTitleSize = 16842842; // 0x101005a
|
||||
field public static final int windowTitleStyle = 16842843; // 0x101005b
|
||||
@@ -51976,7 +51976,7 @@ package android.view {
|
||||
field public static final int FEATURE_OPTIONS_PANEL = 0; // 0x0
|
||||
field @Deprecated public static final int FEATURE_PROGRESS = 2; // 0x2
|
||||
field public static final int FEATURE_RIGHT_ICON = 4; // 0x4
|
||||
field public static final int FEATURE_SWIPE_TO_DISMISS = 11; // 0xb
|
||||
field @Deprecated public static final int FEATURE_SWIPE_TO_DISMISS = 11; // 0xb
|
||||
field public static final int ID_ANDROID_CONTENT = 16908290; // 0x1020002
|
||||
field public static final String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = "android:navigation:background";
|
||||
field @Deprecated public static final int PROGRESS_END = 10000; // 0x2710
|
||||
|
||||
@@ -219,7 +219,6 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
@Nullable Message cancelCallback) {
|
||||
this(context);
|
||||
mCancelable = cancelable;
|
||||
updateWindowForCancelable();
|
||||
mCancelMessage = cancelCallback;
|
||||
}
|
||||
|
||||
@@ -227,7 +226,6 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
@Nullable OnCancelListener cancelListener) {
|
||||
this(context);
|
||||
mCancelable = cancelable;
|
||||
updateWindowForCancelable();
|
||||
setOnCancelListener(cancelListener);
|
||||
}
|
||||
|
||||
@@ -1249,7 +1247,6 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
*/
|
||||
public void setCancelable(boolean flag) {
|
||||
mCancelable = flag;
|
||||
updateWindowForCancelable();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1263,7 +1260,6 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
public void setCanceledOnTouchOutside(boolean cancel) {
|
||||
if (cancel && !mCancelable) {
|
||||
mCancelable = true;
|
||||
updateWindowForCancelable();
|
||||
}
|
||||
|
||||
mWindow.setCloseOnTouchOutside(cancel);
|
||||
@@ -1415,8 +1411,4 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateWindowForCancelable() {
|
||||
mWindow.setCloseOnSwipeEnabled(mCancelable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1242,15 +1242,11 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
|
||||
final boolean isTranslucent =
|
||||
attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent,
|
||||
false);
|
||||
final boolean isSwipeToDismiss = !attributes.hasValue(
|
||||
com.android.internal.R.styleable.Window_windowIsTranslucent)
|
||||
&& attributes.getBoolean(
|
||||
com.android.internal.R.styleable.Window_windowSwipeToDismiss, false);
|
||||
final boolean isFloating =
|
||||
attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating,
|
||||
false);
|
||||
|
||||
return isFloating || isTranslucent || isSwipeToDismiss;
|
||||
return isFloating || isTranslucent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -127,7 +127,10 @@ public abstract class Window {
|
||||
public static final int FEATURE_ACTION_MODE_OVERLAY = 10;
|
||||
/**
|
||||
* Flag for requesting a decoration-free window that is dismissed by swiping from the left.
|
||||
*
|
||||
* @deprecated Swipe-to-dismiss isn't functional anymore.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int FEATURE_SWIPE_TO_DISMISS = 11;
|
||||
/**
|
||||
* Flag for requesting that window content changes should be animated using a
|
||||
@@ -2511,23 +2514,6 @@ public abstract class Window {
|
||||
*/
|
||||
public abstract void reportActivityRelaunched();
|
||||
|
||||
/**
|
||||
* Called to set flag to check if the close on swipe is enabled. This will only function if
|
||||
* FEATURE_SWIPE_TO_DISMISS has been set.
|
||||
* @hide
|
||||
*/
|
||||
public void setCloseOnSwipeEnabled(boolean closeOnSwipeEnabled) {
|
||||
mCloseOnSwipeEnabled = closeOnSwipeEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if the close on swipe is enabled.
|
||||
* @hide
|
||||
*/
|
||||
public boolean isCloseOnSwipeEnabled() {
|
||||
return mCloseOnSwipeEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link WindowInsetsController} associated with this window
|
||||
* @see View#getWindowInsetsController()
|
||||
|
||||
@@ -24,7 +24,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
|
||||
@@ -112,7 +111,6 @@ import com.android.internal.view.menu.MenuHelper;
|
||||
import com.android.internal.view.menu.MenuPresenter;
|
||||
import com.android.internal.view.menu.MenuView;
|
||||
import com.android.internal.widget.DecorContentParent;
|
||||
import com.android.internal.widget.SwipeDismissLayout;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
@@ -378,15 +376,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
removeFeature(FEATURE_ACTION_BAR);
|
||||
}
|
||||
|
||||
if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_SWIPE_TO_DISMISS) {
|
||||
throw new AndroidRuntimeException(
|
||||
"You cannot combine swipe dismissal and the action bar.");
|
||||
}
|
||||
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0 && featureId == FEATURE_ACTION_BAR) {
|
||||
throw new AndroidRuntimeException(
|
||||
"You cannot combine swipe dismissal and the action bar.");
|
||||
}
|
||||
|
||||
if (featureId == FEATURE_INDETERMINATE_PROGRESS &&
|
||||
getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
|
||||
throw new AndroidRuntimeException("You cannot use indeterminate progress on a watch.");
|
||||
@@ -2373,10 +2362,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
requestFeature(FEATURE_ACTION_MODE_OVERLAY);
|
||||
}
|
||||
|
||||
if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
|
||||
requestFeature(FEATURE_SWIPE_TO_DISMISS);
|
||||
}
|
||||
|
||||
if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
|
||||
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
|
||||
}
|
||||
@@ -2565,10 +2550,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
int layoutResource;
|
||||
int features = getLocalFeatures();
|
||||
// System.out.println("Features: 0x" + Integer.toHexString(features));
|
||||
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
|
||||
layoutResource = R.layout.screen_swipe_dismiss;
|
||||
setCloseOnSwipeEnabled(true);
|
||||
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
|
||||
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
|
||||
if (mIsFloating) {
|
||||
TypedValue res = new TypedValue();
|
||||
getContext().getTheme().resolveAttribute(
|
||||
@@ -2638,10 +2620,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
|
||||
registerSwipeCallbacks(contentParent);
|
||||
}
|
||||
|
||||
// Remaining setup -- of background and title -- that only applies
|
||||
// to top-level windows.
|
||||
if (getContainer() == null) {
|
||||
@@ -3047,63 +3025,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
return (mRightIconView = (ImageView)findViewById(R.id.right_icon));
|
||||
}
|
||||
|
||||
private void registerSwipeCallbacks(ViewGroup contentParent) {
|
||||
if (!(contentParent instanceof SwipeDismissLayout)) {
|
||||
Log.w(TAG, "contentParent is not a SwipeDismissLayout: " + contentParent);
|
||||
return;
|
||||
}
|
||||
SwipeDismissLayout swipeDismiss = (SwipeDismissLayout) contentParent;
|
||||
swipeDismiss.setOnDismissedListener(new SwipeDismissLayout.OnDismissedListener() {
|
||||
@Override
|
||||
public void onDismissed(SwipeDismissLayout layout) {
|
||||
dispatchOnWindowSwipeDismissed();
|
||||
dispatchOnWindowDismissed(false /*finishTask*/, true /*suppressWindowTransition*/);
|
||||
}
|
||||
});
|
||||
swipeDismiss.setOnSwipeProgressChangedListener(
|
||||
new SwipeDismissLayout.OnSwipeProgressChangedListener() {
|
||||
@Override
|
||||
public void onSwipeProgressChanged(
|
||||
SwipeDismissLayout layout, float alpha, float translate) {
|
||||
WindowManager.LayoutParams newParams = getAttributes();
|
||||
newParams.x = (int) translate;
|
||||
newParams.alpha = alpha;
|
||||
setAttributes(newParams);
|
||||
|
||||
int flags = 0;
|
||||
if (newParams.x == 0) {
|
||||
flags = FLAG_FULLSCREEN;
|
||||
} else {
|
||||
flags = FLAG_LAYOUT_NO_LIMITS;
|
||||
}
|
||||
setFlags(flags, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeCancelled(SwipeDismissLayout layout) {
|
||||
WindowManager.LayoutParams newParams = getAttributes();
|
||||
// Swipe changes only affect the x-translation and alpha, check to see if
|
||||
// those values have changed first before resetting them.
|
||||
if (newParams.x != 0 || newParams.alpha != 1) {
|
||||
newParams.x = 0;
|
||||
newParams.alpha = 1;
|
||||
setAttributes(newParams);
|
||||
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public void setCloseOnSwipeEnabled(boolean closeOnSwipeEnabled) {
|
||||
if (hasFeature(Window.FEATURE_SWIPE_TO_DISMISS) // swipe-to-dismiss feature is requested
|
||||
&& mContentParent instanceof SwipeDismissLayout) { // check casting mContentParent
|
||||
((SwipeDismissLayout) mContentParent).setDismissable(closeOnSwipeEnabled);
|
||||
}
|
||||
super.setCloseOnSwipeEnabled(closeOnSwipeEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for calling the {@link Callback#onPanelClosed(int, Menu)}
|
||||
* callback. This method will grab whatever extra state is needed for the
|
||||
|
||||
@@ -1,516 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.widget;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ReceiverCallNotAllowedException;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* Special layout that finishes its activity when swiped away.
|
||||
*/
|
||||
public class SwipeDismissLayout extends FrameLayout {
|
||||
private static final String TAG = "SwipeDismissLayout";
|
||||
|
||||
private static final float MAX_DIST_THRESHOLD = .33f;
|
||||
private static final float MIN_DIST_THRESHOLD = .1f;
|
||||
|
||||
public interface OnDismissedListener {
|
||||
void onDismissed(SwipeDismissLayout layout);
|
||||
}
|
||||
|
||||
public interface OnSwipeProgressChangedListener {
|
||||
/**
|
||||
* Called when the layout has been swiped and the position of the window should change.
|
||||
*
|
||||
* @param alpha A number in [0, 1] representing what the alpha transparency of the window
|
||||
* should be.
|
||||
* @param translate A number in [0, w], where w is the width of the
|
||||
* layout. This is equivalent to progress * layout.getWidth().
|
||||
*/
|
||||
void onSwipeProgressChanged(SwipeDismissLayout layout, float alpha, float translate);
|
||||
|
||||
void onSwipeCancelled(SwipeDismissLayout layout);
|
||||
}
|
||||
|
||||
private boolean mIsWindowNativelyTranslucent;
|
||||
|
||||
// Cached ViewConfiguration and system-wide constant values
|
||||
private int mSlop;
|
||||
private int mMinFlingVelocity;
|
||||
|
||||
// Transient properties
|
||||
private int mActiveTouchId;
|
||||
private float mDownX;
|
||||
private float mDownY;
|
||||
private float mLastX;
|
||||
private boolean mSwiping;
|
||||
private boolean mDismissed;
|
||||
private boolean mDiscardIntercept;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private boolean mBlockGesture = false;
|
||||
private boolean mActivityTranslucencyConverted = false;
|
||||
|
||||
private final DismissAnimator mDismissAnimator = new DismissAnimator();
|
||||
|
||||
private OnDismissedListener mDismissedListener;
|
||||
private OnSwipeProgressChangedListener mProgressListener;
|
||||
private BroadcastReceiver mScreenOffReceiver;
|
||||
private IntentFilter mScreenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
|
||||
|
||||
|
||||
private boolean mDismissable = true;
|
||||
|
||||
public SwipeDismissLayout(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public SwipeDismissLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
ViewConfiguration vc = ViewConfiguration.get(context);
|
||||
mSlop = vc.getScaledTouchSlop();
|
||||
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
|
||||
TypedArray a = context.getTheme().obtainStyledAttributes(
|
||||
com.android.internal.R.styleable.Theme);
|
||||
mIsWindowNativelyTranslucent = a.getBoolean(
|
||||
com.android.internal.R.styleable.Window_windowIsTranslucent, false);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
public void setOnDismissedListener(OnDismissedListener listener) {
|
||||
mDismissedListener = listener;
|
||||
}
|
||||
|
||||
public void setOnSwipeProgressChangedListener(OnSwipeProgressChangedListener listener) {
|
||||
mProgressListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
try {
|
||||
mScreenOffReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
post(() -> {
|
||||
if (mDismissed) {
|
||||
dismiss();
|
||||
} else {
|
||||
cancel();
|
||||
}
|
||||
resetMembers();
|
||||
});
|
||||
}
|
||||
};
|
||||
getContext().registerReceiver(mScreenOffReceiver, mScreenOffFilter);
|
||||
} catch (ReceiverCallNotAllowedException e) {
|
||||
/* Exception is thrown if the context is a ReceiverRestrictedContext object. As
|
||||
* ReceiverRestrictedContext is not public, the context type cannot be checked before
|
||||
* calling registerReceiver. The most likely scenario in which the exception would be
|
||||
* thrown would be when a BroadcastReceiver creates a dialog to show the user. */
|
||||
mScreenOffReceiver = null; // clear receiver since it was not used.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
if (mScreenOffReceiver != null) {
|
||||
getContext().unregisterReceiver(mScreenOffReceiver);
|
||||
mScreenOffReceiver = null;
|
||||
}
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
checkGesture((ev));
|
||||
if (mBlockGesture) {
|
||||
return true;
|
||||
}
|
||||
if (!mDismissable) {
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
// Offset because the view is translated during swipe, match X with raw X. Active touch
|
||||
// coordinates are mostly used by the velocity tracker, so offset it to match the raw
|
||||
// coordinates which is what is primarily used elsewhere.
|
||||
ev.offsetLocation(ev.getRawX() - ev.getX(), 0);
|
||||
|
||||
switch (ev.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
resetMembers();
|
||||
mDownX = ev.getRawX();
|
||||
mDownY = ev.getRawY();
|
||||
mActiveTouchId = ev.getPointerId(0);
|
||||
mVelocityTracker = VelocityTracker.obtain("int1");
|
||||
mVelocityTracker.addMovement(ev);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
int actionIndex = ev.getActionIndex();
|
||||
mActiveTouchId = ev.getPointerId(actionIndex);
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
actionIndex = ev.getActionIndex();
|
||||
int pointerId = ev.getPointerId(actionIndex);
|
||||
if (pointerId == mActiveTouchId) {
|
||||
// This was our active pointer going up. Choose a new active pointer.
|
||||
int newActionIndex = actionIndex == 0 ? 1 : 0;
|
||||
mActiveTouchId = ev.getPointerId(newActionIndex);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_UP:
|
||||
resetMembers();
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (mVelocityTracker == null || mDiscardIntercept) {
|
||||
break;
|
||||
}
|
||||
|
||||
int pointerIndex = ev.findPointerIndex(mActiveTouchId);
|
||||
if (pointerIndex == -1) {
|
||||
Log.e(TAG, "Invalid pointer index: ignoring.");
|
||||
mDiscardIntercept = true;
|
||||
break;
|
||||
}
|
||||
float dx = ev.getRawX() - mDownX;
|
||||
float x = ev.getX(pointerIndex);
|
||||
float y = ev.getY(pointerIndex);
|
||||
if (dx != 0 && canScroll(this, false, dx, x, y)) {
|
||||
mDiscardIntercept = true;
|
||||
break;
|
||||
}
|
||||
updateSwiping(ev);
|
||||
break;
|
||||
}
|
||||
|
||||
return !mDiscardIntercept && mSwiping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
checkGesture((ev));
|
||||
if (mBlockGesture) {
|
||||
return true;
|
||||
}
|
||||
if (mVelocityTracker == null || !mDismissable) {
|
||||
return super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
// Offset because the view is translated during swipe, match X with raw X. Active touch
|
||||
// coordinates are mostly used by the velocity tracker, so offset it to match the raw
|
||||
// coordinates which is what is primarily used elsewhere.
|
||||
ev.offsetLocation(ev.getRawX() - ev.getX(), 0);
|
||||
|
||||
switch (ev.getActionMasked()) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
updateDismiss(ev);
|
||||
if (mDismissed) {
|
||||
mDismissAnimator.animateDismissal(ev.getRawX() - mDownX);
|
||||
} else if (mSwiping
|
||||
// Only trigger animation if we had a MOVE event that would shift the
|
||||
// underlying view, otherwise the animation would be janky.
|
||||
&& mLastX != Integer.MIN_VALUE) {
|
||||
mDismissAnimator.animateRecovery(ev.getRawX() - mDownX);
|
||||
}
|
||||
resetMembers();
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
cancel();
|
||||
resetMembers();
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mVelocityTracker.addMovement(ev);
|
||||
mLastX = ev.getRawX();
|
||||
updateSwiping(ev);
|
||||
if (mSwiping) {
|
||||
setProgress(ev.getRawX() - mDownX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setProgress(float deltaX) {
|
||||
if (mProgressListener != null && deltaX >= 0) {
|
||||
mProgressListener.onSwipeProgressChanged(
|
||||
this, progressToAlpha(deltaX / getWidth()), deltaX);
|
||||
}
|
||||
}
|
||||
|
||||
private void dismiss() {
|
||||
if (mDismissedListener != null) {
|
||||
mDismissedListener.onDismissed(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void cancel() {
|
||||
if (!mIsWindowNativelyTranslucent) {
|
||||
Activity activity = findActivity();
|
||||
if (activity != null && mActivityTranslucencyConverted) {
|
||||
activity.convertFromTranslucent();
|
||||
mActivityTranslucencyConverted = false;
|
||||
}
|
||||
}
|
||||
if (mProgressListener != null) {
|
||||
mProgressListener.onSwipeCancelled(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets internal members when canceling.
|
||||
*/
|
||||
private void resetMembers() {
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
}
|
||||
mVelocityTracker = null;
|
||||
mDownX = 0;
|
||||
mLastX = Integer.MIN_VALUE;
|
||||
mDownY = 0;
|
||||
mSwiping = false;
|
||||
mDismissed = false;
|
||||
mDiscardIntercept = false;
|
||||
}
|
||||
|
||||
private void updateSwiping(MotionEvent ev) {
|
||||
boolean oldSwiping = mSwiping;
|
||||
if (!mSwiping) {
|
||||
float deltaX = ev.getRawX() - mDownX;
|
||||
float deltaY = ev.getRawY() - mDownY;
|
||||
if ((deltaX * deltaX) + (deltaY * deltaY) > mSlop * mSlop) {
|
||||
mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < Math.abs(deltaX);
|
||||
} else {
|
||||
mSwiping = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSwiping && !oldSwiping) {
|
||||
// Swiping has started
|
||||
if (!mIsWindowNativelyTranslucent) {
|
||||
Activity activity = findActivity();
|
||||
if (activity != null) {
|
||||
mActivityTranslucencyConverted = activity.convertToTranslucent(null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDismiss(MotionEvent ev) {
|
||||
float deltaX = ev.getRawX() - mDownX;
|
||||
// Don't add the motion event as an UP event would clear the velocity tracker
|
||||
mVelocityTracker.computeCurrentVelocity(1000);
|
||||
float xVelocity = mVelocityTracker.getXVelocity();
|
||||
if (mLastX == Integer.MIN_VALUE) {
|
||||
// If there's no changes to mLastX, we have only one point of data, and therefore no
|
||||
// velocity. Estimate velocity from just the up and down event in that case.
|
||||
xVelocity = deltaX / ((ev.getEventTime() - ev.getDownTime()) / 1000);
|
||||
}
|
||||
if (!mDismissed) {
|
||||
// Adjust the distance threshold linearly between the min and max threshold based on the
|
||||
// x-velocity scaled with the the fling threshold speed
|
||||
float distanceThreshold = getWidth() * Math.max(
|
||||
Math.min((MIN_DIST_THRESHOLD - MAX_DIST_THRESHOLD)
|
||||
* xVelocity / mMinFlingVelocity // scale x-velocity with fling velocity
|
||||
+ MAX_DIST_THRESHOLD, // offset to start at max threshold
|
||||
MAX_DIST_THRESHOLD), // cap at max threshold
|
||||
MIN_DIST_THRESHOLD); // bottom out at min threshold
|
||||
if ((deltaX > distanceThreshold && ev.getRawX() >= mLastX)
|
||||
|| xVelocity >= mMinFlingVelocity) {
|
||||
mDismissed = true;
|
||||
}
|
||||
}
|
||||
// Check if the user tried to undo this.
|
||||
if (mDismissed && mSwiping) {
|
||||
// Check if the user's finger is actually flinging back to left
|
||||
if (xVelocity < -mMinFlingVelocity) {
|
||||
mDismissed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests scrollability within child views of v in the direction of dx.
|
||||
*
|
||||
* @param v View to test for horizontal scrollability
|
||||
* @param checkV Whether the view v passed should itself be checked for scrollability (true),
|
||||
* or just its children (false).
|
||||
* @param dx Delta scrolled in pixels. Only the sign of this is used.
|
||||
* @param x X coordinate of the active touch point
|
||||
* @param y Y coordinate of the active touch point
|
||||
* @return true if child views of v can be scrolled by delta of dx.
|
||||
*/
|
||||
protected boolean canScroll(View v, boolean checkV, float dx, float x, float y) {
|
||||
if (v instanceof ViewGroup) {
|
||||
final ViewGroup group = (ViewGroup) v;
|
||||
final int scrollX = v.getScrollX();
|
||||
final int scrollY = v.getScrollY();
|
||||
final int count = group.getChildCount();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
final View child = group.getChildAt(i);
|
||||
if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
|
||||
y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
|
||||
canScroll(child, true, dx, x + scrollX - child.getLeft(),
|
||||
y + scrollY - child.getTop())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return checkV && v.canScrollHorizontally((int) -dx);
|
||||
}
|
||||
|
||||
public void setDismissable(boolean dismissable) {
|
||||
if (!dismissable && mDismissable) {
|
||||
cancel();
|
||||
resetMembers();
|
||||
}
|
||||
|
||||
mDismissable = dismissable;
|
||||
}
|
||||
|
||||
private void checkGesture(MotionEvent ev) {
|
||||
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
mBlockGesture = mDismissAnimator.isAnimating();
|
||||
}
|
||||
}
|
||||
|
||||
private float progressToAlpha(float 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 final TimeInterpolator DISMISS_INTERPOLATOR = new DecelerateInterpolator(1.5f);
|
||||
private final long DISMISS_DURATION = 250;
|
||||
|
||||
private final ValueAnimator mDismissAnimator = new ValueAnimator();
|
||||
private boolean mWasCanceled = false;
|
||||
private boolean mDismissOnComplete = false;
|
||||
|
||||
/* package */ DismissAnimator() {
|
||||
mDismissAnimator.addUpdateListener(this);
|
||||
mDismissAnimator.addListener(this);
|
||||
}
|
||||
|
||||
/* package */ void animateDismissal(float currentTranslation) {
|
||||
animate(
|
||||
currentTranslation / getWidth(),
|
||||
1,
|
||||
DISMISS_DURATION,
|
||||
DISMISS_INTERPOLATOR,
|
||||
true /* dismiss */);
|
||||
}
|
||||
|
||||
/* package */ void animateRecovery(float currentTranslation) {
|
||||
animate(
|
||||
currentTranslation / getWidth(),
|
||||
0,
|
||||
DISMISS_DURATION,
|
||||
DISMISS_INTERPOLATOR,
|
||||
false /* don't dismiss */);
|
||||
}
|
||||
|
||||
/* package */ boolean isAnimating() {
|
||||
return mDismissAnimator.isStarted();
|
||||
}
|
||||
|
||||
private void animate(float from, float to, long duration, TimeInterpolator interpolator,
|
||||
boolean dismissOnComplete) {
|
||||
mDismissAnimator.cancel();
|
||||
mDismissOnComplete = dismissOnComplete;
|
||||
mDismissAnimator.setFloatValues(from, to);
|
||||
mDismissAnimator.setDuration(duration);
|
||||
mDismissAnimator.setInterpolator(interpolator);
|
||||
mDismissAnimator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
float value = (Float) animation.getAnimatedValue();
|
||||
setProgress(value * getWidth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mWasCanceled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mWasCanceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!mWasCanceled) {
|
||||
if (mDismissOnComplete) {
|
||||
dismiss();
|
||||
} else {
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!--
|
||||
This is a layout for a window whose resident activity is finished when swiped away.
|
||||
-->
|
||||
|
||||
<com.android.internal.widget.SwipeDismissLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/content"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
@@ -363,9 +363,7 @@
|
||||
<attr name="windowOverscan" format="boolean" />
|
||||
<!-- Flag indicating whether this is a floating window. -->
|
||||
<attr name="windowIsFloating" format="boolean" />
|
||||
<!-- Flag indicating whether this is a translucent window. If this attribute is unset (but
|
||||
not if set to false), the window might still be considered translucent, if
|
||||
windowSwipeToDismiss is set to true. -->
|
||||
<!-- Flag indicating whether this is a translucent window. -->
|
||||
<attr name="windowIsTranslucent" format="boolean" />
|
||||
<!-- Flag indicating that this window's background should be the
|
||||
user's current wallpaper. Corresponds
|
||||
@@ -492,7 +490,9 @@
|
||||
<!-- Flag to indicate that a window can be swiped away to be dismissed.
|
||||
Corresponds to {@link android.view.Window#FEATURE_SWIPE_TO_DISMISS}. It will also
|
||||
dynamically change translucency of the window, if the windowIsTranslucent is not set.
|
||||
If windowIsTranslucent is set (to either true or false) it will obey that setting. -->
|
||||
If windowIsTranslucent is set (to either true or false) it will obey that setting.
|
||||
@deprecated Swipe-to-dismiss isn't functional anymore.
|
||||
-->
|
||||
<attr name="windowSwipeToDismiss" format="boolean" />
|
||||
|
||||
<!-- Flag indicating whether this window requests that content changes be performed
|
||||
@@ -1961,7 +1961,6 @@
|
||||
<attr name="windowCloseOnTouchOutside" />
|
||||
<attr name="windowTranslucentStatus" />
|
||||
<attr name="windowTranslucentNavigation" />
|
||||
<attr name="windowSwipeToDismiss" />
|
||||
<attr name="windowContentTransitions" />
|
||||
<attr name="windowActivityTransitions" />
|
||||
<attr name="windowContentTransitionManager" />
|
||||
|
||||
@@ -32,9 +32,6 @@
|
||||
<!-- True if windowOverscan should be on by default. -->
|
||||
<bool name="config_windowOverscanByDefault">false</bool>
|
||||
|
||||
<!-- True if windowSwipeToDismiss should be on by default. -->
|
||||
<bool name="config_windowSwipeToDismiss">false</bool>
|
||||
|
||||
<!-- True if preference fragment should clip to padding. -->
|
||||
<bool name="config_preferenceFragmentClipToPadding">true</bool>
|
||||
|
||||
|
||||
@@ -1841,7 +1841,6 @@
|
||||
<java-symbol type="layout" name="screen_progress" />
|
||||
<java-symbol type="layout" name="screen_simple" />
|
||||
<java-symbol type="layout" name="screen_simple_overlay_action_mode" />
|
||||
<java-symbol type="layout" name="screen_swipe_dismiss" />
|
||||
<java-symbol type="layout" name="screen_title" />
|
||||
<java-symbol type="layout" name="screen_title_icons" />
|
||||
<java-symbol type="string" name="system_ui_date_pattern" />
|
||||
|
||||
@@ -182,7 +182,6 @@ please see themes_device_defaults.xml.
|
||||
<item name="windowSharedElementExitTransition">@transition/move</item>
|
||||
<item name="windowContentTransitions">false</item>
|
||||
<item name="windowActivityTransitions">true</item>
|
||||
<item name="windowSwipeToDismiss">@bool/config_windowSwipeToDismiss</item>
|
||||
|
||||
<!-- Dialog attributes -->
|
||||
<item name="dialogTheme">@style/ThemeOverlay.Material.Dialog</item>
|
||||
@@ -557,7 +556,6 @@ please see themes_device_defaults.xml.
|
||||
<item name="windowSharedElementExitTransition">@transition/move</item>
|
||||
<item name="windowContentTransitions">false</item>
|
||||
<item name="windowActivityTransitions">true</item>
|
||||
<item name="windowSwipeToDismiss">@bool/config_windowSwipeToDismiss</item>
|
||||
|
||||
<!-- Dialog attributes -->
|
||||
<item name="dialogTheme">@style/ThemeOverlay.Material.Dialog</item>
|
||||
|
||||
Reference in New Issue
Block a user