diff options
7 files changed, 30 insertions, 1495 deletions
diff --git a/packages/SystemUI/res/layout/screenshot.xml b/packages/SystemUI/res/layout/screenshot.xml deleted file mode 100644 index c134c8e2d339..000000000000 --- a/packages/SystemUI/res/layout/screenshot.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2011 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. - --> -<com.android.systemui.screenshot.ScreenshotView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/screenshot_frame" - android:theme="@style/FloatingOverlay" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:importantForAccessibility="no"> - <ImageView - android:id="@+id/screenshot_scrolling_scrim" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="gone" - android:clickable="true" - android:importantForAccessibility="no"/> - <ImageView - android:id="@+id/screenshot_flash" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="gone" - android:elevation="7dp" - android:src="@android:color/white"/> - <include layout="@layout/screenshot_static" - android:id="@+id/screenshot_static"/> -</com.android.systemui.screenshot.ScreenshotView> diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt deleted file mode 100644 index 3d024a6a8ccf..000000000000 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2024 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.systemui.screenshot - -import android.animation.Animator -import android.app.Notification -import android.content.Context -import android.graphics.Bitmap -import android.graphics.Rect -import android.util.Log -import android.view.KeyEvent -import android.view.LayoutInflater -import android.view.ScrollCaptureResponse -import android.view.View -import android.view.ViewTreeObserver -import android.view.WindowInsets -import android.window.OnBackInvokedCallback -import android.window.OnBackInvokedDispatcher -import androidx.appcompat.content.res.AppCompatResources -import com.android.internal.logging.UiEventLogger -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.res.R -import com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS -import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER -import com.android.systemui.screenshot.scroll.ScrollCaptureController -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject - -/** - * Legacy implementation of screenshot view methods. Just proxies the calls down into the original - * ScreenshotView. - */ -class LegacyScreenshotViewProxy -@AssistedInject -constructor( - private val logger: UiEventLogger, - flags: FeatureFlags, - @Assisted private val context: Context, - @Assisted private val displayId: Int -) : ScreenshotViewProxy { - override val view: ScreenshotView = - LayoutInflater.from(context).inflate(R.layout.screenshot, null) as ScreenshotView - override val screenshotPreview: View - override var packageName: String = "" - set(value) { - field = value - view.setPackageName(value) - } - override var callbacks: ScreenshotView.ScreenshotViewCallback? = null - set(value) { - field = value - view.setCallbacks(value) - } - override var screenshot: ScreenshotData? = null - set(value) { - field = value - value?.let { - val badgeBg = - AppCompatResources.getDrawable(context, R.drawable.overlay_badge_background) - val user = it.userHandle - if (badgeBg != null && user != null) { - view.badgeScreenshot(context.packageManager.getUserBadgedIcon(badgeBg, user)) - } - view.setScreenshot(it) - } - } - - override val isAttachedToWindow - get() = view.isAttachedToWindow - override val isDismissing - get() = view.isDismissing - override val isPendingSharedTransition - get() = view.isPendingSharedTransition - - init { - view.setUiEventLogger(logger) - view.setDefaultDisplay(displayId) - view.setFlags(flags) - addPredictiveBackListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) } - setOnKeyListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) } - if (LogConfig.DEBUG_WINDOW) { - Log.d(TAG, "adding OnComputeInternalInsetsListener") - } - view.viewTreeObserver.addOnComputeInternalInsetsListener(view) - screenshotPreview = view.screenshotPreview - } - - override fun reset() = view.reset() - override fun updateInsets(insets: WindowInsets) = view.updateInsets(insets) - override fun updateOrientation(insets: WindowInsets) = view.updateOrientation(insets) - - override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator = - view.createScreenshotDropInAnimation(screenRect, showFlash) - - override fun addQuickShareChip(quickShareAction: Notification.Action) = - view.addQuickShareChip(quickShareAction) - - override fun setChipIntents(imageData: ScreenshotController.SavedImageData) = - view.setChipIntents(imageData) - - override fun requestDismissal(event: ScreenshotEvent?) { - if (DEBUG_DISMISS) { - Log.d(TAG, "screenshot dismissal requested") - } - // If we're already animating out, don't restart the animation - if (view.isDismissing) { - if (DEBUG_DISMISS) { - Log.v(TAG, "Already dismissing, ignoring duplicate command $event") - } - return - } - event?.let { logger.log(event, 0, packageName) } - view.animateDismissal() - } - - override fun showScrollChip(packageName: String, onClick: Runnable) = - view.showScrollChip(packageName, onClick) - - override fun hideScrollChip() = view.hideScrollChip() - - override fun prepareScrollingTransition( - response: ScrollCaptureResponse, - screenBitmap: Bitmap, - newScreenshot: Bitmap, - screenshotTakenInPortrait: Boolean, - onTransitionPrepared: Runnable, - ) { - view.prepareScrollingTransition( - response, - screenBitmap, - newScreenshot, - screenshotTakenInPortrait - ) - view.post { onTransitionPrepared.run() } - } - - override fun startLongScreenshotTransition( - transitionDestination: Rect, - onTransitionEnd: Runnable, - longScreenshot: ScrollCaptureController.LongScreenshot - ) = view.startLongScreenshotTransition(transitionDestination, onTransitionEnd, longScreenshot) - - override fun restoreNonScrollingUi() = view.restoreNonScrollingUi() - - override fun fadeForSharedTransition() {} // unused - - override fun stopInputListening() = view.stopInputListening() - - override fun requestFocus() { - view.requestFocus() - } - - override fun announceForAccessibility(string: String) = view.announceForAccessibility(string) - - override fun prepareEntranceAnimation(runnable: Runnable) { - view.viewTreeObserver.addOnPreDrawListener( - object : ViewTreeObserver.OnPreDrawListener { - override fun onPreDraw(): Boolean { - if (LogConfig.DEBUG_WINDOW) { - Log.d(TAG, "onPreDraw: startAnimation") - } - view.viewTreeObserver.removeOnPreDrawListener(this) - runnable.run() - return true - } - } - ) - } - - private fun addPredictiveBackListener(onDismissRequested: (ScreenshotEvent) -> Unit) { - val onBackInvokedCallback = OnBackInvokedCallback { - if (LogConfig.DEBUG_INPUT) { - Log.d(TAG, "Predictive Back callback dispatched") - } - onDismissRequested.invoke(SCREENSHOT_DISMISSED_OTHER) - } - view.addOnAttachStateChangeListener( - object : View.OnAttachStateChangeListener { - override fun onViewAttachedToWindow(v: View) { - if (LogConfig.DEBUG_INPUT) { - Log.d(TAG, "Registering Predictive Back callback") - } - view - .findOnBackInvokedDispatcher() - ?.registerOnBackInvokedCallback( - OnBackInvokedDispatcher.PRIORITY_DEFAULT, - onBackInvokedCallback - ) - } - - override fun onViewDetachedFromWindow(view: View) { - if (LogConfig.DEBUG_INPUT) { - Log.d(TAG, "Unregistering Predictive Back callback") - } - view - .findOnBackInvokedDispatcher() - ?.unregisterOnBackInvokedCallback(onBackInvokedCallback) - } - } - ) - } - private fun setOnKeyListener(onDismissRequested: (ScreenshotEvent) -> Unit) { - view.setOnKeyListener( - object : View.OnKeyListener { - override fun onKey(view: View, keyCode: Int, event: KeyEvent): Boolean { - if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) { - if (LogConfig.DEBUG_INPUT) { - Log.d(TAG, "onKeyEvent: $keyCode") - } - onDismissRequested.invoke(SCREENSHOT_DISMISSED_OTHER) - return true - } - return false - } - } - ) - } - - @AssistedFactory - interface Factory : ScreenshotViewProxy.Factory { - override fun getProxy(context: Context, displayId: Int): LegacyScreenshotViewProxy - } - - companion object { - private const val TAG = "LegacyScreenshotViewProxy" - } -} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt index 596046268984..474afa8bcb9d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt @@ -137,7 +137,7 @@ constructor( val offset = container.height + params.topMargin + params.bottomMargin val anim = if (animateIn) ValueAnimator.ofFloat(0f, 1f) else ValueAnimator.ofFloat(1f, 0f) with(anim) { - duration = ScreenshotView.SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS + duration = MESSAGE_EXPANSION_DURATION_MS interpolator = AccelerateDecelerateInterpolator() addUpdateListener { valueAnimator: ValueAnimator -> val interpolation = valueAnimator.animatedValue as Float @@ -147,4 +147,8 @@ constructor( } return anim } + + companion object { + const val MESSAGE_EXPANSION_DURATION_MS: Long = 400 + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 0cbf8f9e4c21..811d80bc6449 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -477,9 +477,6 @@ public class ScreenshotController implements ScreenshotHandler { } mViewProxy.setPackageName(mPackageName); - - mViewProxy.updateOrientation( - mWindowManager.getCurrentWindowMetrics().getWindowInsets()); } /** @@ -528,7 +525,7 @@ public class ScreenshotController implements ScreenshotHandler { } mMessageContainerController.setView(mViewProxy.getView()); - mViewProxy.setCallbacks(new ScreenshotView.ScreenshotViewCallback() { + mViewProxy.setCallbacks(new ScreenshotShelfViewProxy.ScreenshotViewCallback() { @Override public void onUserInteraction() { if (DEBUG_INPUT) { @@ -538,13 +535,6 @@ public class ScreenshotController implements ScreenshotHandler { } @Override - public void onAction(Intent intent, UserHandle owner, boolean overrideTransition) { - Pair<ActivityOptions, ExitTransitionCoordinator> exit = createWindowTransition(); - mActionIntentExecutor.launchIntentAsync( - intent, owner, overrideTransition, exit.first, exit.second); - } - - @Override public void onDismiss() { finishDismiss(); } @@ -871,62 +861,6 @@ public class ScreenshotController implements ScreenshotHandler { 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); - mScreenshotHandler.resetTimeout(); - - if (imageData.uri != null) { - if (DEBUG_UI) { - Log.d(TAG, "Showing UI actions"); - } - if (!imageData.owner.equals(Process.myUserHandle())) { - Log.d(TAG, "Screenshot saved to user " + imageData.owner + " as " - + imageData.uri); - } - mScreenshotHandler.post(() -> { - if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { - mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - mViewProxy.setChipIntents(imageData); - } - }); - } else { - mViewProxy.setChipIntents(imageData); - } - }); - } - } - - /** - * Sets up the action shade and its entrance animation, once we get the Quick Share action data. - */ - private void showUiOnQuickShareActionReady(ScreenshotController.QuickShareData quickShareData) { - if (DEBUG_UI) { - Log.d(TAG, "Showing UI for Quick Share action"); - } - if (quickShareData.quickShareAction != null) { - mScreenshotHandler.post(() -> { - if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { - mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - mViewProxy.addQuickShareChip(quickShareData.quickShareAction); - } - }); - } else { - mViewProxy.addQuickShareChip(quickShareData.quickShareAction); - } - }); - } - } - /** * Logs success/failure of the screenshot saving task, and shows an error if it failed. */ diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt index 1b5fa345ebb4..4526f448f57d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt @@ -18,7 +18,6 @@ package com.android.systemui.screenshot import android.animation.Animator import android.animation.AnimatorListenerAdapter -import android.app.Notification import android.content.Context import android.graphics.Bitmap import android.graphics.Rect @@ -45,7 +44,6 @@ import com.android.systemui.res.R import com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS import com.android.systemui.screenshot.LogConfig.DEBUG_INPUT import com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW -import com.android.systemui.screenshot.ScreenshotController.SavedImageData import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER import com.android.systemui.screenshot.scroll.ScrollCaptureController import com.android.systemui.screenshot.ui.ScreenshotAnimationController @@ -71,11 +69,21 @@ constructor( @Assisted private val context: Context, @Assisted private val displayId: Int ) : ScreenshotViewProxy { + + interface ScreenshotViewCallback { + fun onUserInteraction() + + fun onDismiss() + + /** DOWN motion event was observed outside of the touchable areas of this view. */ + fun onTouchOutside() + } + override val view: ScreenshotShelfView = LayoutInflater.from(context).inflate(R.layout.screenshot_shelf, null) as ScreenshotShelfView override val screenshotPreview: View override var packageName: String = "" - override var callbacks: ScreenshotView.ScreenshotViewCallback? = null + override var callbacks: ScreenshotViewCallback? = null override var screenshot: ScreenshotData? = null set(value) { value?.let { @@ -94,6 +102,7 @@ constructor( override val isAttachedToWindow get() = view.isAttachedToWindow + override var isDismissing = false override var isPendingSharedTransition = false @@ -141,10 +150,10 @@ constructor( isPendingSharedTransition = false viewModel.reset() } + override fun updateInsets(insets: WindowInsets) { view.updateInsets(insets) } - override fun updateOrientation(insets: WindowInsets) {} override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator { val entrance = @@ -164,10 +173,6 @@ constructor( return entrance } - override fun addQuickShareChip(quickShareAction: Notification.Action) {} - - override fun setChipIntents(imageData: SavedImageData) {} - override fun requestDismissal(event: ScreenshotEvent?) { requestDismissal(event, null) } @@ -187,6 +192,7 @@ constructor( override fun onAnimationStart(animator: Animator) { isDismissing = true } + override fun onAnimationEnd(animator: Animator) { isDismissing = false callbacks?.onDismiss() @@ -196,10 +202,6 @@ constructor( animator.start() } - override fun showScrollChip(packageName: String, onClick: Runnable) {} - - override fun hideScrollChip() {} - override fun prepareScrollingTransition( response: ScrollCaptureResponse, screenBitmap: Bitmap, // unused diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java deleted file mode 100644 index 59e38a836258..000000000000 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ /dev/null @@ -1,1126 +0,0 @@ -/* - * Copyright (C) 2020 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.systemui.screenshot; - -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; - -import static com.android.internal.jank.InteractionJankMonitor.CUJ_TAKE_SCREENSHOT; -import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM; -import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; -import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT; -import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL; -import static com.android.systemui.screenshot.LogConfig.DEBUG_UI; -import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW; -import static com.android.systemui.screenshot.LogConfig.logTag; -import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS; - -import static java.util.Objects.requireNonNull; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.BroadcastOptions; -import android.app.Notification; -import android.content.Context; -import android.content.Intent; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BlendMode; -import android.graphics.Color; -import android.graphics.Insets; -import android.graphics.Matrix; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.Region; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; -import android.graphics.drawable.InsetDrawable; -import android.graphics.drawable.LayerDrawable; -import android.os.Bundle; -import android.os.Looper; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.MathUtils; -import android.view.Choreographer; -import android.view.Display; -import android.view.DisplayCutout; -import android.view.GestureDetector; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.ScrollCaptureResponse; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.WindowInsets; -import android.view.WindowManager; -import android.view.WindowMetrics; -import android.view.accessibility.AccessibilityManager; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; -import android.widget.FrameLayout; -import android.widget.HorizontalScrollView; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import androidx.constraintlayout.widget.ConstraintLayout; - -import com.android.internal.jank.InteractionJankMonitor; -import com.android.internal.logging.UiEventLogger; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.res.R; -import com.android.systemui.screenshot.scroll.ScrollCaptureController; -import com.android.systemui.shared.system.InputChannelCompat; -import com.android.systemui.shared.system.InputMonitorCompat; -import com.android.systemui.shared.system.QuickStepContract; - -import java.util.ArrayList; - -/** - * Handles the visual elements and animations for the screenshot flow. - */ -public class ScreenshotView extends FrameLayout implements - ViewTreeObserver.OnComputeInternalInsetsListener { - - public interface ScreenshotViewCallback { - void onUserInteraction(); - - void onAction(Intent intent, UserHandle owner, boolean overrideTransition); - - void onDismiss(); - - /** DOWN motion event was observed outside of the touchable areas of this view. */ - void onTouchOutside(); - } - - private static final String TAG = logTag(ScreenshotView.class); - - private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133; - private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217; - // delay before starting to fade in dismiss button - private static final long SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS = 200; - private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234; - private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500; - private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234; - public static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400; - private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100; - private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f; - - private final Resources mResources; - private final Interpolator mFastOutSlowIn; - private final DisplayMetrics mDisplayMetrics; - private final float mFixedSize; - private final AccessibilityManager mAccessibilityManager; - private final GestureDetector mSwipeDetector; - - private int mDefaultDisplay = Display.DEFAULT_DISPLAY; - private int mNavMode; - private boolean mOrientationPortrait; - private boolean mDirectionLTR; - - private ImageView mScrollingScrim; - private DraggableConstraintLayout mScreenshotStatic; - private ImageView mScreenshotPreview; - private ImageView mScreenshotBadge; - private View mScreenshotPreviewBorder; - private ImageView mScrollablePreview; - private ImageView mScreenshotFlash; - private ImageView mActionsContainerBackground; - private HorizontalScrollView mActionsContainer; - private LinearLayout mActionsView; - private FrameLayout mDismissButton; - private OverlayActionChip mShareChip; - private OverlayActionChip mEditChip; - private OverlayActionChip mScrollChip; - private OverlayActionChip mQuickShareChip; - - private UiEventLogger mUiEventLogger; - private ScreenshotViewCallback mCallbacks; - private boolean mPendingSharedTransition; - private InputMonitorCompat mInputMonitor; - private InputChannelCompat.InputEventReceiver mInputEventReceiver; - private boolean mShowScrollablePreview; - private String mPackageName = ""; - - private final ArrayList<OverlayActionChip> mSmartChips = new ArrayList<>(); - private PendingInteraction mPendingInteraction; - // Should only be set/used if the SCREENSHOT_METADATA flag is set. - private ScreenshotData mScreenshotData; - - private final InteractionJankMonitor mInteractionJankMonitor; - private FeatureFlags mFlags; - private final Bundle mInteractiveBroadcastOption; - - private enum PendingInteraction { - PREVIEW, - EDIT, - SHARE, - QUICK_SHARE - } - - public ScreenshotView(Context context) { - this(context, null); - } - - public ScreenshotView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ScreenshotView(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public ScreenshotView( - Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - mResources = mContext.getResources(); - mInteractionJankMonitor = getInteractionJankMonitorInstance(); - - BroadcastOptions options = BroadcastOptions.makeBasic(); - options.setInteractive(true); - mInteractiveBroadcastOption = options.toBundle(); - - mFixedSize = mResources.getDimensionPixelSize(R.dimen.overlay_x_scale); - - // standard material ease - mFastOutSlowIn = - AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in); - - mDisplayMetrics = new DisplayMetrics(); - mContext.getDisplay().getRealMetrics(mDisplayMetrics); - - mAccessibilityManager = AccessibilityManager.getInstance(mContext); - - mSwipeDetector = new GestureDetector(mContext, - new GestureDetector.SimpleOnGestureListener() { - final Rect mActionsRect = new Rect(); - - @Override - public boolean onScroll( - MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) { - mActionsContainer.getBoundsOnScreen(mActionsRect); - // return true if we aren't in the actions bar, or if we are but it isn't - // scrollable in the direction of movement - return !mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY()) - || !mActionsContainer.canScrollHorizontally((int) distanceX); - } - }); - mSwipeDetector.setIsLongpressEnabled(false); - addOnAttachStateChangeListener(new OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - startInputListening(); - } - - @Override - public void onViewDetachedFromWindow(View v) { - stopInputListening(); - } - }); - } - - private InteractionJankMonitor getInteractionJankMonitorInstance() { - return InteractionJankMonitor.getInstance(); - } - - public void hideScrollChip() { - mScrollChip.setVisibility(View.GONE); - } - - /** - * Called to display the scroll action chip when support is detected. - * - * @param packageName the owning package of the window to be captured - * @param onClick the action to take when the chip is clicked. - */ - public void showScrollChip(String packageName, Runnable onClick) { - if (DEBUG_SCROLL) { - Log.d(TAG, "Showing Scroll option"); - } - mScrollChip.setVisibility(VISIBLE); - mScrollChip.setOnClickListener((v) -> onClick.run()); - } - - @Override // ViewTreeObserver.OnComputeInternalInsetsListener - public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) { - inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - inoutInfo.touchableRegion.set(getTouchRegion(true)); - } - - private Region getSwipeRegion() { - Region swipeRegion = new Region(); - - final Rect tmpRect = new Rect(); - int swipePadding = (int) FloatingWindowUtil.dpToPx( - mDisplayMetrics, DraggableConstraintLayout.SWIPE_PADDING_DP * -1); - mScreenshotPreview.getBoundsOnScreen(tmpRect); - tmpRect.inset(swipePadding, swipePadding); - swipeRegion.op(tmpRect, Region.Op.UNION); - mActionsContainerBackground.getBoundsOnScreen(tmpRect); - tmpRect.inset(swipePadding, swipePadding); - swipeRegion.op(tmpRect, Region.Op.UNION); - mDismissButton.getBoundsOnScreen(tmpRect); - swipeRegion.op(tmpRect, Region.Op.UNION); - - View messageContainer = findViewById(R.id.screenshot_message_container); - if (messageContainer != null) { - messageContainer.getBoundsOnScreen(tmpRect); - swipeRegion.op(tmpRect, Region.Op.UNION); - } - View messageDismiss = findViewById(R.id.message_dismiss_button); - if (messageDismiss != null) { - messageDismiss.getBoundsOnScreen(tmpRect); - swipeRegion.op(tmpRect, Region.Op.UNION); - } - - return swipeRegion; - } - - private Region getTouchRegion(boolean includeScrim) { - Region touchRegion = getSwipeRegion(); - - if (includeScrim && mScrollingScrim.getVisibility() == View.VISIBLE) { - final Rect tmpRect = new Rect(); - mScrollingScrim.getBoundsOnScreen(tmpRect); - touchRegion.op(tmpRect, Region.Op.UNION); - } - - if (QuickStepContract.isGesturalMode(mNavMode)) { - final WindowManager wm = mContext.getSystemService(WindowManager.class); - final WindowMetrics windowMetrics = wm.getCurrentWindowMetrics(); - final Insets gestureInsets = windowMetrics.getWindowInsets().getInsets( - WindowInsets.Type.systemGestures()); - // Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE - Rect inset = new Rect(0, 0, gestureInsets.left, mDisplayMetrics.heightPixels); - touchRegion.op(inset, Region.Op.UNION); - inset.set(mDisplayMetrics.widthPixels - gestureInsets.right, 0, - mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels); - touchRegion.op(inset, Region.Op.UNION); - } - return touchRegion; - } - - private void startInputListening() { - stopInputListening(); - mInputMonitor = new InputMonitorCompat("Screenshot", mDefaultDisplay); - mInputEventReceiver = mInputMonitor.getInputReceiver( - Looper.getMainLooper(), Choreographer.getInstance(), ev -> { - if (ev instanceof MotionEvent) { - MotionEvent event = (MotionEvent) ev; - if (event.getActionMasked() == MotionEvent.ACTION_DOWN - && !getTouchRegion(false).contains( - (int) event.getRawX(), (int) event.getRawY())) { - mCallbacks.onTouchOutside(); - } - } - }); - } - - void stopInputListening() { - if (mInputMonitor != null) { - mInputMonitor.dispose(); - mInputMonitor = null; - } - if (mInputEventReceiver != null) { - mInputEventReceiver.dispose(); - mInputEventReceiver = null; - } - } - - @Override // View - protected void onFinishInflate() { - super.onFinishInflate(); - mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim)); - mScreenshotStatic = requireNonNull(findViewById(R.id.screenshot_static)); - mScreenshotPreview = requireNonNull(findViewById(R.id.screenshot_preview)); - - mScreenshotPreviewBorder = requireNonNull( - findViewById(R.id.screenshot_preview_border)); - mScreenshotPreview.setClipToOutline(true); - mScreenshotBadge = requireNonNull(findViewById(R.id.screenshot_badge)); - - mActionsContainerBackground = requireNonNull(findViewById( - R.id.actions_container_background)); - mActionsContainer = requireNonNull(findViewById(R.id.actions_container)); - mActionsView = requireNonNull(findViewById(R.id.screenshot_actions)); - mDismissButton = requireNonNull(findViewById(R.id.screenshot_dismiss_button)); - mScrollablePreview = requireNonNull(findViewById(R.id.screenshot_scrollable_preview)); - mScreenshotFlash = requireNonNull(findViewById(R.id.screenshot_flash)); - mShareChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_share_chip)); - mEditChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_edit_chip)); - mScrollChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_scroll_chip)); - - setFocusable(true); - mActionsContainer.setScrollX(0); - - mNavMode = getResources().getInteger( - com.android.internal.R.integer.config_navBarInteractionMode); - mOrientationPortrait = - getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT; - mDirectionLTR = - getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; - - // Get focus so that the key events go to the layout. - setFocusableInTouchMode(true); - requestFocus(); - - mScreenshotStatic.setCallbacks(new DraggableConstraintLayout.SwipeDismissCallbacks() { - @Override - public void onInteraction() { - mCallbacks.onUserInteraction(); - } - - @Override - public void onSwipeDismissInitiated(Animator animator) { - if (DEBUG_DISMISS) { - Log.d(ScreenshotView.TAG, "dismiss triggered via swipe gesture"); - } - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, 0, - mPackageName); - } - - @Override - public void onDismissComplete() { - if (mInteractionJankMonitor.isInstrumenting(CUJ_TAKE_SCREENSHOT)) { - mInteractionJankMonitor.end(CUJ_TAKE_SCREENSHOT); - } - mCallbacks.onDismiss(); - } - }); - } - - View getScreenshotPreview() { - return mScreenshotPreview; - } - - void setUiEventLogger(UiEventLogger uiEventLogger) { - mUiEventLogger = uiEventLogger; - } - - void setCallbacks(ScreenshotViewCallback callbacks) { - mCallbacks = callbacks; - } - - void setFlags(FeatureFlags flags) { - mFlags = flags; - } - - void setScreenshot(Bitmap bitmap, Insets screenInsets) { - mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets)); - } - - void setScreenshot(ScreenshotData screenshot) { - mScreenshotData = screenshot; - setScreenshot(screenshot.getBitmap(), screenshot.getInsets()); - mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, screenshot.getBitmap(), - screenshot.getInsets())); - } - - void setPackageName(String packageName) { - mPackageName = packageName; - } - - void setDefaultDisplay(int displayId) { - mDefaultDisplay = displayId; - } - - void updateInsets(WindowInsets insets) { - int orientation = mContext.getResources().getConfiguration().orientation; - mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT); - FrameLayout.LayoutParams p = - (FrameLayout.LayoutParams) mScreenshotStatic.getLayoutParams(); - DisplayCutout cutout = insets.getDisplayCutout(); - Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars()); - if (cutout == null) { - p.setMargins(0, 0, 0, navBarInsets.bottom); - } else { - Insets waterfall = cutout.getWaterfallInsets(); - if (mOrientationPortrait) { - p.setMargins( - waterfall.left, - Math.max(cutout.getSafeInsetTop(), waterfall.top), - waterfall.right, - Math.max(cutout.getSafeInsetBottom(), - Math.max(navBarInsets.bottom, waterfall.bottom))); - } else { - p.setMargins( - Math.max(cutout.getSafeInsetLeft(), waterfall.left), - waterfall.top, - Math.max(cutout.getSafeInsetRight(), waterfall.right), - Math.max(navBarInsets.bottom, waterfall.bottom)); - } - } - mScreenshotStatic.setLayoutParams(p); - mScreenshotStatic.requestLayout(); - } - - void updateOrientation(WindowInsets insets) { - int orientation = mContext.getResources().getConfiguration().orientation; - mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT); - updateInsets(insets); - ViewGroup.LayoutParams params = mScreenshotPreview.getLayoutParams(); - if (mOrientationPortrait) { - params.width = (int) mFixedSize; - params.height = LayoutParams.WRAP_CONTENT; - mScreenshotPreview.setScaleType(ImageView.ScaleType.FIT_START); - } else { - params.width = LayoutParams.WRAP_CONTENT; - params.height = (int) mFixedSize; - mScreenshotPreview.setScaleType(ImageView.ScaleType.FIT_END); - } - - mScreenshotPreview.setLayoutParams(params); - } - - AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) { - if (DEBUG_ANIM) { - Log.d(TAG, "createAnim: bounds=" + bounds + " showFlash=" + showFlash); - } - - Rect targetPosition = new Rect(); - mScreenshotPreview.getHitRect(targetPosition); - - // ratio of preview width, end vs. start size - float cornerScale = - mFixedSize / (mOrientationPortrait ? bounds.width() : bounds.height()); - final float currentScale = 1 / cornerScale; - - AnimatorSet dropInAnimation = new AnimatorSet(); - ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1); - flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS); - flashInAnimator.setInterpolator(mFastOutSlowIn); - flashInAnimator.addUpdateListener(animation -> - mScreenshotFlash.setAlpha((float) animation.getAnimatedValue())); - - ValueAnimator flashOutAnimator = ValueAnimator.ofFloat(1, 0); - flashOutAnimator.setDuration(SCREENSHOT_FLASH_OUT_DURATION_MS); - flashOutAnimator.setInterpolator(mFastOutSlowIn); - flashOutAnimator.addUpdateListener(animation -> - mScreenshotFlash.setAlpha((float) animation.getAnimatedValue())); - - // animate from the current location, to the static preview location - final PointF startPos = new PointF(bounds.centerX(), bounds.centerY()); - final PointF finalPos = new PointF(targetPosition.exactCenterX(), - targetPosition.exactCenterY()); - - // Shift to screen coordinates so that the animation runs on top of the entire screen, - // including e.g. bars covering the display cutout. - int[] locInScreen = mScreenshotPreview.getLocationOnScreen(); - startPos.offset(targetPosition.left - locInScreen[0], targetPosition.top - locInScreen[1]); - - if (DEBUG_ANIM) { - Log.d(TAG, "toCorner: startPos=" + startPos); - Log.d(TAG, "toCorner: finalPos=" + finalPos); - } - - ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1); - toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS); - - toCorner.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mScreenshotPreview.setScaleX(currentScale); - mScreenshotPreview.setScaleY(currentScale); - mScreenshotPreview.setVisibility(View.VISIBLE); - if (mAccessibilityManager.isEnabled()) { - mDismissButton.setAlpha(0); - mDismissButton.setVisibility(View.VISIBLE); - } - } - }); - - float xPositionPct = - SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS; - float dismissPct = - SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS; - float scalePct = - SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS; - toCorner.addUpdateListener(animation -> { - float t = animation.getAnimatedFraction(); - if (t < scalePct) { - float scale = MathUtils.lerp( - currentScale, 1, mFastOutSlowIn.getInterpolation(t / scalePct)); - mScreenshotPreview.setScaleX(scale); - mScreenshotPreview.setScaleY(scale); - } else { - mScreenshotPreview.setScaleX(1); - mScreenshotPreview.setScaleY(1); - } - - if (t < xPositionPct) { - float xCenter = MathUtils.lerp(startPos.x, finalPos.x, - mFastOutSlowIn.getInterpolation(t / xPositionPct)); - mScreenshotPreview.setX(xCenter - mScreenshotPreview.getWidth() / 2f); - } else { - mScreenshotPreview.setX(finalPos.x - mScreenshotPreview.getWidth() / 2f); - } - float yCenter = MathUtils.lerp( - startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t)); - mScreenshotPreview.setY(yCenter - mScreenshotPreview.getHeight() / 2f); - - if (t >= dismissPct) { - mDismissButton.setAlpha((t - dismissPct) / (1 - dismissPct)); - float currentX = mScreenshotPreview.getX(); - float currentY = mScreenshotPreview.getY(); - mDismissButton.setY(currentY - mDismissButton.getHeight() / 2f); - if (mDirectionLTR) { - mDismissButton.setX(currentX + mScreenshotPreview.getWidth() - - mDismissButton.getWidth() / 2f); - } else { - mDismissButton.setX(currentX - mDismissButton.getWidth() / 2f); - } - } - }); - - mScreenshotFlash.setAlpha(0f); - mScreenshotFlash.setVisibility(View.VISIBLE); - - ValueAnimator borderFadeIn = ValueAnimator.ofFloat(0, 1); - borderFadeIn.setDuration(100); - borderFadeIn.addUpdateListener((animation) -> { - float borderAlpha = animation.getAnimatedFraction(); - mScreenshotPreviewBorder.setAlpha(borderAlpha); - mScreenshotBadge.setAlpha(borderAlpha); - }); - - if (showFlash) { - dropInAnimation.play(flashOutAnimator).after(flashInAnimator); - dropInAnimation.play(flashOutAnimator).with(toCorner); - } else { - dropInAnimation.play(toCorner); - } - dropInAnimation.play(borderFadeIn).after(toCorner); - - dropInAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationCancel(Animator animation) { - mInteractionJankMonitor.cancel(CUJ_TAKE_SCREENSHOT); - } - - @Override - public void onAnimationStart(Animator animation) { - InteractionJankMonitor.Configuration.Builder builder = - InteractionJankMonitor.Configuration.Builder.withView( - CUJ_TAKE_SCREENSHOT, mScreenshotPreview) - .setTag("DropIn"); - mInteractionJankMonitor.begin(builder); - } - - @Override - public void onAnimationEnd(Animator animation) { - if (DEBUG_ANIM) { - Log.d(TAG, "drop-in animation ended"); - } - mDismissButton.setOnClickListener(view -> { - if (DEBUG_INPUT) { - Log.d(TAG, "dismiss button clicked"); - } - mUiEventLogger.log( - ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL, 0, mPackageName); - animateDismissal(); - }); - mDismissButton.setAlpha(1); - float dismissOffset = mDismissButton.getWidth() / 2f; - float finalDismissX = mDirectionLTR - ? finalPos.x - dismissOffset + bounds.width() * cornerScale / 2f - : finalPos.x - dismissOffset - bounds.width() * cornerScale / 2f; - mDismissButton.setX(finalDismissX); - mDismissButton.setY( - finalPos.y - dismissOffset - bounds.height() * cornerScale / 2f); - mScreenshotPreview.setScaleX(1); - mScreenshotPreview.setScaleY(1); - mScreenshotPreview.setX(finalPos.x - mScreenshotPreview.getWidth() / 2f); - mScreenshotPreview.setY(finalPos.y - mScreenshotPreview.getHeight() / 2f); - requestLayout(); - mInteractionJankMonitor.end(CUJ_TAKE_SCREENSHOT); - createScreenshotActionsShadeAnimation().start(); - } - }); - - return dropInAnimation; - } - - ValueAnimator createScreenshotActionsShadeAnimation() { - // By default the activities won't be able to start immediately; override this to keep - // the same behavior as if started from a notification - try { - ActivityManager.getService().resumeAppSwitches(); - } catch (RemoteException e) { - } - - ArrayList<OverlayActionChip> chips = new ArrayList<>(); - - mShareChip.setContentDescription(mContext.getString(R.string.screenshot_share_description)); - mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true); - mShareChip.setOnClickListener(v -> { - mShareChip.setIsPending(true); - mEditChip.setIsPending(false); - if (mQuickShareChip != null) { - mQuickShareChip.setIsPending(false); - } - mPendingInteraction = PendingInteraction.SHARE; - }); - chips.add(mShareChip); - - mEditChip.setContentDescription( - mContext.getString(R.string.screenshot_edit_description)); - mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), - true); - mEditChip.setOnClickListener(v -> { - mEditChip.setIsPending(true); - mShareChip.setIsPending(false); - if (mQuickShareChip != null) { - mQuickShareChip.setIsPending(false); - } - mPendingInteraction = PendingInteraction.EDIT; - }); - chips.add(mEditChip); - - mScreenshotPreview.setOnClickListener(v -> { - mShareChip.setIsPending(false); - mEditChip.setIsPending(false); - if (mQuickShareChip != null) { - mQuickShareChip.setIsPending(false); - } - mPendingInteraction = PendingInteraction.PREVIEW; - }); - - mScrollChip.setText(mContext.getString(R.string.screenshot_scroll_label)); - mScrollChip.setIcon(Icon.createWithResource(mContext, - R.drawable.ic_screenshot_scroll), true); - chips.add(mScrollChip); - - // remove the margin from the last chip so that it's correctly aligned with the end - LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) - mActionsView.getChildAt(0).getLayoutParams(); - params.setMarginEnd(0); - mActionsView.getChildAt(0).setLayoutParams(params); - - ValueAnimator animator = ValueAnimator.ofFloat(0, 1); - animator.setDuration(SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS); - float alphaFraction = (float) SCREENSHOT_ACTIONS_ALPHA_DURATION_MS - / SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS; - mActionsContainer.setAlpha(0f); - mActionsContainerBackground.setAlpha(0f); - mActionsContainer.setVisibility(View.VISIBLE); - mActionsContainerBackground.setVisibility(View.VISIBLE); - - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationCancel(Animator animation) { - mInteractionJankMonitor.cancel(CUJ_TAKE_SCREENSHOT); - } - - @Override - public void onAnimationEnd(Animator animation) { - mInteractionJankMonitor.end(CUJ_TAKE_SCREENSHOT); - } - - @Override - public void onAnimationStart(Animator animation) { - InteractionJankMonitor.Configuration.Builder builder = - InteractionJankMonitor.Configuration.Builder.withView( - CUJ_TAKE_SCREENSHOT, mScreenshotStatic) - .setTag("Actions") - .setTimeout(SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS); - mInteractionJankMonitor.begin(builder); - } - }); - - animator.addUpdateListener(animation -> { - float t = animation.getAnimatedFraction(); - float containerAlpha = t < alphaFraction ? t / alphaFraction : 1; - mActionsContainer.setAlpha(containerAlpha); - mActionsContainerBackground.setAlpha(containerAlpha); - float containerScale = SCREENSHOT_ACTIONS_START_SCALE_X - + (t * (1 - SCREENSHOT_ACTIONS_START_SCALE_X)); - mActionsContainer.setScaleX(containerScale); - mActionsContainerBackground.setScaleX(containerScale); - for (OverlayActionChip chip : chips) { - chip.setAlpha(t); - chip.setScaleX(1 / containerScale); // invert to keep size of children constant - } - mActionsContainer.setScrollX(mDirectionLTR ? 0 : mActionsContainer.getWidth()); - mActionsContainer.setPivotX(mDirectionLTR ? 0 : mActionsContainer.getWidth()); - mActionsContainerBackground.setPivotX( - mDirectionLTR ? 0 : mActionsContainerBackground.getWidth()); - }); - return animator; - } - - void badgeScreenshot(@Nullable Drawable badge) { - mScreenshotBadge.setImageDrawable(badge); - mScreenshotBadge.setVisibility(badge != null ? View.VISIBLE : View.GONE); - } - - void setChipIntents(ScreenshotController.SavedImageData imageData) { - mShareChip.setOnClickListener(v -> { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED, 0, mPackageName); - prepareSharedTransition(); - - Intent shareIntent = ActionIntentCreator.INSTANCE.createShareWithSubject( - imageData.uri, imageData.subject); - mCallbacks.onAction(shareIntent, imageData.owner, false); - - }); - mEditChip.setOnClickListener(v -> { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED, 0, mPackageName); - prepareSharedTransition(); - mCallbacks.onAction( - ActionIntentCreator.INSTANCE.createEdit(imageData.uri, mContext), - imageData.owner, true); - }); - mScreenshotPreview.setOnClickListener(v -> { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED, 0, mPackageName); - prepareSharedTransition(); - mCallbacks.onAction( - ActionIntentCreator.INSTANCE.createEdit(imageData.uri, mContext), - imageData.owner, true); - }); - if (mQuickShareChip != null) { - if (imageData.quickShareAction != null) { - mQuickShareChip.setPendingIntent(imageData.quickShareAction.actionIntent, - () -> { - mUiEventLogger.log( - ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED, 0, - mPackageName); - animateDismissal(); - }); - } else { - // hide chip and unset pending interaction if necessary, since we don't actually - // have a useable quick share intent - Log.wtf(TAG, "Showed quick share chip, but quick share intent was null"); - if (mPendingInteraction == PendingInteraction.QUICK_SHARE) { - mPendingInteraction = null; - } - mQuickShareChip.setVisibility(GONE); - } - } - - if (mPendingInteraction != null) { - switch (mPendingInteraction) { - case PREVIEW: - mScreenshotPreview.callOnClick(); - break; - case SHARE: - mShareChip.callOnClick(); - break; - case EDIT: - mEditChip.callOnClick(); - break; - case QUICK_SHARE: - mQuickShareChip.callOnClick(); - break; - } - } else { - LayoutInflater inflater = LayoutInflater.from(mContext); - - for (Notification.Action smartAction : imageData.smartActions) { - OverlayActionChip actionChip = (OverlayActionChip) inflater.inflate( - R.layout.overlay_action_chip, mActionsView, false); - actionChip.setText(smartAction.title); - actionChip.setIcon(smartAction.getIcon(), false); - actionChip.setPendingIntent(smartAction.actionIntent, - () -> { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED, - 0, mPackageName); - animateDismissal(); - }); - actionChip.setAlpha(1); - mActionsView.addView(actionChip, mActionsView.getChildCount() - 1); - mSmartChips.add(actionChip); - } - } - } - - void addQuickShareChip(Notification.Action quickShareAction) { - if (mQuickShareChip != null) { - mSmartChips.remove(mQuickShareChip); - mActionsView.removeView(mQuickShareChip); - } - if (mPendingInteraction == PendingInteraction.QUICK_SHARE) { - mPendingInteraction = null; - } - if (mPendingInteraction == null) { - LayoutInflater inflater = LayoutInflater.from(mContext); - mQuickShareChip = (OverlayActionChip) inflater.inflate( - R.layout.overlay_action_chip, mActionsView, false); - mQuickShareChip.setText(quickShareAction.title); - mQuickShareChip.setIcon(quickShareAction.getIcon(), false); - mQuickShareChip.setOnClickListener(v -> { - mShareChip.setIsPending(false); - mEditChip.setIsPending(false); - mQuickShareChip.setIsPending(true); - mPendingInteraction = PendingInteraction.QUICK_SHARE; - }); - mQuickShareChip.setAlpha(1); - mActionsView.addView(mQuickShareChip); - mSmartChips.add(mQuickShareChip); - } - } - - private Rect scrollableAreaOnScreen(ScrollCaptureResponse response) { - Rect r = new Rect(response.getBoundsInWindow()); - Rect windowInScreen = response.getWindowBounds(); - r.offset(windowInScreen.left, windowInScreen.top); - r.intersect(new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); - return r; - } - - void startLongScreenshotTransition(Rect destination, Runnable onTransitionEnd, - ScrollCaptureController.LongScreenshot longScreenshot) { - mPendingSharedTransition = true; - AnimatorSet animSet = new AnimatorSet(); - - ValueAnimator scrimAnim = ValueAnimator.ofFloat(0, 1); - scrimAnim.addUpdateListener(animation -> - mScrollingScrim.setAlpha(1 - animation.getAnimatedFraction())); - - if (mShowScrollablePreview) { - mScrollablePreview.setImageBitmap(longScreenshot.toBitmap()); - float startX = mScrollablePreview.getX(); - float startY = mScrollablePreview.getY(); - int[] locInScreen = mScrollablePreview.getLocationOnScreen(); - destination.offset((int) startX - locInScreen[0], (int) startY - locInScreen[1]); - mScrollablePreview.setPivotX(0); - mScrollablePreview.setPivotY(0); - mScrollablePreview.setAlpha(1f); - float currentScale = mScrollablePreview.getWidth() / (float) longScreenshot.getWidth(); - Matrix matrix = new Matrix(); - matrix.setScale(currentScale, currentScale); - matrix.postTranslate( - longScreenshot.getLeft() * currentScale, - longScreenshot.getTop() * currentScale); - mScrollablePreview.setImageMatrix(matrix); - float destinationScale = destination.width() / (float) mScrollablePreview.getWidth(); - - ValueAnimator previewAnim = ValueAnimator.ofFloat(0, 1); - previewAnim.addUpdateListener(animation -> { - float t = animation.getAnimatedFraction(); - float currScale = MathUtils.lerp(1, destinationScale, t); - mScrollablePreview.setScaleX(currScale); - mScrollablePreview.setScaleY(currScale); - mScrollablePreview.setX(MathUtils.lerp(startX, destination.left, t)); - mScrollablePreview.setY(MathUtils.lerp(startY, destination.top, t)); - }); - ValueAnimator previewFadeAnim = ValueAnimator.ofFloat(1, 0); - previewFadeAnim.addUpdateListener(animation -> - mScrollablePreview.setAlpha(1 - animation.getAnimatedFraction())); - animSet.play(previewAnim).with(scrimAnim).before(previewFadeAnim); - previewAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - onTransitionEnd.run(); - } - }); - } else { - // if we switched orientations between the original screenshot and the long screenshot - // capture, just fade out the scrim instead of running the preview animation - animSet.play(scrimAnim); - animSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - onTransitionEnd.run(); - } - }); - } - animSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - mCallbacks.onDismiss(); - } - }); - animSet.start(); - } - - void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap, - Bitmap newBitmap, boolean screenshotTakenInPortrait) { - mShowScrollablePreview = (screenshotTakenInPortrait == mOrientationPortrait); - - mScrollingScrim.setImageBitmap(newBitmap); - mScrollingScrim.setVisibility(View.VISIBLE); - - if (mShowScrollablePreview) { - Rect scrollableArea = scrollableAreaOnScreen(response); - - float scale = mFixedSize - / (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight()); - ConstraintLayout.LayoutParams params = - (ConstraintLayout.LayoutParams) mScrollablePreview.getLayoutParams(); - - params.width = (int) (scale * scrollableArea.width()); - params.height = (int) (scale * scrollableArea.height()); - Matrix matrix = new Matrix(); - matrix.setScale(scale, scale); - matrix.postTranslate(-scrollableArea.left * scale, -scrollableArea.top * scale); - - mScrollablePreview.setTranslationX(scale - * (mDirectionLTR ? scrollableArea.left : scrollableArea.right - getWidth())); - mScrollablePreview.setTranslationY(scale * scrollableArea.top); - mScrollablePreview.setImageMatrix(matrix); - mScrollablePreview.setImageBitmap(screenBitmap); - mScrollablePreview.setVisibility(View.VISIBLE); - } - mDismissButton.setVisibility(View.GONE); - mActionsContainer.setVisibility(View.GONE); - // set these invisible, but not gone, so that the views are laid out correctly - mActionsContainerBackground.setVisibility(View.INVISIBLE); - mScreenshotPreviewBorder.setVisibility(View.INVISIBLE); - mScreenshotPreview.setVisibility(View.INVISIBLE); - mScrollingScrim.setImageTintBlendMode(BlendMode.SRC_ATOP); - ValueAnimator anim = ValueAnimator.ofFloat(0, .3f); - anim.addUpdateListener(animation -> mScrollingScrim.setImageTintList( - ColorStateList.valueOf(Color.argb((float) animation.getAnimatedValue(), 0, 0, 0)))); - anim.setDuration(200); - anim.start(); - } - - void restoreNonScrollingUi() { - mScrollChip.setVisibility(View.GONE); - mScrollablePreview.setVisibility(View.GONE); - mScrollingScrim.setVisibility(View.GONE); - - if (mAccessibilityManager.isEnabled()) { - mDismissButton.setVisibility(View.VISIBLE); - } - mActionsContainer.setVisibility(View.VISIBLE); - mActionsContainerBackground.setVisibility(View.VISIBLE); - mScreenshotPreviewBorder.setVisibility(View.VISIBLE); - mScreenshotPreview.setVisibility(View.VISIBLE); - // reset the timeout - mCallbacks.onUserInteraction(); - } - - boolean isDismissing() { - return mScreenshotStatic.isDismissing(); - } - - boolean isPendingSharedTransition() { - return mPendingSharedTransition; - } - - void animateDismissal() { - mScreenshotStatic.dismiss(); - } - - void reset() { - if (DEBUG_UI) { - Log.d(TAG, "reset screenshot view"); - } - mScreenshotStatic.cancelDismissal(); - if (DEBUG_WINDOW) { - Log.d(TAG, "removing OnComputeInternalInsetsListener"); - } - // Make sure we clean up the view tree observer - getViewTreeObserver().removeOnComputeInternalInsetsListener(this); - // Clear any references to the bitmap - mScreenshotPreview.setImageDrawable(null); - mScreenshotPreview.setVisibility(View.INVISIBLE); - mScreenshotPreview.setAlpha(1f); - mScreenshotPreviewBorder.setAlpha(0); - mScreenshotBadge.setAlpha(0f); - mScreenshotBadge.setVisibility(View.GONE); - mScreenshotBadge.setImageDrawable(null); - mPendingSharedTransition = false; - mActionsContainerBackground.setVisibility(View.INVISIBLE); - mActionsContainer.setVisibility(View.GONE); - mDismissButton.setVisibility(View.GONE); - mScrollingScrim.setVisibility(View.GONE); - mScrollablePreview.setVisibility(View.GONE); - mScreenshotStatic.setTranslationX(0); - mScreenshotPreview.setContentDescription( - mContext.getResources().getString(R.string.screenshot_preview_description)); - mScreenshotPreview.setOnClickListener(null); - mShareChip.setOnClickListener(null); - mScrollingScrim.setVisibility(View.GONE); - mEditChip.setOnClickListener(null); - mShareChip.setIsPending(false); - mEditChip.setIsPending(false); - mPendingInteraction = null; - for (OverlayActionChip chip : mSmartChips) { - mActionsView.removeView(chip); - } - mSmartChips.clear(); - mQuickShareChip = null; - setAlpha(1); - mScreenshotStatic.setAlpha(1); - mScreenshotData = null; - } - - private void prepareSharedTransition() { - mPendingSharedTransition = true; - // fade out non-preview UI - createScreenshotFadeDismissAnimation().start(); - } - - ValueAnimator createScreenshotFadeDismissAnimation() { - ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); - alphaAnim.addUpdateListener(animation -> { - float alpha = 1 - animation.getAnimatedFraction(); - mDismissButton.setAlpha(alpha); - mActionsContainerBackground.setAlpha(alpha); - mActionsContainer.setAlpha(alpha); - mScreenshotPreviewBorder.setAlpha(alpha); - mScreenshotBadge.setAlpha(alpha); - }); - alphaAnim.setDuration(600); - return alphaAnim; - } - - /** - * Create a drawable using the size of the bitmap and insets as the fractional inset parameters. - */ - private static Drawable createScreenDrawable(Resources res, Bitmap bitmap, Insets insets) { - int insettedWidth = bitmap.getWidth() - insets.left - insets.right; - int insettedHeight = bitmap.getHeight() - insets.top - insets.bottom; - - 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 " - + "degenerate region: " + bitmap.getWidth() + "x" + bitmap.getHeight() + " " - + bitmapDrawable); - return bitmapDrawable; - } - - InsetDrawable insetDrawable = new InsetDrawable(bitmapDrawable, - -1f * insets.left / insettedWidth, - -1f * insets.top / insettedHeight, - -1f * insets.right / insettedWidth, - -1f * insets.bottom / insettedHeight); - - if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) { - // Are any of the insets negative, meaning the bitmap is smaller than the bounds so need - // to fill in the background of the drawable. - return new LayerDrawable(new Drawable[]{ - new ColorDrawable(Color.BLACK), insetDrawable}); - } else { - return insetDrawable; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt index df93a5e56c22..ba0421041888 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt @@ -17,7 +17,6 @@ package com.android.systemui.screenshot import android.animation.Animator -import android.app.Notification import android.content.Context import android.graphics.Bitmap import android.graphics.Rect @@ -33,7 +32,7 @@ interface ScreenshotViewProxy { val screenshotPreview: View var packageName: String - var callbacks: ScreenshotView.ScreenshotViewCallback? + var callbacks: ScreenshotShelfViewProxy.ScreenshotViewCallback? var screenshot: ScreenshotData? val isAttachedToWindow: Boolean @@ -41,15 +40,13 @@ interface ScreenshotViewProxy { val isPendingSharedTransition: Boolean fun reset() + fun updateInsets(insets: WindowInsets) - fun updateOrientation(insets: WindowInsets) + fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator - fun addQuickShareChip(quickShareAction: Notification.Action) - fun setChipIntents(imageData: ScreenshotController.SavedImageData) + fun requestDismissal(event: ScreenshotEvent?) - fun showScrollChip(packageName: String, onClick: Runnable) - fun hideScrollChip() fun prepareScrollingTransition( response: ScrollCaptureResponse, screenBitmap: Bitmap, @@ -57,17 +54,23 @@ interface ScreenshotViewProxy { screenshotTakenInPortrait: Boolean, onTransitionPrepared: Runnable, ) + fun startLongScreenshotTransition( transitionDestination: Rect, onTransitionEnd: Runnable, longScreenshot: ScrollCaptureController.LongScreenshot ) + fun restoreNonScrollingUi() + fun fadeForSharedTransition() fun stopInputListening() + fun requestFocus() + fun announceForAccessibility(string: String) + fun prepareEntranceAnimation(runnable: Runnable) interface Factory { |