diff options
11 files changed, 291 insertions, 32 deletions
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml index 2cb4b0233a90..49d3a8ec8ad8 100644 --- a/packages/SystemUI/res/layout/screenshot_shelf.xml +++ b/packages/SystemUI/res/layout/screenshot_shelf.xml @@ -135,11 +135,12 @@ android:id="@+id/screenshot_scrollable_preview" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:clipToOutline="true" android:scaleType="matrix" android:visibility="gone" app:layout_constraintStart_toStartOf="@id/screenshot_preview" app:layout_constraintTop_toTopOf="@id/screenshot_preview" - android:elevation="7dp"/> + android:elevation="3dp"/> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" @@ -170,6 +171,13 @@ </FrameLayout> </androidx.constraintlayout.widget.ConstraintLayout> <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" diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt index caa67dff086f..1868b4a29f20 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt @@ -25,7 +25,6 @@ import android.content.Intent import android.os.UserHandle import android.util.Log import android.util.Pair -import android.view.View import android.view.Window import com.android.app.tracing.coroutines.launch import com.android.internal.app.ChooserActivity @@ -41,8 +40,8 @@ constructor( private val intentExecutor: ActionIntentExecutor, @Application private val applicationScope: CoroutineScope, @Assisted val window: Window, - @Assisted val transitionView: View, - @Assisted val onDismiss: (() -> Unit) + @Assisted val viewProxy: ScreenshotViewProxy, + @Assisted val finishDismiss: () -> Unit, ) { var isPendingSharedTransition = false @@ -50,6 +49,7 @@ constructor( fun startSharedTransition(intent: Intent, user: UserHandle, overrideTransition: Boolean) { isPendingSharedTransition = true + viewProxy.fadeForSharedTransition() val windowTransition = createWindowTransition() applicationScope.launch("$TAG#launchIntentAsync") { intentExecutor.launchIntent( @@ -70,7 +70,7 @@ constructor( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED ) pendingIntent.send(options.toBundle()) - onDismiss.invoke() + viewProxy.requestDismissal(null) } catch (e: PendingIntent.CanceledException) { Log.e(TAG, "Intent cancelled", e) } @@ -89,7 +89,7 @@ constructor( override fun hideSharedElements() { isPendingSharedTransition = false - onDismiss.invoke() + finishDismiss.invoke() } override fun onFinish() {} @@ -98,13 +98,20 @@ constructor( window, callbacks, null, - Pair.create(transitionView, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME) + Pair.create( + viewProxy.screenshotPreview, + ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME + ) ) } @AssistedFactory interface Factory { - fun create(window: Window, transitionView: View, onDismiss: (() -> Unit)): ActionExecutor + fun create( + window: Window, + viewProxy: ScreenshotViewProxy, + finishDismiss: (() -> Unit) + ): ActionExecutor } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt index a0cef529ecde..15638d3496e9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt @@ -97,6 +97,7 @@ object ActionIntentCreator { .putExtra(LongScreenshotActivity.EXTRA_SCREENSHOT_USER_HANDLE, owner) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) } private const val EXTRA_EDIT_SOURCE = "edit_source" diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt index 4cf18fb482d8..3d024a6a8ccf 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt @@ -157,6 +157,8 @@ constructor( override fun restoreNonScrollingUi() = view.restoreNonScrollingUi() + override fun fadeForSharedTransition() {} // unused + override fun stopInputListening() = view.stopInputListening() override fun requestFocus() { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 2f026aee64d8..9ad6d0faea88 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -317,9 +317,9 @@ public class ScreenshotController { mConfigChanges.applyNewConfig(context.getResources()); reloadAssets(); - mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy.getScreenshotPreview(), + mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy, () -> { - requestDismissal(null); + finishDismiss(); return Unit.INSTANCE; }); @@ -623,9 +623,11 @@ public class ScreenshotController { (response) -> { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION, 0, response.getPackageName()); - if (screenshotShelfUi2() && mActionsProvider != null) { - mActionsProvider.onScrollChipReady( - () -> onScrollButtonClicked(owner, response)); + if (screenshotShelfUi2()) { + if (mActionsProvider != null) { + mActionsProvider.onScrollChipReady( + () -> onScrollButtonClicked(owner, response)); + } } else { mViewProxy.showScrollChip(response.getPackageName(), () -> onScrollButtonClicked(owner, response)); @@ -657,9 +659,7 @@ public class ScreenshotController { () -> { final Intent intent = ActionIntentCreator.INSTANCE.createLongScreenshotIntent( owner, mContext); - mActionIntentExecutor.launchIntentAsync(intent, owner, true, - ActivityOptions.makeCustomAnimation(mContext, 0, 0), null); - + mActionIntentExecutor.launchIntentAsync(intent, owner, true, null, null); }, mViewProxy::restoreNonScrollingUi, mViewProxy::startLongScreenshotTransition); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt index 846884fe4cf9..3ac070a28b2b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt @@ -90,15 +90,15 @@ constructor( override var isDismissing = false override var isPendingSharedTransition = false - private val animationController = ScreenshotAnimationController(view) + private val animationController = ScreenshotAnimationController(view, viewModel) init { shelfViewBinder.bind( view, viewModel, + animationController, LayoutInflater.from(context), onDismissalRequested = { event, velocity -> requestDismissal(event, velocity) }, - onDismissalCancelled = { animationController.getSwipeReturnAnimation().start() }, onUserInteraction = { callbacks?.onUserInteraction() } ) view.updateInsets(windowManager.currentWindowMetrics.windowInsets) @@ -188,24 +188,53 @@ constructor( override fun prepareScrollingTransition( response: ScrollCaptureResponse, - screenBitmap: Bitmap, + screenBitmap: Bitmap, // unused newScreenshot: Bitmap, screenshotTakenInPortrait: Boolean, onTransitionPrepared: Runnable, ) { - onTransitionPrepared.run() + viewModel.setScrollingScrimBitmap(newScreenshot) + viewModel.setScrollableRect(scrollableAreaOnScreen(response)) + animationController.fadeForLongScreenshotTransition() + view.post { onTransitionPrepared.run() } + } + + private fun scrollableAreaOnScreen(response: ScrollCaptureResponse): Rect { + val r = Rect(response.boundsInWindow) + val windowInScreen = response.windowBounds + r.offset(windowInScreen?.left ?: 0, windowInScreen?.top ?: 0) + r.intersect( + Rect( + 0, + 0, + context.resources.displayMetrics.widthPixels, + context.resources.displayMetrics.heightPixels + ) + ) + return r } override fun startLongScreenshotTransition( transitionDestination: Rect, onTransitionEnd: Runnable, - longScreenshot: ScrollCaptureController.LongScreenshot + longScreenshot: ScrollCaptureController.LongScreenshot, ) { - onTransitionEnd.run() - callbacks?.onDismiss() + val transitionAnimation = + animationController.runLongScreenshotTransition( + transitionDestination, + longScreenshot, + onTransitionEnd + ) + transitionAnimation.doOnEnd { callbacks?.onDismiss() } + transitionAnimation.start() } - override fun restoreNonScrollingUi() {} + override fun restoreNonScrollingUi() { + viewModel.setScrollableRect(null) + viewModel.setScrollingScrimBitmap(null) + animationController.restoreUI() + callbacks?.onUserInteraction() // reset the timeout + } override fun stopInputListening() {} @@ -228,6 +257,10 @@ constructor( ) } + override fun fadeForSharedTransition() { + animationController.fadeForSharedTransition() + } + private fun addPredictiveBackListener(onDismissRequested: (ScreenshotEvent) -> Unit) { val onBackInvokedCallback = OnBackInvokedCallback { debugLog(DEBUG_INPUT) { "Predictive Back callback dispatched" } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt index a4069d11f8fb..df93a5e56c22 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt @@ -63,6 +63,7 @@ interface ScreenshotViewProxy { longScreenshot: ScrollCaptureController.LongScreenshot ) fun restoreNonScrollingUi() + fun fadeForSharedTransition() fun stopInputListening() fun requestFocus() diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt index 06e88f46c5f1..a4906c12b487 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt @@ -20,6 +20,10 @@ import android.animation.Animator import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.animation.ValueAnimator +import android.content.res.ColorStateList +import android.graphics.BlendMode +import android.graphics.Color +import android.graphics.Matrix import android.graphics.PointF import android.graphics.Rect import android.util.MathUtils @@ -29,13 +33,21 @@ import android.widget.ImageView import androidx.core.animation.doOnEnd import androidx.core.animation.doOnStart import com.android.systemui.res.R +import com.android.systemui.screenshot.scroll.ScrollCaptureController +import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel import kotlin.math.abs import kotlin.math.max import kotlin.math.sign -class ScreenshotAnimationController(private val view: ScreenshotShelfView) { +class ScreenshotAnimationController( + private val view: ScreenshotShelfView, + private val viewModel: ScreenshotViewModel +) { private var animator: Animator? = null private val screenshotPreview = view.requireViewById<ImageView>(R.id.screenshot_preview) + private val scrollingScrim = view.requireViewById<ImageView>((R.id.screenshot_scrolling_scrim)) + private val scrollTransitionPreview = + view.requireViewById<ImageView>(R.id.screenshot_scrollable_preview) private val flashView = view.requireViewById<View>(R.id.screenshot_flash) private val actionContainer = view.requireViewById<View>(R.id.actions_container_background) private val fastOutSlowIn = @@ -46,6 +58,14 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { view.requireViewById(R.id.screenshot_badge), view.requireViewById(R.id.screenshot_dismiss_button) ) + private val fadeUI = + listOf<View>( + view.requireViewById(R.id.screenshot_preview_border), + view.requireViewById(R.id.actions_container_background), + view.requireViewById(R.id.screenshot_badge), + view.requireViewById(R.id.screenshot_dismiss_button), + view.requireViewById(R.id.screenshot_message_container), + ) fun getEntranceAnimation( bounds: Rect, @@ -96,15 +116,108 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { } entranceAnimation.play(fadeInAnimator).after(previewAnimator) entranceAnimation.doOnStart { + viewModel.setIsAnimating(true) for (child in staticUI) { child.alpha = 0f } } + entranceAnimation.doOnEnd { viewModel.setIsAnimating(false) } this.animator = entranceAnimation return entranceAnimation } + fun fadeForSharedTransition() { + animator?.cancel() + val fadeAnimator = ValueAnimator.ofFloat(1f, 0f) + fadeAnimator.addUpdateListener { + for (view in fadeUI) { + view.alpha = it.animatedValue as Float + } + } + animator = fadeAnimator + fadeAnimator.start() + } + + fun runLongScreenshotTransition( + destRect: Rect, + longScreenshot: ScrollCaptureController.LongScreenshot, + onTransitionEnd: Runnable + ): Animator { + val animSet = AnimatorSet() + + val scrimAnim = ValueAnimator.ofFloat(0f, 1f) + scrimAnim.addUpdateListener { animation: ValueAnimator -> + scrollingScrim.setAlpha(1 - animation.animatedFraction) + } + scrollTransitionPreview.visibility = View.VISIBLE + if (true) { + scrollTransitionPreview.setImageBitmap(longScreenshot.toBitmap()) + val startX: Float = scrollTransitionPreview.x + val startY: Float = scrollTransitionPreview.y + val locInScreen: IntArray = scrollTransitionPreview.getLocationOnScreen() + destRect.offset(startX.toInt() - locInScreen[0], startY.toInt() - locInScreen[1]) + scrollTransitionPreview.pivotX = 0f + scrollTransitionPreview.pivotY = 0f + scrollTransitionPreview.setAlpha(1f) + val currentScale: Float = scrollTransitionPreview.width / longScreenshot.width.toFloat() + val matrix = Matrix() + matrix.setScale(currentScale, currentScale) + matrix.postTranslate( + longScreenshot.left * currentScale, + longScreenshot.top * currentScale + ) + scrollTransitionPreview.setImageMatrix(matrix) + val destinationScale: Float = destRect.width() / scrollTransitionPreview.width.toFloat() + val previewAnim = ValueAnimator.ofFloat(0f, 1f) + previewAnim.addUpdateListener { animation: ValueAnimator -> + val t = animation.animatedFraction + val currScale = MathUtils.lerp(1f, destinationScale, t) + scrollTransitionPreview.scaleX = currScale + scrollTransitionPreview.scaleY = currScale + scrollTransitionPreview.x = MathUtils.lerp(startX, destRect.left.toFloat(), t) + scrollTransitionPreview.y = MathUtils.lerp(startY, destRect.top.toFloat(), t) + } + val previewFadeAnim = ValueAnimator.ofFloat(1f, 0f) + previewFadeAnim.addUpdateListener { animation: ValueAnimator -> + scrollTransitionPreview.setAlpha(1 - animation.animatedFraction) + } + previewAnim.doOnEnd { onTransitionEnd.run() } + animSet.play(previewAnim).with(scrimAnim).before(previewFadeAnim) + } 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 + scrimAnim.doOnEnd { onTransitionEnd.run() } + animSet.play(scrimAnim) + } + animator = animSet + return animSet + } + + fun fadeForLongScreenshotTransition() { + scrollingScrim.imageTintBlendMode = BlendMode.SRC_ATOP + val anim = ValueAnimator.ofFloat(0f, .3f) + anim.addUpdateListener { + scrollingScrim.setImageTintList( + ColorStateList.valueOf(Color.argb(it.animatedValue as Float, 0f, 0f, 0f)) + ) + } + for (view in fadeUI) { + view.alpha = 0f + } + screenshotPreview.alpha = 0f + anim.setDuration(200) + anim.start() + } + + fun restoreUI() { + animator?.cancel() + for (view in fadeUI) { + view.alpha = 1f + } + screenshotPreview.alpha = 1f + } + fun getSwipeReturnAnimation(): Animator { animator?.cancel() val animator = ValueAnimator.ofFloat(view.translationX, 0f) @@ -114,6 +227,7 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { } fun getSwipeDismissAnimation(requestedVelocity: Float?): Animator { + animator?.cancel() val velocity = getAdjustedVelocity(requestedVelocity) val screenWidth = view.resources.displayMetrics.widthPixels // translation at which point the visible UI is fully off the screen (in the direction @@ -131,6 +245,8 @@ class ScreenshotAnimationController(private val view: ScreenshotShelfView) { view.alpha = 1f - it.animatedFraction } animator.duration = ((abs(distance / velocity))).toLong() + animator.doOnStart { viewModel.setIsAnimating(true) } + animator.doOnEnd { viewModel.setIsAnimating(false) } this.animator = animator return animator diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt index c7bc50cb3802..442b3873be4d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt @@ -16,7 +16,11 @@ package com.android.systemui.screenshot.ui.binder +import android.content.res.Configuration import android.graphics.Bitmap +import android.graphics.Matrix +import android.graphics.Rect +import android.util.LayoutDirection import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -29,6 +33,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.screenshot.ScreenshotEvent +import com.android.systemui.screenshot.ui.ScreenshotAnimationController import com.android.systemui.screenshot.ui.ScreenshotShelfView import com.android.systemui.screenshot.ui.SwipeGestureListener import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel @@ -45,9 +50,9 @@ constructor(private val buttonViewBinder: ActionButtonViewBinder) { fun bind( view: ScreenshotShelfView, viewModel: ScreenshotViewModel, + animationController: ScreenshotAnimationController, layoutInflater: LayoutInflater, onDismissalRequested: (event: ScreenshotEvent, velocity: Float?) -> Unit, - onDismissalCancelled: () -> Unit, onUserInteraction: () -> Unit ) { val swipeGestureListener = @@ -56,7 +61,7 @@ constructor(private val buttonViewBinder: ActionButtonViewBinder) { onDismiss = { onDismissalRequested(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, it) }, - onCancel = onDismissalCancelled + onCancel = { animationController.getSwipeReturnAnimation().start() } ) view.onTouchInterceptListener = { swipeGestureListener.onMotionEvent(it) } view.userInteractionCallback = onUserInteraction @@ -66,11 +71,14 @@ constructor(private val buttonViewBinder: ActionButtonViewBinder) { val previewBorder = view.requireViewById<View>(R.id.screenshot_preview_border) previewView.clipToOutline = true previewViewBlur.clipToOutline = true + val actionsContainer: LinearLayout = view.requireViewById(R.id.screenshot_actions) val dismissButton = view.requireViewById<View>(R.id.screenshot_dismiss_button) dismissButton.visibility = if (viewModel.showDismissButton) View.VISIBLE else View.GONE dismissButton.setOnClickListener { onDismissalRequested(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL, null) } + val scrollingScrim: ImageView = view.requireViewById(R.id.screenshot_scrolling_scrim) + val scrollablePreview: ImageView = view.requireViewById(R.id.screenshot_scrollable_preview) val badgeView = view.requireViewById<ImageView>(R.id.screenshot_badge) // use immediate dispatcher to ensure screenshot bitmap is set before animation @@ -91,6 +99,29 @@ constructor(private val buttonViewBinder: ActionButtonViewBinder) { } } launch { + viewModel.scrollingScrim.collect { bitmap -> + if (bitmap != null) { + scrollingScrim.setImageBitmap(bitmap) + scrollingScrim.visibility = View.VISIBLE + } else { + scrollingScrim.visibility = View.GONE + } + } + } + launch { + viewModel.scrollableRect.collect { rect -> + if (rect != null) { + setScrollablePreview( + scrollablePreview, + viewModel.preview.value, + rect + ) + } else { + scrollablePreview.visibility = View.GONE + } + } + } + launch { viewModel.badge.collect { badge -> badgeView.setImageDrawable(badge) badgeView.visibility = if (badge != null) View.VISIBLE else View.GONE @@ -102,6 +133,14 @@ constructor(private val buttonViewBinder: ActionButtonViewBinder) { } } launch { + viewModel.isAnimating.collect { isAnimating -> + previewView.isClickable = !isAnimating + for (child in actionsContainer.children) { + child.isClickable = !isAnimating + } + } + } + launch { viewModel.actions.collect { actions -> updateActions( actions, @@ -191,4 +230,35 @@ constructor(private val buttonViewBinder: ActionButtonViewBinder) { screenshotPreview.layoutParams = params screenshotPreview.requestLayout() } + + private fun setScrollablePreview( + scrollablePreview: ImageView, + bitmap: Bitmap?, + scrollableRect: Rect + ) { + if (bitmap == null) { + return + } + val fixedSize = scrollablePreview.resources.getDimensionPixelSize(R.dimen.overlay_x_scale) + val inPortrait = + scrollablePreview.resources.configuration.orientation == + Configuration.ORIENTATION_PORTRAIT + val scale: Float = fixedSize / ((if (inPortrait) bitmap.width else bitmap.height).toFloat()) + val params = scrollablePreview.layoutParams + + params.width = (scale * scrollableRect.width()).toInt() + params.height = (scale * scrollableRect.height()).toInt() + val matrix = Matrix() + matrix.setScale(scale, scale) + matrix.postTranslate(-scrollableRect.left * scale, -scrollableRect.top * scale) + + scrollablePreview.translationX = + (scale * + if (scrollablePreview.layoutDirection == LayoutDirection.LTR) scrollableRect.left + else scrollableRect.right - (scrollablePreview.parent as View).width) + scrollablePreview.translationY = scale * scrollableRect.top + scrollablePreview.setImageMatrix(matrix) + scrollablePreview.setImageBitmap(bitmap) + scrollablePreview.setVisibility(View.VISIBLE) + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt index 81bc281191ed..3f99bc4597cb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt @@ -17,6 +17,7 @@ package com.android.systemui.screenshot.ui.viewmodel import android.graphics.Bitmap +import android.graphics.Rect import android.graphics.drawable.Drawable import android.util.Log import android.view.accessibility.AccessibilityManager @@ -26,6 +27,8 @@ import kotlinx.coroutines.flow.StateFlow class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager) { private val _preview = MutableStateFlow<Bitmap?>(null) val preview: StateFlow<Bitmap?> = _preview + private val _scrollingScrim = MutableStateFlow<Bitmap?>(null) + val scrollingScrim: StateFlow<Bitmap?> = _scrollingScrim private val _badge = MutableStateFlow<Drawable?>(null) val badge: StateFlow<Drawable?> = _badge private val _previewAction = MutableStateFlow<(() -> Unit)?>(null) @@ -35,6 +38,10 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager private val _animationState = MutableStateFlow(AnimationState.NOT_STARTED) val animationState: StateFlow<AnimationState> = _animationState + private val _isAnimating = MutableStateFlow(false) + val isAnimating: StateFlow<Boolean> = _isAnimating + private val _scrollableRect = MutableStateFlow<Rect?>(null) + val scrollableRect: StateFlow<Rect?> = _scrollableRect val showDismissButton: Boolean get() = accessibilityManager.isEnabled @@ -42,6 +49,10 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager _preview.value = bitmap } + fun setScrollingScrimBitmap(bitmap: Bitmap?) { + _scrollingScrim.value = bitmap + } + fun setScreenshotBadge(badge: Drawable?) { _badge.value = badge } @@ -114,12 +125,23 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager _animationState.value = state } + fun setIsAnimating(isAnimating: Boolean) { + _isAnimating.value = isAnimating + } + + fun setScrollableRect(rect: Rect?) { + _scrollableRect.value = rect + } + fun reset() { _preview.value = null + _scrollingScrim.value = null _badge.value = null _previewAction.value = null _actions.value = listOf() _animationState.value = AnimationState.NOT_STARTED + _isAnimating.value = false + _scrollableRect.value = null } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt index 5e7d8fb5df02..a10d81f86d8e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt @@ -21,7 +21,6 @@ import android.content.Intent import android.os.Bundle import android.os.UserHandle import android.testing.AndroidTestingRunner -import android.view.View import android.view.Window import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -49,7 +48,7 @@ class ActionExecutorTest : SysuiTestCase() { private val intentExecutor = mock<ActionIntentExecutor>() private val window = mock<Window>() - private val view = mock<View>() + private val viewProxy = mock<ScreenshotShelfViewProxy>() private val onDismiss = mock<(() -> Unit)>() private val pendingIntent = mock<PendingIntent>() @@ -70,16 +69,16 @@ class ActionExecutorTest : SysuiTestCase() { } @Test - fun sendPendingIntent_dismisses() = runTest { + fun sendPendingIntent_requestsDismissal() = runTest { actionExecutor = createActionExecutor() actionExecutor.sendPendingIntent(pendingIntent) verify(pendingIntent).send(any(Bundle::class.java)) - verify(onDismiss).invoke() + verify(viewProxy).requestDismissal(null) } private fun createActionExecutor(): ActionExecutor { - return ActionExecutor(intentExecutor, testScope, window, view, onDismiss) + return ActionExecutor(intentExecutor, testScope, window, viewProxy, onDismiss) } } |