diff options
3 files changed, 126 insertions, 41 deletions
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml index 2bf41d2472be..3c58d16b9552 100644 --- a/packages/SystemUI/res/layout/global_screenshot.xml +++ b/packages/SystemUI/res/layout/global_screenshot.xml @@ -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> diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 6a4e93be0fe5..3f163d3fc108 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -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(() -> { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index cce60f9c7c9d..4ca3dccf229c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -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; - } } } |