Merge "Use distinct value for BAL_BLOCK" into main
diff --git a/packages/SystemUI/res/layout/screenshot.xml b/packages/SystemUI/res/layout/screenshot.xml
deleted file mode 100644
index c134c8e..0000000
--- 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/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
index 1868b4a..54e0319 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
@@ -40,7 +40,7 @@
private val intentExecutor: ActionIntentExecutor,
@Application private val applicationScope: CoroutineScope,
@Assisted val window: Window,
- @Assisted val viewProxy: ScreenshotViewProxy,
+ @Assisted val viewProxy: ScreenshotShelfViewProxy,
@Assisted val finishDismiss: () -> Unit,
) {
@@ -109,7 +109,7 @@
interface Factory {
fun create(
window: Window,
- viewProxy: ScreenshotViewProxy,
+ viewProxy: ScreenshotShelfViewProxy,
finishDismiss: (() -> Unit)
): ActionExecutor
}
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 3d024a6..0000000
--- 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 5960462..474afa8b 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 @@
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 @@
}
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 0cbf8f9..7739009 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -191,7 +191,7 @@
private final WindowContext mContext;
private final FeatureFlags mFlags;
- private final ScreenshotViewProxy mViewProxy;
+ private final ScreenshotShelfViewProxy mViewProxy;
private final ScreenshotNotificationsController mNotificationsController;
private final ScreenshotSmartActions mScreenshotSmartActions;
private final UiEventLogger mUiEventLogger;
@@ -246,7 +246,7 @@
Context context,
WindowManager windowManager,
FeatureFlags flags,
- ScreenshotViewProxy.Factory viewProxyFactory,
+ ScreenshotShelfViewProxy.Factory viewProxyFactory,
ScreenshotSmartActions screenshotSmartActions,
ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory,
UiEventLogger uiEventLogger,
@@ -477,9 +477,6 @@
}
mViewProxy.setPackageName(mPackageName);
-
- mViewProxy.updateOrientation(
- mWindowManager.getCurrentWindowMetrics().getWindowInsets());
}
/**
@@ -528,7 +525,7 @@
}
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 @@
}
@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 @@
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 1b5fa34..50215af 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 @@
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.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
@@ -70,13 +68,23 @@
private val thumbnailObserver: ThumbnailObserver,
@Assisted private val context: Context,
@Assisted private val displayId: Int
-) : ScreenshotViewProxy {
- override val view: ScreenshotShelfView =
+) {
+
+ interface ScreenshotViewCallback {
+ fun onUserInteraction()
+
+ fun onDismiss()
+
+ /** DOWN motion event was observed outside of the touchable areas of this view. */
+ fun onTouchOutside()
+ }
+
+ 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 screenshot: ScreenshotData? = null
+ val screenshotPreview: View
+ var packageName: String = ""
+ var callbacks: ScreenshotViewCallback? = null
+ var screenshot: ScreenshotData? = null
set(value) {
value?.let {
viewModel.setScreenshotBitmap(it.bitmap)
@@ -92,10 +100,11 @@
field = value
}
- override val isAttachedToWindow
+ val isAttachedToWindow
get() = view.isAttachedToWindow
- override var isDismissing = false
- override var isPendingSharedTransition = false
+
+ var isDismissing = false
+ var isPendingSharedTransition = false
private val animationController = ScreenshotAnimationController(view, viewModel)
private var inputMonitor: InputMonitorCompat? = null
@@ -136,17 +145,17 @@
)
}
- override fun reset() {
+ fun reset() {
animationController.cancel()
isPendingSharedTransition = false
viewModel.reset()
}
- override fun updateInsets(insets: WindowInsets) {
+
+ fun updateInsets(insets: WindowInsets) {
view.updateInsets(insets)
}
- override fun updateOrientation(insets: WindowInsets) {}
- override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator {
+ fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator {
val entrance =
animationController.getEntranceAnimation(screenRect, showFlash) {
viewModel.setAnimationState(AnimationState.ENTRANCE_REVEAL)
@@ -164,11 +173,7 @@
return entrance
}
- override fun addQuickShareChip(quickShareAction: Notification.Action) {}
-
- override fun setChipIntents(imageData: SavedImageData) {}
-
- override fun requestDismissal(event: ScreenshotEvent?) {
+ fun requestDismissal(event: ScreenshotEvent?) {
requestDismissal(event, null)
}
@@ -187,6 +192,7 @@
override fun onAnimationStart(animator: Animator) {
isDismissing = true
}
+
override fun onAnimationEnd(animator: Animator) {
isDismissing = false
callbacks?.onDismiss()
@@ -196,11 +202,7 @@
animator.start()
}
- override fun showScrollChip(packageName: String, onClick: Runnable) {}
-
- override fun hideScrollChip() {}
-
- override fun prepareScrollingTransition(
+ fun prepareScrollingTransition(
response: ScrollCaptureResponse,
screenBitmap: Bitmap, // unused
newScreenshot: Bitmap,
@@ -228,7 +230,7 @@
return r
}
- override fun startLongScreenshotTransition(
+ fun startLongScreenshotTransition(
transitionDestination: Rect,
onTransitionEnd: Runnable,
longScreenshot: ScrollCaptureController.LongScreenshot,
@@ -243,27 +245,27 @@
transitionAnimation.start()
}
- override fun restoreNonScrollingUi() {
+ fun restoreNonScrollingUi() {
viewModel.setScrollableRect(null)
viewModel.setScrollingScrimBitmap(null)
animationController.restoreUI()
callbacks?.onUserInteraction() // reset the timeout
}
- override fun stopInputListening() {
+ fun stopInputListening() {
inputMonitor?.dispose()
inputMonitor = null
inputEventReceiver?.dispose()
inputEventReceiver = null
}
- override fun requestFocus() {
+ fun requestFocus() {
view.requestFocus()
}
- override fun announceForAccessibility(string: String) = view.announceForAccessibility(string)
+ fun announceForAccessibility(string: String) = view.announceForAccessibility(string)
- override fun prepareEntranceAnimation(runnable: Runnable) {
+ fun prepareEntranceAnimation(runnable: Runnable) {
view.viewTreeObserver.addOnPreDrawListener(
object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
@@ -276,7 +278,7 @@
)
}
- override fun fadeForSharedTransition() {
+ fun fadeForSharedTransition() {
animationController.fadeForSharedTransition()
}
@@ -349,7 +351,7 @@
}
@AssistedFactory
- interface Factory : ScreenshotViewProxy.Factory {
- override fun getProxy(context: Context, displayId: Int): ScreenshotShelfViewProxy
+ interface Factory {
+ fun getProxy(context: Context, displayId: Int): ScreenshotShelfViewProxy
}
}
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 59e38a8..0000000
--- 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
deleted file mode 100644
index df93a5e..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
+++ /dev/null
@@ -1,76 +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.view.ScrollCaptureResponse
-import android.view.View
-import android.view.ViewGroup
-import android.view.WindowInsets
-import com.android.systemui.screenshot.scroll.ScrollCaptureController
-
-/** Abstraction of the surface between ScreenshotController and ScreenshotView */
-interface ScreenshotViewProxy {
- val view: ViewGroup
- val screenshotPreview: View
-
- var packageName: String
- var callbacks: ScreenshotView.ScreenshotViewCallback?
- var screenshot: ScreenshotData?
-
- val isAttachedToWindow: Boolean
- val isDismissing: Boolean
- 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,
- newScreenshot: Bitmap,
- 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 {
- fun getProxy(context: Context, displayId: Int): ScreenshotViewProxy
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 56ba1af4..682f848 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -24,12 +24,10 @@
import com.android.systemui.screenshot.ImageCaptureImpl;
import com.android.systemui.screenshot.ScreenshotPolicy;
import com.android.systemui.screenshot.ScreenshotPolicyImpl;
-import com.android.systemui.screenshot.ScreenshotShelfViewProxy;
import com.android.systemui.screenshot.ScreenshotSoundController;
import com.android.systemui.screenshot.ScreenshotSoundControllerImpl;
import com.android.systemui.screenshot.ScreenshotSoundProvider;
import com.android.systemui.screenshot.ScreenshotSoundProviderImpl;
-import com.android.systemui.screenshot.ScreenshotViewProxy;
import com.android.systemui.screenshot.TakeScreenshotExecutor;
import com.android.systemui.screenshot.TakeScreenshotExecutorImpl;
import com.android.systemui.screenshot.TakeScreenshotService;
@@ -92,8 +90,4 @@
AccessibilityManager accessibilityManager) {
return new ScreenshotViewModel(accessibilityManager);
}
-
- @Binds
- abstract ScreenshotViewProxy.Factory bindScreenshotViewProxyFactory(
- ScreenshotShelfViewProxy.Factory shelfScreenshotViewProxyFactory);
}
diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
index ec5d96d..4a82057 100644
--- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java
+++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
@@ -106,11 +106,11 @@
/**
* Potentially log a zen mode change if the provided config and policy changes warrant it.
*
- * @param prevInfo ZenModeInfo (zen mode setting, config, policy) prior to this change
- * @param newInfo ZenModeInfo after this change takes effect
- * @param callingUid the calling UID associated with the change; may be used to attribute the
- * change to a particular package or determine if this is a user action
- * @param origin The origin of the Zen change.
+ * @param prevInfo ZenModeInfo (zen mode setting, config, policy) prior to this change
+ * @param newInfo ZenModeInfo after this change takes effect
+ * @param callingUid the calling UID associated with the change; may be used to attribute the
+ * change to a particular package or determine if this is a user action
+ * @param origin The origin of the Zen change.
*/
public final void maybeLogZenChange(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid,
@ConfigChangeOrigin int origin) {
@@ -127,6 +127,9 @@
/**
* Reassign callingUid in mChangeState if we have more specific information that warrants it
* (for instance, if the change is automatic and due to an automatic rule change).
+ *
+ * <p>When Flags.modesUi() is enabled, we reassign the calling UID to the package UID in all
+ * changes whose source is not system or system UI, as long as there is only one rule changed.
*/
private void maybeReassignCallingUid() {
int userId = Process.INVALID_UID;
@@ -145,12 +148,23 @@
userId = mChangeState.mNewConfig.user; // mNewConfig must not be null if enabler exists
}
- // The conditions where we should consider reassigning UID for an automatic rule change:
+ // The conditions where we should consider reassigning UID for an automatic rule change
+ // (pre-modes_ui):
// - we've determined it's not a user action
// - our current best guess is that the calling uid is system/sysui
+ // When Flags.modesUi() is true, we get the package UID for the changed rule, as long as:
+ // - the change does not originate from the system based on change origin
+ // - there is only one rule changed
if (mChangeState.getChangedRuleType() == RULE_TYPE_AUTOMATIC) {
- if (mChangeState.getIsUserAction() || !mChangeState.isFromSystemOrSystemUi()) {
- return;
+ if (Flags.modesUi()) {
+ // ignore anything whose origin is system
+ if (mChangeState.isFromSystemOrSystemUi()) {
+ return;
+ }
+ } else {
+ if (mChangeState.getIsUserAction() || !mChangeState.isFromSystemOrSystemUi()) {
+ return;
+ }
}
// Only try to get the package UID if there's exactly one changed automatic rule. If
@@ -202,7 +216,8 @@
/* int32 package_uid = 7 */ mChangeState.getPackageUid(),
/* DNDPolicyProto current_policy = 8 */ mChangeState.getDNDPolicyProto(),
/* bool are_channels_bypassing = 9 */ mChangeState.getAreChannelsBypassing(),
- /* ActiveRuleType active_rule_types = 10 */ mChangeState.getActiveRuleTypes());
+ /* ActiveRuleType active_rule_types = 10 */ mChangeState.getActiveRuleTypes(),
+ /* ChangeOrigin change_origin = 11 */ mChangeState.getChangeOrigin());
}
/**
@@ -235,7 +250,8 @@
ZenModeConfig mPrevConfig, mNewConfig;
NotificationManager.Policy mPrevPolicy, mNewPolicy;
int mCallingUid = Process.INVALID_UID;
- @ConfigChangeOrigin int mOrigin = ZenModeConfig.UPDATE_ORIGIN_UNKNOWN;
+ @ConfigChangeOrigin
+ int mOrigin = ZenModeConfig.UPDATE_ORIGIN_UNKNOWN;
private void init(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid,
@ConfigChangeOrigin int origin) {
@@ -388,7 +404,8 @@
* rules available.
*/
@SuppressLint("WrongConstant") // special case for log-only type on manual rule
- @NonNull List<ZenRule> activeRulesList(ZenModeConfig config) {
+ @NonNull
+ List<ZenRule> activeRulesList(ZenModeConfig config) {
ArrayList<ZenRule> rules = new ArrayList<>();
if (config == null) {
return rules;
@@ -548,6 +565,17 @@
}
/**
+ * Get the config change origin associated with this change, which is stored in mOrigin.
+ * Only useable if modes_ui is true.
+ */
+ int getChangeOrigin() {
+ if (Flags.modesUi()) {
+ return mOrigin;
+ }
+ return 0;
+ }
+
+ /**
* Convert the new policy to a DNDPolicyProto format for output in logs.
*
* <p>If {@code mNewZenMode} is {@code ZEN_MODE_OFF} (which can mean either no rules
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java
index ff1308c..1c8cb8f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeEventLoggerFake.java
@@ -137,4 +137,9 @@
checkInRange(i);
return mChanges.get(i).getActiveRuleTypes();
}
+
+ public int getChangeOrigin(int i) throws IllegalArgumentException {
+ checkInRange(i);
+ return mChanges.get(i).getChangeOrigin();
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 7bb633e..4bbbc2b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -818,7 +818,7 @@
// 1. Current ringer is normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
// Set zen to priority-only with all notification sounds muted (so ringer will be muted)
- Policy totalSilence = new Policy(0,0,0);
+ Policy totalSilence = new Policy(0, 0, 0);
mZenModeHelper.setNotificationPolicy(totalSilence, UPDATE_ORIGIN_APP, 1);
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
@@ -873,7 +873,7 @@
// even when ringer is muted (since all ringer sounds cannot bypass DND),
// system stream is still affected by ringer mode
- mZenModeHelper.setNotificationPolicy(new Policy(0,0,0), UPDATE_ORIGIN_APP, 1);
+ mZenModeHelper.setNotificationPolicy(new Policy(0, 0, 0), UPDATE_ORIGIN_APP, 1);
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
UPDATE_ORIGIN_APP, "test", "caller", 1);
ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerNotMuted =
@@ -1065,9 +1065,10 @@
@Test
public void testParcelConfig() {
mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS
- | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
- | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED,
- PRIORITY_SENDERS_STARRED, 0, CONVERSATION_SENDERS_ANYONE), UPDATE_ORIGIN_UNKNOWN,
+ | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
+ | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED,
+ PRIORITY_SENDERS_STARRED, 0, CONVERSATION_SENDERS_ANYONE),
+ UPDATE_ORIGIN_UNKNOWN,
1);
mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
@@ -1085,13 +1086,14 @@
@Test
public void testWriteXml() throws Exception {
mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS
- | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
- | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED,
- PRIORITY_SENDERS_STARRED, SUPPRESSED_EFFECT_BADGE, CONVERSATION_SENDERS_ANYONE),
+ | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
+ | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED,
+ PRIORITY_SENDERS_STARRED, SUPPRESSED_EFFECT_BADGE,
+ CONVERSATION_SENDERS_ANYONE),
UPDATE_ORIGIN_UNKNOWN, 1);
mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
- .setShouldDimWallpaper(true)
- .setShouldDisplayGrayscale(true)
+ .setShouldDimWallpaper(true)
+ .setShouldDisplayGrayscale(true)
.build(), UPDATE_ORIGIN_UNKNOWN, "test", 1);
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
UPDATE_ORIGIN_UNKNOWN, "test", "me", 1);
@@ -2210,7 +2212,7 @@
customDefaultRule.name = "Schedule Default Rule";
customDefaultRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
ScheduleInfo scheduleInfo = new ScheduleInfo();
- scheduleInfo.days = new int[] { Calendar.SUNDAY };
+ scheduleInfo.days = new int[]{Calendar.SUNDAY};
scheduleInfo.startHour = 18;
scheduleInfo.endHour = 19;
customDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(scheduleInfo);
@@ -3027,7 +3029,7 @@
// Turn zen mode on (to important_interruptions)
// Need to additionally call the looper in order to finish the post-apply-config process
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
- Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null,
+ Flags.modesApi() ? UPDATE_ORIGIN_USER : UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null,
Process.SYSTEM_UID);
// Now turn zen mode off, but via a different package UID -- this should get registered as
@@ -3060,6 +3062,9 @@
assertTrue(mZenModeEventLogger.getIsUserAction(0));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(0));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0));
+ // change origin should be populated only under modes_ui
+ assertThat(mZenModeEventLogger.getChangeOrigin(0)).isEqualTo(
+ (Flags.modesApi() && Flags.modesUi()) ? UPDATE_ORIGIN_USER : 0);
// and from turning zen mode off:
// - event ID: DND_TURNED_OFF
@@ -3082,6 +3087,8 @@
} else {
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
}
+ assertThat(mZenModeEventLogger.getChangeOrigin(1)).isEqualTo(
+ Flags.modesUi() ? UPDATE_ORIGIN_APP : 0);
}
@Test
@@ -3098,17 +3105,21 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
+ UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID);
// Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
+ // Note that pre-modes_ui, this event serves as a test that automatic changes to an app's
+ // that look like they're coming from the system are attributed to the app, but when
+ // modes_ui is true, we opt to trust the provided change origin.
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
+ Flags.modesUi() ? UPDATE_ORIGIN_APP : UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ CUSTOM_PKG_UID);
// Event 2: "User" turns off the automatic rule (sets it to not enabled)
zenRule.setEnabled(false);
mZenModeHelper.updateAutomaticZenRule(id, zenRule,
- Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+ Flags.modesApi() ? UPDATE_ORIGIN_USER : UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
Process.SYSTEM_UID);
AutomaticZenRule systemRule = new AutomaticZenRule("systemRule",
@@ -3118,7 +3129,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule,
- Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test",
+ Flags.modesApi() ? UPDATE_ORIGIN_USER : UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test",
Process.SYSTEM_UID);
// Event 3: turn on the system rule
@@ -3128,7 +3139,7 @@
// Event 4: "User" deletes the rule
mZenModeHelper.removeAutomaticZenRule(systemId,
- Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+ Flags.modesApi() ? UPDATE_ORIGIN_USER : UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
Process.SYSTEM_UID);
// In total, this represents 4 events
assertEquals(4, mZenModeEventLogger.numLoggedChanges());
@@ -3151,9 +3162,13 @@
assertFalse(mZenModeEventLogger.getIsUserAction(0));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(0));
checkDndProtoMatchesDefaultZenConfig(mZenModeEventLogger.getPolicyProto(0));
+ assertThat(mZenModeEventLogger.getChangeOrigin(0)).isEqualTo(
+ Flags.modesUi() ? UPDATE_ORIGIN_APP : 0);
// When the automatic rule is disabled, this should turn off zen mode and also count as a
// user action. We don't care what the consolidated policy is when DND turns off.
+ // When modes_ui is true, this event should look like a user action attributed to the
+ // specific app.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
mZenModeEventLogger.getEventId(1));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getPrevZenMode(1));
@@ -3161,12 +3176,15 @@
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(1));
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertTrue(mZenModeEventLogger.getIsUserAction(1));
- assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(1));
+ assertThat(mZenModeEventLogger.getPackageUid(1)).isEqualTo(
+ Flags.modesUi() ? CUSTOM_PKG_UID : Process.SYSTEM_UID);
if (Flags.modesApi()) {
assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
} else {
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
}
+ assertThat(mZenModeEventLogger.getChangeOrigin(1)).isEqualTo(
+ Flags.modesUi() ? UPDATE_ORIGIN_USER : 0);
// When the system rule is enabled, this counts as an automatic action that comes from the
// system and turns on DND
@@ -3176,6 +3194,8 @@
assertEquals(1, mZenModeEventLogger.getNumRulesActive(2));
assertFalse(mZenModeEventLogger.getIsUserAction(2));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(2));
+ assertThat(mZenModeEventLogger.getChangeOrigin(2)).isEqualTo(
+ Flags.modesUi() ? UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI : 0);
// When the system rule is deleted, we consider this a user action that turns DND off
// (again)
@@ -3185,6 +3205,8 @@
assertEquals(0, mZenModeEventLogger.getNumRulesActive(3));
assertTrue(mZenModeEventLogger.getIsUserAction(3));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(3));
+ assertThat(mZenModeEventLogger.getChangeOrigin(3)).isEqualTo(
+ Flags.modesUi() ? UPDATE_ORIGIN_USER : 0);
}
@Test
@@ -3238,6 +3260,8 @@
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
assertTrue(mZenModeEventLogger.getIsUserAction(0));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(0));
+ assertThat(mZenModeEventLogger.getChangeOrigin(0)).isEqualTo(
+ Flags.modesUi() ? UPDATE_ORIGIN_USER : 0);
// Automatic rule turned off automatically by app:
// - event ID: DND_TURNED_OFF
@@ -3249,6 +3273,8 @@
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertFalse(mZenModeEventLogger.getIsUserAction(1));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(1));
+ assertThat(mZenModeEventLogger.getChangeOrigin(1)).isEqualTo(
+ Flags.modesUi() ? UPDATE_ORIGIN_APP : 0);
// Automatic rule turned on automatically by app:
// - event ID: DND_TURNED_ON
@@ -3261,6 +3287,8 @@
assertEquals(1, mZenModeEventLogger.getNumRulesActive(2));
assertFalse(mZenModeEventLogger.getIsUserAction(2));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(2));
+ assertThat(mZenModeEventLogger.getChangeOrigin(2)).isEqualTo(
+ Flags.modesUi() ? UPDATE_ORIGIN_APP : 0);
// Automatic rule turned off automatically by the user:
// - event ID: DND_TURNED_ON
@@ -3272,6 +3300,8 @@
assertEquals(0, mZenModeEventLogger.getNumRulesActive(3));
assertTrue(mZenModeEventLogger.getIsUserAction(3));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(3));
+ assertThat(mZenModeEventLogger.getChangeOrigin(3)).isEqualTo(
+ Flags.modesUi() ? UPDATE_ORIGIN_USER : 0);
}
@Test
@@ -3335,7 +3365,7 @@
AutomaticZenRule zenRule = new AutomaticZenRule("name",
null,
- new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ new ComponentName("android", "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
@@ -3345,7 +3375,7 @@
// Rule 2, same as rule 1
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
null,
- new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ new ComponentName("android", "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
@@ -3395,7 +3425,7 @@
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
assertFalse(mZenModeEventLogger.getIsUserAction(0));
- assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(0));
+ assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(0));
checkDndProtoMatchesDefaultZenConfig(mZenModeEventLogger.getPolicyProto(0));
// Event 2: rule 2 turns on. This should not change anything about the policy, so the only
@@ -3404,7 +3434,7 @@
mZenModeEventLogger.getEventId(1));
assertEquals(2, mZenModeEventLogger.getNumRulesActive(1));
assertFalse(mZenModeEventLogger.getIsUserAction(1));
- assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(1));
+ assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(1));
checkDndProtoMatchesDefaultZenConfig(mZenModeEventLogger.getPolicyProto(1));
// Event 3: rule 3 turns on. This should trigger a policy change, and be classified as such,
@@ -3482,9 +3512,11 @@
// Turn on rule 1; call looks like it's from the system. Because setting a condition is
// typically an automatic (non-user-initiated) action, expect the calling UID to be
// re-evaluated to the one associated with CUSTOM_PKG_NAME.
+ // When modes_ui is true: we expect the change origin to be the source of truth.
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
+ Flags.modesUi() ? UPDATE_ORIGIN_APP : UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ Process.SYSTEM_UID);
// Second: turn on rule 2. This is a system-owned rule and the UID should not be modified
// (nor even looked up; the mock PackageManager won't handle "android" as input).
@@ -3493,7 +3525,7 @@
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Disable rule 1. Because this looks like a user action, the UID should not be modified
- // from the system-provided one.
+ // from the system-provided one unless modes_ui is true.
zenRule.setEnabled(false);
mZenModeHelper.updateAutomaticZenRule(id, zenRule,
UPDATE_ORIGIN_USER, "", Process.SYSTEM_UID);
@@ -3504,6 +3536,7 @@
// Change rule 2's condition, but from some other UID. Since it doesn't look like it's from
// the system, we keep the UID info.
+ // Note that this probably shouldn't be able to occur in real scenarios.
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_FALSE),
UPDATE_ORIGIN_APP, 12345);
@@ -3528,11 +3561,13 @@
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(1));
// Third event: disable rule 1. This looks like a user action so UID should be left alone.
+ // When modes_ui is true, we assign log this user action with the app that owns the rule.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId(),
mZenModeEventLogger.getEventId(2));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(2));
assertTrue(mZenModeEventLogger.getIsUserAction(2));
- assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(2));
+ assertThat(mZenModeEventLogger.getPackageUid(2)).isEqualTo(
+ Flags.modesUi() ? CUSTOM_PKG_UID : Process.SYSTEM_UID);
// Fourth event: turns on manual mode. Doesn't change effective policy so this is just a
// change in active rules. Confirm that the package UID is left unchanged.
@@ -6202,7 +6237,7 @@
public void setManualZenRuleDeviceEffects_noPreexistingMode() {
ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
- .build();
+ .build();
mZenModeHelper.setManualZenRuleDeviceEffects(effects, UPDATE_ORIGIN_USER, "settings", 1000);
assertThat(mZenModeHelper.getConfig().manualRule).isNotNull();
@@ -6339,21 +6374,21 @@
private static final Correspondence<ZenRule, ZenRule> IGNORE_METADATA =
Correspondence.transforming(zr -> {
- Parcel p = Parcel.obtain();
- try {
- zr.writeToParcel(p, 0);
- p.setDataPosition(0);
- ZenRule copy = new ZenRule(p);
- copy.creationTime = 0;
- copy.userModifiedFields = 0;
- copy.zenPolicyUserModifiedFields = 0;
- copy.zenDeviceEffectsUserModifiedFields = 0;
- return copy;
- } finally {
- p.recycle();
- }
- },
- "Ignoring timestamp and userModifiedFields");
+ Parcel p = Parcel.obtain();
+ try {
+ zr.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ ZenRule copy = new ZenRule(p);
+ copy.creationTime = 0;
+ copy.userModifiedFields = 0;
+ copy.zenPolicyUserModifiedFields = 0;
+ copy.zenDeviceEffectsUserModifiedFields = 0;
+ return copy;
+ } finally {
+ p.recycle();
+ }
+ },
+ "Ignoring timestamp and userModifiedFields");
private ZenRule expectedImplicitRule(String ownerPkg, int zenMode, ZenPolicy policy,
@Nullable Boolean conditionActive) {