Merge "Screenshot swipe-dismiss polish"
This commit is contained in:
committed by
Android (Google) Code Review
commit
312fe058f7
@@ -39,5 +39,6 @@
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:pointerIcon="crosshair"/>
|
||||
<include layout="@layout/global_screenshot_static"/>
|
||||
<include layout="@layout/global_screenshot_static"
|
||||
android:id="@+id/global_screenshot_static"/>
|
||||
</com.android.systemui.screenshot.ScreenshotView>
|
||||
|
||||
@@ -84,6 +84,7 @@ import javax.inject.Inject;
|
||||
*/
|
||||
public class ScreenshotController {
|
||||
private static final String TAG = logTag(ScreenshotController.class);
|
||||
|
||||
/**
|
||||
* POD used in the AsyncTask which saves an image in the background.
|
||||
*/
|
||||
@@ -350,10 +351,20 @@ public class ScreenshotController {
|
||||
// Inflate the screenshot layout
|
||||
mScreenshotView = (ScreenshotView)
|
||||
LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
|
||||
mScreenshotView.init(mUiEventLogger, this::resetScreenshotView);
|
||||
mScreenshotView.init(mUiEventLogger, new ScreenshotView.ScreenshotViewCallback() {
|
||||
@Override
|
||||
public void onUserInteraction() {
|
||||
resetTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
resetScreenshotView();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO(159460485): Remove this when focus is handled properly in the system
|
||||
mScreenshotView.setOnTouchListener((v, event) -> {
|
||||
mDecorView.setOnTouchListener((v, event) -> {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
|
||||
if (DEBUG_INPUT) {
|
||||
Log.d(TAG, "onTouch: ACTION_OUTSIDE");
|
||||
@@ -432,7 +443,7 @@ public class ScreenshotController {
|
||||
}
|
||||
if (DEBUG_WINDOW) {
|
||||
Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
|
||||
+ "(dismissing=" + mScreenshotView.isDismissing() + ")");
|
||||
+ "(dismissing=" + mScreenshotView.isDismissing() + ")");
|
||||
}
|
||||
mScreenshotView.reset();
|
||||
}
|
||||
@@ -580,11 +591,8 @@ public class ScreenshotController {
|
||||
mSaveInBgTask.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the action shade and its entrance animation, once we get the screenshot URI.
|
||||
*/
|
||||
private void showUiOnActionsReady(ScreenshotController.SavedImageData imageData) {
|
||||
logSuccessOnActionsReady(imageData);
|
||||
private void resetTimeout() {
|
||||
mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
|
||||
|
||||
AccessibilityManager accessibilityManager = (AccessibilityManager)
|
||||
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
@@ -592,12 +600,25 @@ public class ScreenshotController {
|
||||
SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS,
|
||||
AccessibilityManager.FLAG_CONTENT_CONTROLS);
|
||||
|
||||
if (DEBUG_UI) {
|
||||
Log.d(TAG, "Showing UI actions, dismiss timeout: " + timeoutMs + " ms");
|
||||
}
|
||||
mScreenshotHandler.sendMessageDelayed(
|
||||
mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
|
||||
timeoutMs);
|
||||
if (DEBUG_UI) {
|
||||
Log.d(TAG, "dismiss timeout: " + timeoutMs + " ms");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the action shade and its entrance animation, once we get the screenshot URI.
|
||||
*/
|
||||
private void showUiOnActionsReady(ScreenshotController.SavedImageData imageData) {
|
||||
logSuccessOnActionsReady(imageData);
|
||||
if (DEBUG_UI) {
|
||||
Log.d(TAG, "Showing UI actions");
|
||||
}
|
||||
|
||||
resetTimeout();
|
||||
|
||||
if (imageData.uri != null) {
|
||||
mScreenshotHandler.post(() -> {
|
||||
|
||||
@@ -58,6 +58,7 @@ import android.util.MathUtils;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.TouchDelegate;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.view.ViewTreeObserver;
|
||||
@@ -83,6 +84,12 @@ import java.util.function.Consumer;
|
||||
public class ScreenshotView extends FrameLayout implements
|
||||
ViewTreeObserver.OnComputeInternalInsetsListener {
|
||||
|
||||
interface ScreenshotViewCallback {
|
||||
void onUserInteraction();
|
||||
|
||||
void onDismiss();
|
||||
}
|
||||
|
||||
private static final String TAG = logTag(ScreenshotView.class);
|
||||
|
||||
private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
|
||||
@@ -99,6 +106,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade
|
||||
private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
|
||||
private static final float ROUNDED_CORNER_RADIUS = .05f;
|
||||
private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
|
||||
|
||||
private final Interpolator mAccelerateInterpolator = new AccelerateInterpolator();
|
||||
|
||||
@@ -115,6 +123,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
private boolean mDirectionLTR;
|
||||
|
||||
private ScreenshotSelectorView mScreenshotSelectorView;
|
||||
private View mScreenshotStatic;
|
||||
private ImageView mScreenshotPreview;
|
||||
private ImageView mScreenshotFlash;
|
||||
private ImageView mActionsContainerBackground;
|
||||
@@ -127,7 +136,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
private ScreenshotActionChip mScrollChip;
|
||||
|
||||
private UiEventLogger mUiEventLogger;
|
||||
private Runnable mOnDismissRunnable;
|
||||
private ScreenshotViewCallback mCallbacks;
|
||||
private Animator mDismissAnimation;
|
||||
|
||||
private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
|
||||
@@ -195,8 +204,10 @@ public class ScreenshotView extends FrameLayout implements
|
||||
|
||||
final Rect tmpRect = new Rect();
|
||||
mScreenshotPreview.getBoundsOnScreen(tmpRect);
|
||||
tmpRect.inset((int) dpToPx(-SWIPE_PADDING_DP), (int) dpToPx(-SWIPE_PADDING_DP));
|
||||
touchRegion.op(tmpRect, Region.Op.UNION);
|
||||
mActionsContainer.getBoundsOnScreen(tmpRect);
|
||||
mActionsContainerBackground.getBoundsOnScreen(tmpRect);
|
||||
tmpRect.inset((int) dpToPx(-SWIPE_PADDING_DP), (int) dpToPx(-SWIPE_PADDING_DP));
|
||||
touchRegion.op(tmpRect, Region.Op.UNION);
|
||||
mDismissButton.getBoundsOnScreen(tmpRect);
|
||||
touchRegion.op(tmpRect, Region.Op.UNION);
|
||||
@@ -215,7 +226,9 @@ public class ScreenshotView extends FrameLayout implements
|
||||
|
||||
@Override // View
|
||||
protected void onFinishInflate() {
|
||||
mScreenshotStatic = requireNonNull(findViewById(R.id.global_screenshot_static));
|
||||
mScreenshotPreview = requireNonNull(findViewById(R.id.global_screenshot_preview));
|
||||
|
||||
mActionsContainerBackground = requireNonNull(findViewById(
|
||||
R.id.global_screenshot_actions_container_background));
|
||||
mActionsContainer = requireNonNull(findViewById(R.id.global_screenshot_actions_container));
|
||||
@@ -238,6 +251,16 @@ public class ScreenshotView extends FrameLayout implements
|
||||
}
|
||||
});
|
||||
|
||||
int swipePaddingPx = (int) dpToPx(SWIPE_PADDING_DP);
|
||||
TouchDelegate previewDelegate = new TouchDelegate(
|
||||
new Rect(swipePaddingPx, swipePaddingPx, swipePaddingPx, swipePaddingPx),
|
||||
mScreenshotPreview);
|
||||
mScreenshotPreview.setTouchDelegate(previewDelegate);
|
||||
TouchDelegate actionsDelegate = new TouchDelegate(
|
||||
new Rect(swipePaddingPx, swipePaddingPx, swipePaddingPx, swipePaddingPx),
|
||||
mActionsContainerBackground);
|
||||
mActionsContainerBackground.setTouchDelegate(actionsDelegate);
|
||||
|
||||
setFocusable(true);
|
||||
mScreenshotSelectorView.setFocusable(true);
|
||||
mScreenshotSelectorView.setFocusableInTouchMode(true);
|
||||
@@ -272,9 +295,9 @@ public class ScreenshotView extends FrameLayout implements
|
||||
* Note: must be called before any other (non-constructor) method or null pointer exceptions
|
||||
* may occur.
|
||||
*/
|
||||
void init(UiEventLogger uiEventLogger, Runnable onDismissRunnable) {
|
||||
void init(UiEventLogger uiEventLogger, ScreenshotViewCallback callbacks) {
|
||||
mUiEventLogger = uiEventLogger;
|
||||
mOnDismissRunnable = onDismissRunnable;
|
||||
mCallbacks = callbacks;
|
||||
}
|
||||
|
||||
void takePartialScreenshot(Consumer<Rect> onPartialScreenshotSelected) {
|
||||
@@ -414,8 +437,14 @@ public class ScreenshotView extends FrameLayout implements
|
||||
mScreenshotPreview.setX(finalPos.x - bounds.width() * cornerScale / 2f);
|
||||
mScreenshotPreview.setY(finalPos.y - bounds.height() * cornerScale / 2f);
|
||||
requestLayout();
|
||||
mScreenshotPreview.setOnTouchListener(new SwipeDismissHandler());
|
||||
|
||||
createScreenshotActionsShadeAnimation().start();
|
||||
|
||||
SwipeDismissHandler swipeDismissHandler = new SwipeDismissHandler();
|
||||
mScreenshotPreview.setOnTouchListener(swipeDismissHandler);
|
||||
mActionsContainer.setOnTouchListener(swipeDismissHandler);
|
||||
mActionsContainerBackground.setOnTouchListener(swipeDismissHandler);
|
||||
mBackgroundProtection.setOnTouchListener(swipeDismissHandler);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -544,6 +573,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
|
||||
animateDismissal();
|
||||
});
|
||||
actionChip.setAlpha(1);
|
||||
mActionsView.addView(actionChip);
|
||||
mSmartChips.add(actionChip);
|
||||
}
|
||||
@@ -583,7 +613,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
if (DEBUG_ANIM) {
|
||||
Log.d(TAG, "after dismiss animation, calling onDismissRunnable.run()");
|
||||
}
|
||||
mOnDismissRunnable.run();
|
||||
mCallbacks.onDismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -617,7 +647,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
mDismissButton.setVisibility(View.GONE);
|
||||
mScreenshotPreview.setVisibility(View.GONE);
|
||||
mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
mScreenshotPreview.setTranslationX(0);
|
||||
mScreenshotStatic.setTranslationX(0);
|
||||
mScreenshotPreview.setTranslationY(0);
|
||||
mScreenshotPreview.setContentDescription(
|
||||
mContext.getResources().getString(R.string.screenshot_preview_description));
|
||||
@@ -675,7 +705,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
BitmapDrawable bitmapDrawable = new BitmapDrawable(res, bitmap);
|
||||
if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
|
||||
|| bitmap.getHeight() == 0) {
|
||||
Log.e(TAG, "Can't create inset drawable, using 0 insets bitmap and insets create "
|
||||
Log.e(TAG, "Can't create inset drawable, using 0 insets bitmap and insets create "
|
||||
+ "degenerate region: " + bitmap.getWidth() + "x" + bitmap.getHeight() + " "
|
||||
+ bitmapDrawable);
|
||||
return bitmapDrawable;
|
||||
@@ -697,6 +727,10 @@ public class ScreenshotView extends FrameLayout implements
|
||||
}
|
||||
}
|
||||
|
||||
private float dpToPx(float dp) {
|
||||
return dp * mDisplayMetrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT;
|
||||
}
|
||||
|
||||
class SwipeDismissHandler implements OnTouchListener {
|
||||
|
||||
// if distance moved on ACTION_UP is less than this, register a click
|
||||
@@ -707,11 +741,17 @@ public class ScreenshotView extends FrameLayout implements
|
||||
|
||||
private final GestureDetector mGestureDetector;
|
||||
private final float mDismissStartX;
|
||||
private final Rect mActionsRect = new Rect();
|
||||
|
||||
private float mStartX;
|
||||
private float mStartY;
|
||||
private float mTranslationX = 0;
|
||||
|
||||
// tracks whether mStartX has been set for this interaction
|
||||
private boolean mInteractionStarted = false;
|
||||
// tracks whether we're dragging the UI (as opposed to scrolling the actions bar)
|
||||
private boolean mIsDragging = false;
|
||||
|
||||
SwipeDismissHandler() {
|
||||
GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener();
|
||||
mGestureDetector = new GestureDetector(mContext, gestureListener);
|
||||
@@ -720,10 +760,16 @@ public class ScreenshotView extends FrameLayout implements
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
boolean gestureResult = mGestureDetector.onTouchEvent(event);
|
||||
mCallbacks.onUserInteraction();
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
mInteractionStarted = true;
|
||||
mStartX = event.getRawX();
|
||||
mStartY = event.getRawY();
|
||||
return true;
|
||||
} else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
|
||||
mInteractionStarted = false;
|
||||
mIsDragging = false;
|
||||
if (isPastDismissThreshold()
|
||||
&& (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
|
||||
if (DEBUG_INPUT) {
|
||||
@@ -745,18 +791,45 @@ public class ScreenshotView extends FrameLayout implements
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return mGestureDetector.onTouchEvent(event);
|
||||
return gestureResult;
|
||||
}
|
||||
|
||||
class SwipeDismissGestureListener extends GestureDetector.SimpleOnGestureListener {
|
||||
|
||||
/**
|
||||
* This is somewhat complicated to handle because we want to allow scrolling the actions
|
||||
* bar (if it extends off the screen) as well as dismissing the UI horizontally by
|
||||
* dragging the actions bar. In addition, we don't get the initial ACTION_DOWN because
|
||||
* it is consumed by the action bar view.
|
||||
*
|
||||
* So, we use a gated system: first, keep track of the pointer location as we move;
|
||||
* next, check whether the actions bar can scroll in the direction we're moving in. If
|
||||
* it can, let the actions bar handle the event; otherwise, we've gone as far as we can
|
||||
* and can start dragging the UI instead.
|
||||
*/
|
||||
@Override
|
||||
public boolean onScroll(
|
||||
MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
|
||||
mTranslationX = ev2.getRawX() - ev1.getRawX();
|
||||
mScreenshotPreview.setTranslationX(mTranslationX);
|
||||
mDismissButton.setX(mDismissStartX + mTranslationX);
|
||||
return true;
|
||||
mActionsContainer.getBoundsOnScreen(mActionsRect);
|
||||
if (!mInteractionStarted) {
|
||||
if (mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())) {
|
||||
mStartX = ev2.getRawX();
|
||||
mInteractionStarted = true;
|
||||
}
|
||||
} else {
|
||||
float distance = ev2.getRawX() - mStartX;
|
||||
if ((mIsDragging && distance * mTranslationX > 0)
|
||||
|| !mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())
|
||||
|| !mActionsContainer.canScrollHorizontally(-1 * (int) distance)) {
|
||||
mIsDragging = true;
|
||||
mTranslationX = distance;
|
||||
mScreenshotStatic.setTranslationX(mTranslationX);
|
||||
return true;
|
||||
} else {
|
||||
mStartX = ev2.getRawX();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,23 +845,18 @@ public class ScreenshotView extends FrameLayout implements
|
||||
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
|
||||
float startX = mTranslationX;
|
||||
float finalX = mDirectionLTR
|
||||
? -1 * (mDismissStartX + mDismissButton.getWidth())
|
||||
? -1 * (mActionsContainerBackground.getX()
|
||||
+ mActionsContainerBackground.getWidth())
|
||||
: mDisplayMetrics.widthPixels;
|
||||
|
||||
anim.addUpdateListener(animation -> {
|
||||
float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
|
||||
mScreenshotPreview.setTranslationX(translation);
|
||||
mDismissButton.setX(mDismissStartX + translation);
|
||||
|
||||
float yDelta = MathUtils.lerp(0, mDismissDeltaY, animation.getAnimatedFraction());
|
||||
|
||||
mActionsContainer.setTranslationY(yDelta);
|
||||
mActionsContainerBackground.setTranslationY(yDelta);
|
||||
float translation = MathUtils.lerp(startX, finalX,
|
||||
animation.getAnimatedFraction());
|
||||
mScreenshotStatic.setTranslationX(translation);
|
||||
|
||||
setAlpha(1 - animation.getAnimatedFraction());
|
||||
});
|
||||
anim.setDuration(400);
|
||||
|
||||
return anim;
|
||||
}
|
||||
|
||||
@@ -801,15 +869,10 @@ public class ScreenshotView extends FrameLayout implements
|
||||
anim.addUpdateListener(animation -> {
|
||||
float translation = MathUtils.lerp(
|
||||
startX, finalX, animation.getAnimatedFraction());
|
||||
mScreenshotPreview.setTranslationX(translation);
|
||||
mDismissButton.setX(mDismissStartX + translation);
|
||||
mScreenshotStatic.setTranslationX(translation);
|
||||
});
|
||||
|
||||
return anim;
|
||||
}
|
||||
|
||||
private float dpToPx(float dp) {
|
||||
return dp * mDisplayMetrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user