summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/bootanimation/BootAnimation.cpp2
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/provider/Settings.java3
-rw-r--r--core/java/android/security/flags.aconfig7
-rw-r--r--core/java/android/service/notification/Adjustment.java2
-rw-r--r--core/jni/android_media_ImageWriter.cpp2
-rw-r--r--core/jni/android_view_Surface.cpp8
-rw-r--r--core/jni/android_view_SurfaceSession.cpp8
-rw-r--r--core/jni/android_view_TextureView.cpp2
-rw-r--r--core/jni/com_google_android_gles_jni_EGLImpl.cpp2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java34
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt36
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt50
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt34
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java2
-rw-r--r--native/android/surface_control.cpp2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/common/domain/interactor/SysUIStatePerDisplayInteractorTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt137
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt168
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt100
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt90
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelTest.kt88
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt)122
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml26
-rw-r--r--packages/SystemUI/res/drawable/qs_media_rec_scrim.xml25
-rw-r--r--packages/SystemUI/res/layout/media_recommendation_view.xml90
-rw-r--r--packages/SystemUI/res/layout/media_recommendations.xml75
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/dimens.xml5
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml13
-rw-r--r--packages/SystemUI/res/values/styles.xml51
-rw-r--r--packages/SystemUI/res/xml/media_recommendations_collapsed.xml64
-rw-r--r--packages/SystemUI/res/xml/media_recommendations_expanded.xml71
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt156
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt469
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt196
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java494
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt238
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SysUIStateChange.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SysUiState.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt151
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt159
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt835
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt100
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractorKosmos.kt37
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelKosmos.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt3
-rw-r--r--packages/Vcn/service-b/Android.bp5
-rw-r--r--packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java20
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java36
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java8
-rw-r--r--services/core/java/com/android/server/input/InputGestureManager.java5
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java151
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java8
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java2
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java69
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java59
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java2
-rw-r--r--telecomm/java/android/telecom/Call.java28
-rw-r--r--telecomm/java/android/telecom/ParcelableCall.java32
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt8
-rw-r--r--tools/fonts/Android.bp5
-rw-r--r--tools/lint/fix/Android.bp5
-rw-r--r--tools/lint/global/integration_tests/Android.bp5
87 files changed, 911 insertions, 4385 deletions
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 844e52c3ecf2..b0070c5faa36 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -207,7 +207,7 @@ BootAnimation::BootAnimation(sp<Callbacks> callbacks)
: Thread(false), mLooper(new Looper(false)), mClockEnabled(true), mTimeIsAccurate(false),
mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
ATRACE_CALL();
- mSession = new SurfaceComposerClient();
+ mSession = sp<SurfaceComposerClient>::make();
std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
if (powerCtl.empty()) {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 00ec48b79541..d651010b641a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3031,6 +3031,7 @@ package android.provider {
field public static final String DISABLED_PRINT_SERVICES = "disabled_print_services";
field @Deprecated public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
+ field public static final String GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled";
field public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations";
field public static final String NOTIFICATION_BADGING = "notification_badging";
field public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 60e57b588be0..b97c9b5e83f2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10583,6 +10583,9 @@ public final class Settings {
*
* @hide
*/
+ @TestApi
+ @Readable
+ @SuppressLint({"UnflaggedApi", "NoSettingsProvider"}) // @TestApi purely for CTS support.
public static final String GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled";
/**
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 3a3ea189b227..7013f7d705f8 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -52,13 +52,6 @@ flag {
}
flag {
- name: "deprecate_fsv_sig"
- namespace: "hardware_backed_security"
- description: "Feature flag for deprecating .fsv_sig"
- bug: "277916185"
-}
-
-flag {
name: "extend_vb_chain_to_updated_apk"
namespace: "hardware_backed_security"
description: "Use v4 signature and fs-verity to chain verification of allowlisted APKs to Verified Boot"
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 505db30ca719..772fd968e507 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -225,7 +225,7 @@ public final class Adjustment implements Parcelable {
public static final int TYPE_CONTENT_RECOMMENDATION = 4;
/**
- * Data type: String, a summarization of the text of the notification, or, if provided for
+ * Data type: CharSequence, a summarization of the text of the notification, or, if provided for
* a group summary, a summarization of the text of all of the notificatrions in the group.
* Send this key with a null value to remove an existing summarization.
*/
diff --git a/core/jni/android_media_ImageWriter.cpp b/core/jni/android_media_ImageWriter.cpp
index 1357dd842ff1..8e58922bd9df 100644
--- a/core/jni/android_media_ImageWriter.cpp
+++ b/core/jni/android_media_ImageWriter.cpp
@@ -399,7 +399,7 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje
}
sp<JNIImageWriterContext> ctx(new JNIImageWriterContext(env, weakThiz, clazz));
- sp<Surface> producer = new Surface(bufferProducer, /*controlledByApp*/false);
+ sp<Surface> producer = sp<Surface>::make(bufferProducer, /*controlledByApp*/ false);
ctx->setProducer(producer);
/**
* NATIVE_WINDOW_API_CPU isn't a good choice here, as it makes the bufferQueue not connectable
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 312c2067d396..783daec82b9e 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -139,7 +139,7 @@ jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env,
return NULL;
}
- sp<Surface> surface(new Surface(bufferProducer, true));
+ sp<Surface> surface = sp<Surface>::make(bufferProducer, true);
return android_view_Surface_createFromSurface(env, surface);
}
@@ -161,7 +161,7 @@ static jlong nativeCreateFromSurfaceTexture(JNIEnv* env, jclass clazz,
return 0;
}
- sp<Surface> surface(new Surface(producer, true));
+ sp<Surface> surface = sp<Surface>::make(producer, true);
if (surface == NULL) {
jniThrowException(env, OutOfResourcesException, NULL);
return 0;
@@ -358,8 +358,8 @@ static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz,
sp<Surface> sur;
if (surfaceShim.graphicBufferProducer != nullptr) {
// we have a new IGraphicBufferProducer, create a new Surface for it
- sur = new Surface(surfaceShim.graphicBufferProducer, true,
- surfaceShim.surfaceControlHandle);
+ sur = sp<Surface>::make(surfaceShim.graphicBufferProducer, true,
+ surfaceShim.surfaceControlHandle);
// and keep a reference before passing to java
sur->incStrong(&sRefBaseOwner);
}
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 6ad109e80752..4f2ab09252c8 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -42,14 +42,14 @@ sp<SurfaceComposerClient> android_view_SurfaceSession_getClient(
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
- SurfaceComposerClient* client = new SurfaceComposerClient();
- client->incStrong((void*)nativeCreate);
- return reinterpret_cast<jlong>(client);
+ // Will be deleted via decStrong() in nativeDestroy.
+ auto client = sp<SurfaceComposerClient>::make();
+ return reinterpret_cast<jlong>(client.release());
}
static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
- client->decStrong((void*)nativeCreate);
+ client->decStrong((void*)client);
}
static const JNINativeMethod gMethods[] = {
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 21fe1f020b29..f71878ccff08 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -85,7 +85,7 @@ static void android_view_TextureView_createNativeWindow(JNIEnv* env, jobject tex
jobject surface) {
sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, surface));
- sp<ANativeWindow> window = new Surface(producer, true);
+ sp<ANativeWindow> window = sp<Surface>::make(producer, true);
window->incStrong((void*)android_view_TextureView_createNativeWindow);
SET_LONG(textureView, gTextureViewClassInfo.nativeWindow, jlong(window.get()));
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 75330be2624d..1b3b14da49d5 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -300,7 +300,7 @@ not_valid_surface:
}
sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(_env, native_window));
- window = new Surface(producer, true);
+ window = sp<Surface>::make(producer, true);
if (window == NULL)
goto not_valid_surface;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 56de48daf810..70539902f651 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -103,6 +103,10 @@ public class DesktopModeVisualIndicator {
return null;
}
}
+
+ private static boolean isDragToDesktopStartState(DragStartState startState) {
+ return startState == FROM_FULLSCREEN || startState == FROM_SPLIT;
+ }
}
private final VisualIndicatorViewContainer mVisualIndicatorViewContainer;
@@ -125,7 +129,12 @@ public class DesktopModeVisualIndicator {
@Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider,
SnapEventHandler snapEventHandler) {
SurfaceControl.Builder builder = new SurfaceControl.Builder();
- taskDisplayAreaOrganizer.attachToDisplayArea(taskInfo.displayId, builder);
+ if (!DragStartState.isDragToDesktopStartState(dragStartState)
+ || !DesktopModeFlags.ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX.isTrue()) {
+ // In the DragToDesktop transition we attach the indicator to the transition root once
+ // that is available - for all other cases attach the indicator here.
+ taskDisplayAreaOrganizer.attachToDisplayArea(taskInfo.displayId, builder);
+ }
mVisualIndicatorViewContainer = new VisualIndicatorViewContainer(
DesktopModeFlags.ENABLE_DESKTOP_INDICATOR_IN_SEPARATE_THREAD_BUGFIX.isTrue()
? desktopExecutor : mainExecutor,
@@ -159,6 +168,18 @@ public class DesktopModeVisualIndicator {
mVisualIndicatorViewContainer.releaseVisualIndicator();
}
+ /** Reparent the visual indicator to {@code newParent}. */
+ void reparentLeash(SurfaceControl.Transaction t, SurfaceControl newParent) {
+ mVisualIndicatorViewContainer.reparentLeash(t, newParent);
+ }
+
+ /** Start the fade-in animation. */
+ void fadeInIndicator() {
+ mVisualIndicatorViewContainer.fadeInIndicator(
+ mDisplayController.getDisplayLayout(mTaskInfo.displayId), mCurrentType,
+ mTaskInfo.displayId);
+ }
+
/**
* Based on the coordinates of the current drag event, determine which indicator type we should
* display, including no visible indicator.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 9cacb0c7d2b8..5d3cb86bf584 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -639,6 +639,7 @@ class DesktopTasksController(
dragToDesktopTransitionHandler.startDragToDesktopTransition(
taskInfo,
dragToDesktopValueAnimator,
+ visualIndicator,
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index d396d8bff2b8..c8f7ea9f885f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -25,6 +25,7 @@ import android.os.SystemProperties
import android.os.UserHandle
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CLOSE
+import android.window.DesktopModeFlags
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
import android.window.TransitionRequestInfo
@@ -118,6 +119,7 @@ sealed class DragToDesktopTransitionHandler(
fun startDragToDesktopTransition(
taskInfo: RunningTaskInfo,
dragToDesktopAnimator: MoveToDesktopAnimator,
+ visualIndicator: DesktopModeVisualIndicator?,
) {
if (inProgress) {
logV("Drag to desktop transition already in progress.")
@@ -163,12 +165,14 @@ sealed class DragToDesktopTransitionHandler(
dragAnimator = dragToDesktopAnimator,
startTransitionToken = startTransitionToken,
otherSplitTask = otherTask,
+ visualIndicator = visualIndicator,
)
} else {
TransitionState.FromFullscreen(
draggedTaskId = taskInfo.taskId,
dragAnimator = dragToDesktopAnimator,
startTransitionToken = startTransitionToken,
+ visualIndicator = visualIndicator,
)
}
}
@@ -457,6 +461,13 @@ sealed class DragToDesktopTransitionHandler(
state.surfaceLayers = layers
state.startTransitionFinishCb = finishCallback
state.startTransitionFinishTransaction = finishTransaction
+
+ val taskChange = state.draggedTaskChange ?: error("Expected non-null task change.")
+ val taskInfo = taskChange.taskInfo ?: error("Expected non-null task info.")
+
+ if (DesktopModeFlags.ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX.isTrue) {
+ attachIndicatorToTransitionRoot(state, info, taskInfo, startTransaction)
+ }
startTransaction.apply()
if (state.cancelState == CancelState.NO_CANCEL) {
@@ -485,8 +496,6 @@ sealed class DragToDesktopTransitionHandler(
} else {
SPLIT_POSITION_BOTTOM_OR_RIGHT
}
- val taskInfo =
- state.draggedTaskChange?.taskInfo ?: error("Expected non-null task info.")
val wct = WindowContainerTransaction()
restoreWindowOrder(wct)
state.startTransitionFinishTransaction?.apply()
@@ -511,6 +520,21 @@ sealed class DragToDesktopTransitionHandler(
return true
}
+ private fun attachIndicatorToTransitionRoot(
+ state: TransitionState,
+ info: TransitionInfo,
+ taskInfo: RunningTaskInfo,
+ t: SurfaceControl.Transaction,
+ ) {
+ val transitionRoot = info.getRoot(info.findRootIndex(taskInfo.displayId))
+ state.visualIndicator?.let {
+ // Attach the indicator to the transition root so that it's removed at the end of the
+ // transition regardless of whether we managed to release the indicator.
+ it.reparentLeash(t, transitionRoot.leash)
+ it.fadeInIndicator()
+ }
+ }
+
/**
* Calculates start drag to desktop layers for transition [info]. The leash layer is calculated
* based on its change position in the transition, e.g. `appLayer = appLayers - i`, where i is
@@ -901,6 +925,7 @@ sealed class DragToDesktopTransitionHandler(
abstract var surfaceLayers: DragToDesktopLayers?
abstract var cancelState: CancelState
abstract var startAborted: Boolean
+ abstract val visualIndicator: DesktopModeVisualIndicator?
data class FromFullscreen(
override val draggedTaskId: Int,
@@ -915,6 +940,7 @@ sealed class DragToDesktopTransitionHandler(
override var surfaceLayers: DragToDesktopLayers? = null,
override var cancelState: CancelState = CancelState.NO_CANCEL,
override var startAborted: Boolean = false,
+ override val visualIndicator: DesktopModeVisualIndicator?,
var otherRootChanges: MutableList<Change> = mutableListOf(),
) : TransitionState()
@@ -931,6 +957,7 @@ sealed class DragToDesktopTransitionHandler(
override var surfaceLayers: DragToDesktopLayers? = null,
override var cancelState: CancelState = CancelState.NO_CANCEL,
override var startAborted: Boolean = false,
+ override val visualIndicator: DesktopModeVisualIndicator?,
var splitRootChange: Change? = null,
var otherSplitTask: Int,
) : TransitionState()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
index 919e8164b58e..23562388b3e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
@@ -130,6 +130,12 @@ constructor(
}
}
+ /** Reparent the indicator to {@code newParent}. */
+ fun reparentLeash(t: SurfaceControl.Transaction, newParent: SurfaceControl) {
+ val leash = indicatorLeash ?: return
+ t.reparent(leash, newParent)
+ }
+
private fun showIndicator(taskSurface: SurfaceControl, leash: SurfaceControl) {
mainExecutor.execute {
indicatorLeash = leash
@@ -166,7 +172,7 @@ constructor(
displayController.getDisplayLayout(taskInfo.displayId)
?: error("Expected to find DisplayLayout for taskId${taskInfo.taskId}.")
if (currentType == IndicatorType.NO_INDICATOR) {
- fadeInIndicator(layout, newType, taskInfo.displayId, snapEventHandler)
+ fadeInIndicatorInternal(layout, newType, taskInfo.displayId, snapEventHandler)
} else if (newType == IndicatorType.NO_INDICATOR) {
fadeOutIndicator(
layout,
@@ -195,10 +201,22 @@ constructor(
}
/**
+ * Fade indicator in as provided type.
+ *
+ * Animator fades the indicator in while expanding the bounds outwards.
+ */
+ fun fadeInIndicator(layout: DisplayLayout, type: IndicatorType, displayId: Int) {
+ if (isReleased) return
+ desktopExecutor.execute {
+ fadeInIndicatorInternal(layout, type, displayId, snapEventHandler)
+ }
+ }
+
+ /**
* Fade indicator in as provided type. Animator fades it in while expanding the bounds outwards.
*/
@VisibleForTesting
- fun fadeInIndicator(
+ fun fadeInIndicatorInternal(
layout: DisplayLayout,
type: IndicatorType,
displayId: Int,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index fed336b17f19..65fa9b4b5529 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -52,6 +52,7 @@ import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.TransitionUtil;
+import com.android.wm.shell.shared.split.SplitScreenConstants;
import com.android.wm.shell.transition.OneShotRemoteHandler;
import com.android.wm.shell.transition.Transitions;
@@ -362,7 +363,8 @@ class SplitScreenTransitions {
WindowContainerTransaction wct,
@Nullable RemoteTransition remoteTransition,
Transitions.TransitionHandler handler,
- int extraTransitType, boolean resizeAnim) {
+ int extraTransitType, boolean resizeAnim,
+ @SplitScreenConstants.PersistentSnapPosition int snapPosition) {
if (mPendingEnter != null) {
ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " skip to start enter split transition since it already exist. ");
@@ -373,16 +375,18 @@ class SplitScreenTransitions {
.onSplitAnimationInvoked(true /*animationRunning*/));
}
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
- setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim);
+ setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim,
+ snapPosition);
return transition;
}
/** Sets a transition to enter split. */
void setEnterTransition(@NonNull IBinder transition,
@Nullable RemoteTransition remoteTransition,
- int extraTransitType, boolean resizeAnim) {
+ int extraTransitType, boolean resizeAnim,
+ int snapPosition) {
mPendingEnter = new EnterSession(
- transition, remoteTransition, extraTransitType, resizeAnim);
+ transition, remoteTransition, extraTransitType, resizeAnim, snapPosition);
ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Enter split screen");
@@ -675,13 +679,16 @@ class SplitScreenTransitions {
/** Bundled information of enter transition. */
class EnterSession extends TransitSession {
final boolean mResizeAnim;
+ /** The starting snap position we'll enter into with this transition. */
+ final @SplitScreenConstants.PersistentSnapPosition int mEnteringPosition;
EnterSession(IBinder transition,
@Nullable RemoteTransition remoteTransition,
- int extraTransitType, boolean resizeAnim) {
+ int extraTransitType, boolean resizeAnim, int snapPosition) {
super(transition, null /* consumedCallback */, null /* finishedCallback */,
remoteTransition, extraTransitType);
this.mResizeAnim = resizeAnim;
+ this.mEnteringPosition = snapPosition;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index e1b57e229d36..7472b0ea56ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -653,7 +653,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
null, this,
isSplitScreenVisible()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN,
- !mIsDropEntering);
+ !mIsDropEntering, SNAP_TO_2_50_50);
// Due to drag already pip task entering split by this method so need to reset flag here.
mIsDropEntering = false;
@@ -787,7 +787,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering, index);
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this,
- extraTransitType, !mIsDropEntering);
+ extraTransitType, !mIsDropEntering, SNAP_TO_2_50_50);
}
/**
@@ -833,7 +833,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering, index);
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this,
- extraTransitType, !mIsDropEntering);
+ extraTransitType, !mIsDropEntering, SNAP_TO_2_50_50);
}
/**
@@ -1050,7 +1050,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mPausingTasks.clear();
}
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, remoteTransition, this,
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false, snapPosition);
setEnterInstanceId(instanceId);
}
@@ -1140,7 +1140,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, remoteTransition, this,
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false, snapPosition);
setEnterInstanceId(instanceId);
}
@@ -1645,6 +1645,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
grantFocusToStage(stageToFocus);
}
+ private void grantFocusForSnapPosition(@PersistentSnapPosition int enteringPosition) {
+ switch (enteringPosition) {
+ case SNAP_TO_2_90_10 -> grantFocusToPosition(true /*leftOrTop*/);
+ case SNAP_TO_2_10_90 -> grantFocusToPosition(false /*leftOrTop*/);
+ default -> { /*no-op*/ }
+ }
+ }
+
private void clearRequestIfPresented() {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented");
if (mSideStage.mVisible && mSideStage.mHasChildren
@@ -2911,7 +2919,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// split, prepare to enter split screen.
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering, SNAP_TO_2_50_50);
} else if (isSplitScreenVisible() && isOpening) {
// launching into an existing split stage; possibly launchAdjacent
// If we're replacing a pip-able app, we need to let mixed handler take care of
@@ -2920,7 +2928,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// updated layout will get applied in startAnimation pendingResize
mSplitTransitions.setEnterTransition(transition,
request.getRemoteTransition(),
- TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, true /*resizeAnim*/);
+ TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, true /*resizeAnim*/,
+ SNAP_TO_2_50_50);
}
} else if (inFullscreen && isSplitScreenVisible()) {
// If the trigger task is in fullscreen and in split, exit split and place
@@ -2998,14 +3007,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
out = new WindowContainerTransaction();
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering, SNAP_TO_2_50_50);
return out;
}
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d "
+ "restoring to split", request.getDebugId());
out = new WindowContainerTransaction();
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
- TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, false /* resizeAnim */);
+ TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, false /* resizeAnim */,
+ SNAP_TO_2_50_50);
}
return out;
}
@@ -3192,7 +3202,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (keepSplitWithPip) {
// Set an enter transition for when startAnimation gets called again
mSplitTransitions.setEnterTransition(transition, /*remoteTransition*/ null,
- TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, /*resizeAnim*/ false);
+ TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, /*resizeAnim*/ false,
+ SNAP_TO_2_50_50);
} else {
int finalClosingTaskId = closingSplitTaskId;
mRecentTasks.ifPresent(recentTasks ->
@@ -3577,6 +3588,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
});
mPausingTasks.clear();
+ if (enableFlexibleTwoAppSplit()) {
+ grantFocusForSnapPosition(enterTransition.mEnteringPosition);
+ }
});
if (info.getType() == TRANSIT_CHANGE && !isSplitActive()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index dcc9e2415039..fe1dc29181b9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -20,6 +20,7 @@ import android.animation.AnimatorTestRule
import android.app.ActivityManager.RunningTaskInfo
import android.graphics.PointF
import android.graphics.Rect
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -28,6 +29,7 @@ import android.view.SurfaceControl
import androidx.test.filters.SmallTest
import com.android.internal.policy.SystemBarUtils
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.window.flags.Flags.FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
@@ -45,6 +47,8 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.kotlin.any
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
/**
@@ -345,6 +349,38 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
assertThat(visualIndicator.indicatorBounds).isEqualTo(dropTargetBounds)
}
+ @Test
+ @DisableFlags(FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX)
+ fun createIndicator_inTransitionFlagDisabled_isAttachedToDisplayArea() {
+ createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
+
+ verify(taskDisplayAreaOrganizer).attachToDisplayArea(anyInt(), any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX)
+ fun createIndicator_fromFreeform_inTransitionFlagEnabled_isAttachedToDisplayArea() {
+ createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
+
+ verify(taskDisplayAreaOrganizer).attachToDisplayArea(anyInt(), any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX)
+ fun createIndicator_fromFullscreen_inTransitionFlagEnabled_notAttachedToDisplayArea() {
+ createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
+
+ verify(taskDisplayAreaOrganizer, never()).attachToDisplayArea(anyInt(), any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX)
+ fun createIndicator_fromSplit_inTransitionFlagEnabled_notAttachedToDisplayArea() {
+ createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
+
+ verify(taskDisplayAreaOrganizer, never()).attachToDisplayArea(anyInt(), any())
+ }
+
private fun createVisualIndicator(dragStartState: DesktopModeVisualIndicator.DragStartState) {
visualIndicator =
DesktopModeVisualIndicator(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index de55db86d1e7..9588a5c73385 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -11,8 +11,11 @@ import android.graphics.PointF
import android.graphics.Rect
import android.os.IBinder
import android.os.SystemProperties
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_OPEN
import android.window.TransitionInfo
@@ -23,6 +26,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
import com.android.internal.jank.InteractionJankMonitor
+import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -78,6 +82,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
@Mock private lateinit var homeTaskLeash: SurfaceControl
@Mock private lateinit var desktopUserRepositories: DesktopUserRepositories
@Mock private lateinit var bubbleController: BubbleController
+ @Mock private lateinit var visualIndicator: DesktopModeVisualIndicator
private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
@@ -740,11 +745,47 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
assertThat(fraction).isWithin(TOLERANCE).of(0f)
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX)
+ fun startDrag_indicatorFlagEnabled_attachesIndicatorToTransitionRoot() {
+ val task = createTask()
+ val rootLeash = mock<SurfaceControl>()
+ val startTransaction = mock<SurfaceControl.Transaction>()
+ startDrag(
+ defaultHandler,
+ task,
+ startTransaction = startTransaction,
+ transitionRootLeash = rootLeash,
+ )
+
+ verify(visualIndicator).reparentLeash(startTransaction, rootLeash)
+ verify(visualIndicator).fadeInIndicator()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX)
+ fun startDrag_indicatorFlagDisabled_doesNotAttachIndicatorToTransitionRoot() {
+ val task = createTask()
+ val rootLeash = mock<SurfaceControl>()
+ val startTransaction = mock<SurfaceControl.Transaction>()
+ startDrag(
+ defaultHandler,
+ task,
+ startTransaction = startTransaction,
+ transitionRootLeash = rootLeash,
+ )
+
+ verify(visualIndicator, never()).reparentLeash(any(), any())
+ verify(visualIndicator, never()).fadeInIndicator()
+ }
+
private fun startDrag(
handler: DragToDesktopTransitionHandler,
task: RunningTaskInfo = createTask(),
+ startTransaction: SurfaceControl.Transaction = mock(),
finishTransaction: SurfaceControl.Transaction = mock(),
homeChange: TransitionInfo.Change? = createHomeChange(),
+ transitionRootLeash: SurfaceControl? = null,
): IBinder {
whenever(dragAnimator.position).thenReturn(PointF())
// Simulate transition is started and is ready to animate.
@@ -756,8 +797,9 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
draggedTask = task,
homeChange = homeChange,
+ rootLeash = transitionRootLeash,
),
- startTransaction = mock(),
+ startTransaction = startTransaction,
finishTransaction = finishTransaction,
finishCallback = {},
)
@@ -778,7 +820,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
)
)
.thenReturn(token)
- handler.startDragToDesktopTransition(task, dragAnimator)
+ handler.startDragToDesktopTransition(task, dragAnimator, visualIndicator)
return token
}
@@ -845,6 +887,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
type: Int,
draggedTask: RunningTaskInfo,
homeChange: TransitionInfo.Change? = createHomeChange(),
+ rootLeash: SurfaceControl? = null,
) =
TransitionInfo(type, /* flags= */ 0).apply {
homeChange?.let { addChange(it) }
@@ -861,6 +904,9 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
flags = flags or FLAG_IS_WALLPAPER
}
)
+ if (rootLeash != null) {
+ addRootLeash(DEFAULT_DISPLAY, rootLeash, /* offsetLeft= */ 0, /* offsetTop= */ 0)
+ }
}
private fun createHomeChange() =
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
index 4c8cb3823f7e..c7518d5914b4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
@@ -25,6 +25,7 @@ import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.Display
+import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
import android.view.View
@@ -49,6 +50,8 @@ import org.mockito.Mockito.mock
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyZeroInteractions
@@ -121,7 +124,7 @@ class VisualIndicatorViewContainerTest : ShellTestCase() {
DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
)
desktopExecutor.flushAll()
- verify(spyViewContainer).fadeInIndicator(any(), any(), any(), any())
+ verify(spyViewContainer).fadeInIndicatorInternal(any(), any(), any(), any())
}
@Test
@@ -265,6 +268,35 @@ class VisualIndicatorViewContainerTest : ShellTestCase() {
)
}
+ @Test
+ fun fadeInIndicator_callsFadeIn() {
+ val spyViewContainer = setupSpyViewContainer()
+
+ spyViewContainer.fadeInIndicator(
+ mock<DisplayLayout>(),
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ DEFAULT_DISPLAY,
+ )
+ desktopExecutor.flushAll()
+
+ verify(spyViewContainer).fadeInIndicatorInternal(any(), any(), any(), any())
+ }
+
+ @Test
+ fun fadeInIndicator_alreadyReleased_doesntCallFadeIn() {
+ val spyViewContainer = setupSpyViewContainer()
+ spyViewContainer.releaseVisualIndicator()
+
+ spyViewContainer.fadeInIndicator(
+ mock<DisplayLayout>(),
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ DEFAULT_DISPLAY,
+ )
+ desktopExecutor.flushAll()
+
+ verify(spyViewContainer, never()).fadeInIndicatorInternal(any(), any(), any(), any())
+ }
+
private fun setupSpyViewContainer(): VisualIndicatorViewContainer {
val viewContainer =
VisualIndicatorViewContainer(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index e5a6a6d258dd..70603fad37b9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
@@ -213,7 +214,7 @@ public class SplitTransitionTests extends ShellTestCase {
IBinder transition = mSplitScreenTransitions.startEnterTransition(
TRANSIT_OPEN, new WindowContainerTransaction(),
new RemoteTransition(testRemote, "Test"), mStageCoordinator,
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false, SNAP_TO_2_50_50);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -239,7 +240,7 @@ public class SplitTransitionTests extends ShellTestCase {
IBinder transition = mSplitScreenTransitions.startEnterTransition(
TRANSIT_OPEN, new WindowContainerTransaction(),
new RemoteTransition(testRemote, "Test"), mStageCoordinator,
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false, SNAP_TO_2_50_50);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
@@ -262,7 +263,7 @@ public class SplitTransitionTests extends ShellTestCase {
IBinder transition = mSplitScreenTransitions.startEnterTransition(
TRANSIT_OPEN, new WindowContainerTransaction(),
new RemoteTransition(testRemote, "Test"), mStageCoordinator,
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false, SNAP_TO_2_50_50);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
@@ -524,7 +525,7 @@ public class SplitTransitionTests extends ShellTestCase {
IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
TRANSIT_OPEN, new WindowContainerTransaction(),
new RemoteTransition(new TestRemoteTransition(), "Test"),
- mStageCoordinator, TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+ mStageCoordinator, TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false, SNAP_TO_2_50_50);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index e9c4c31729e9..e246329446dc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -475,7 +475,7 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator.startTask(mTaskId, SPLIT_POSITION_TOP_OR_LEFT, null /*options*/,
null, SPLIT_INDEX_UNDEFINED);
verify(mSplitScreenTransitions).startEnterTransition(anyInt(),
- mWctCaptor.capture(), any(), any(), anyInt(), anyBoolean());
+ mWctCaptor.capture(), any(), any(), anyInt(), anyBoolean(), anyInt());
int windowingMode = mWctCaptor.getValue().getChanges().get(mBinder).getWindowingMode();
assertEquals(windowingMode, WINDOWING_MODE_UNDEFINED);
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 275972495206..f6dc41ed128a 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -89,7 +89,7 @@ ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* window, const c
CHECK_NOT_NULL(window);
CHECK_NOT_NULL(debug_name);
- sp<SurfaceComposerClient> client = new SurfaceComposerClient();
+ sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
if (client->initCheck() != NO_ERROR) {
return nullptr;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/domain/interactor/SysUIStatePerDisplayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/domain/interactor/SysUIStatePerDisplayInteractorTest.kt
index ed9cd98a825a..f64f13d4a9b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/domain/interactor/SysUIStatePerDisplayInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/domain/interactor/SysUIStatePerDisplayInteractorTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.common.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.model.StateChange
import com.android.systemui.model.fakeSysUIStatePerDisplayRepository
import com.android.systemui.model.sysUiStateFactory
@@ -26,6 +27,7 @@ import com.android.systemui.model.sysuiStateInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
+import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.runner.RunWith
@@ -49,6 +51,13 @@ class SysUIStatePerDisplayInteractorTest : SysuiTestCase() {
add(1, state1)
add(2, state2)
}
+ runBlocking {
+ kosmos.displayRepository.apply {
+ addDisplay(0)
+ addDisplay(1)
+ addDisplay(2)
+ }
+ }
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index 0197a1e61801..c72afc72fa16 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -16,26 +16,17 @@
package com.android.systemui.media.controls.domain.interactor
-import android.R
-import android.graphics.drawable.Icon
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.MediaTestHelper
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
-import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor
import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
-import com.android.systemui.media.controls.domain.pipeline.interactor.mediaRecommendationsInteractor
-import com.android.systemui.media.controls.shared.model.MediaCommonModel
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
-import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
-import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -52,16 +43,6 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
private val mediaFilterRepository: MediaFilterRepository =
with(kosmos) { mediaFilterRepository }
- private val mediaRecommendationsInteractor: MediaRecommendationsInteractor =
- kosmos.mediaRecommendationsInteractor
- val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
- private val mediaRecommendation =
- SmartspaceMediaData(
- targetId = KEY_MEDIA_SMARTSPACE,
- isActive = true,
- packageName = PACKAGE_NAME,
- recommendations = MediaTestHelper.getValidRecommendationList(icon),
- )
private val underTest: MediaCarouselInteractor = kosmos.mediaCarouselInteractor
@@ -119,81 +100,6 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
}
@Test
- fun addActiveRecommendation_inactiveMedia() =
- testScope.runTest {
- val hasActiveMediaOrRecommendation by
- collectLastValue(underTest.hasActiveMediaOrRecommendation)
- val hasAnyMediaOrRecommendation by
- collectLastValue(underTest.hasAnyMediaOrRecommendation)
- val currentMedia by collectLastValue(underTest.currentMedia)
-
- val userMedia = MediaData(active = false)
- val recsLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
- val mediaLoadingModel = MediaDataLoadingModel.Loaded(userMedia.instanceId)
-
- mediaFilterRepository.setRecommendation(mediaRecommendation)
- mediaFilterRepository.setRecommendationsLoadingState(recsLoadingModel)
-
- assertThat(hasActiveMediaOrRecommendation).isTrue()
- assertThat(hasAnyMediaOrRecommendation).isTrue()
- assertThat(currentMedia)
- .containsExactly(MediaCommonModel.MediaRecommendations(recsLoadingModel))
-
- mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
- mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
- mediaFilterRepository.setOrderedMedia()
-
- assertThat(hasActiveMediaOrRecommendation).isTrue()
- assertThat(hasAnyMediaOrRecommendation).isTrue()
- assertThat(currentMedia)
- .containsExactly(
- MediaCommonModel.MediaRecommendations(recsLoadingModel),
- MediaCommonModel.MediaControl(mediaLoadingModel, true),
- )
- .inOrder()
- }
-
- @Test
- fun addActiveRecommendation_thenInactive() =
- testScope.runTest {
- val hasActiveMediaOrRecommendation by
- collectLastValue(underTest.hasActiveMediaOrRecommendation)
- val hasAnyMediaOrRecommendation by
- collectLastValue(underTest.hasAnyMediaOrRecommendation)
-
- mediaFilterRepository.setRecommendation(mediaRecommendation)
-
- assertThat(hasActiveMediaOrRecommendation).isTrue()
- assertThat(hasAnyMediaOrRecommendation).isTrue()
-
- mediaFilterRepository.setRecommendation(mediaRecommendation.copy(isActive = false))
-
- assertThat(hasActiveMediaOrRecommendation).isFalse()
- assertThat(hasAnyMediaOrRecommendation).isFalse()
- }
-
- @Test
- fun addActiveRecommendation_thenInvalid() =
- testScope.runTest {
- val hasActiveMediaOrRecommendation by
- collectLastValue(underTest.hasActiveMediaOrRecommendation)
- val hasAnyMediaOrRecommendation by
- collectLastValue(underTest.hasAnyMediaOrRecommendation)
-
- mediaFilterRepository.setRecommendation(mediaRecommendation)
-
- assertThat(hasActiveMediaOrRecommendation).isTrue()
- assertThat(hasAnyMediaOrRecommendation).isTrue()
-
- mediaFilterRepository.setRecommendation(
- mediaRecommendation.copy(recommendations = listOf())
- )
-
- assertThat(hasActiveMediaOrRecommendation).isFalse()
- assertThat(hasAnyMediaOrRecommendation).isFalse()
- }
-
- @Test
fun hasAnyMedia_noMediaSet_returnsFalse() =
testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() }
@@ -208,47 +114,4 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
@Test
fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() =
testScope.runTest { assertThat(underTest.hasActiveMediaOrRecommendation.value).isFalse() }
-
- @Test
- fun loadMediaFromRec() =
- testScope.runTest {
- val currentMedia by collectLastValue(underTest.currentMedia)
- val instanceId = InstanceId.fakeInstanceId(123)
- val data =
- MediaData(
- active = true,
- instanceId = instanceId,
- packageName = PACKAGE_NAME,
- notificationKey = KEY,
- )
- val smartspaceLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
- val mediaLoadingModel = MediaDataLoadingModel.Loaded(instanceId)
-
- mediaFilterRepository.setRecommendation(mediaRecommendation)
- mediaFilterRepository.setRecommendationsLoadingState(smartspaceLoadingModel)
- mediaRecommendationsInteractor.switchToMediaControl(PACKAGE_NAME)
- mediaFilterRepository.addSelectedUserMediaEntry(data)
- mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
-
- assertThat(currentMedia)
- .containsExactly(MediaCommonModel.MediaRecommendations(smartspaceLoadingModel))
- .inOrder()
-
- mediaFilterRepository.addSelectedUserMediaEntry(data.copy(isPlaying = true))
- mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
-
- assertThat(currentMedia)
- .containsExactly(
- MediaCommonModel.MediaControl(mediaLoadingModel, isMediaFromRec = true),
- MediaCommonModel.MediaRecommendations(smartspaceLoadingModel),
- )
- .inOrder()
- }
-
- companion object {
- private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
- private const val PACKAGE_NAME = "com.android.example"
- private const val KEY = "key"
- private const val SURFACE = 4
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
deleted file mode 100644
index 2265c0149cc3..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
+++ /dev/null
@@ -1,168 +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.media.controls.domain.interactor
-
-import android.R
-import android.content.ComponentName
-import android.content.Intent
-import android.content.applicationContext
-import android.graphics.drawable.Icon
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.Expandable
-import com.android.systemui.broadcast.broadcastSender
-import com.android.systemui.broadcast.mockBroadcastSender
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.MediaTestHelper
-import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
-import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor
-import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor.Companion.EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME
-import com.android.systemui.media.controls.domain.pipeline.interactor.mediaRecommendationsInteractor
-import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
-import com.android.systemui.media.controls.shared.model.MediaRecModel
-import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
-import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
-import com.android.systemui.plugins.activityStarter
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.doNothing
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
-import org.mockito.kotlin.eq
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class MediaRecommendationsInteractorTest : SysuiTestCase() {
-
- private val spyContext = spy(context)
- private val kosmos = testKosmos().apply { applicationContext = spyContext }
- private val testScope = kosmos.testScope
-
- private val mediaDataFilter: MediaDataFilterImpl = with(kosmos) { mediaDataFilter }
- private val activityStarter = kosmos.activityStarter
- private val icon: Icon = Icon.createWithResource(context, R.drawable.ic_media_play)
- private val smartspaceMediaData: SmartspaceMediaData =
- SmartspaceMediaData(
- targetId = KEY_MEDIA_SMARTSPACE,
- isActive = true,
- packageName = PACKAGE_NAME,
- recommendations = MediaTestHelper.getValidRecommendationList(icon),
- )
-
- private val underTest: MediaRecommendationsInteractor =
- with(kosmos) {
- broadcastSender = mockBroadcastSender
- kosmos.mediaRecommendationsInteractor
- }
-
- @Test
- fun addRecommendation_smartspaceMediaDataUpdate() =
- testScope.runTest {
- val recommendations by collectLastValue(underTest.recommendations)
-
- val model =
- MediaRecommendationsModel(
- key = KEY_MEDIA_SMARTSPACE,
- packageName = PACKAGE_NAME,
- areRecommendationsValid = true,
- mediaRecs =
- listOf(
- MediaRecModel(icon = icon),
- MediaRecModel(icon = icon),
- MediaRecModel(icon = icon),
- ),
- )
-
- mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
-
- assertThat(recommendations).isEqualTo(model)
- }
-
- @Test
- fun addInvalidRecommendation() =
- testScope.runTest {
- val recommendations by collectLastValue(underTest.recommendations)
- val inValidData = smartspaceMediaData.copy(recommendations = listOf())
-
- mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
- assertThat(recommendations?.areRecommendationsValid).isTrue()
-
- mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, inValidData)
- assertThat(recommendations?.areRecommendationsValid).isFalse()
- assertThat(recommendations?.mediaRecs?.isEmpty()).isTrue()
- }
-
- @Test
- fun removeRecommendation_noTrampolineActivity() {
- val intent = Intent()
-
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-
- mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
- underTest.removeMediaRecommendations(KEY_MEDIA_SMARTSPACE, intent, 0)
-
- verify(kosmos.mockBroadcastSender).sendBroadcast(eq(intent))
- }
-
- @Test
- fun removeRecommendation_usingTrampolineActivity() {
- doNothing().whenever(spyContext).startActivity(any())
- val intent = Intent()
-
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- intent.component = ComponentName(PACKAGE_NAME, EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME)
-
- underTest.removeMediaRecommendations(KEY_MEDIA_SMARTSPACE, intent, 0)
-
- verify(spyContext).startActivity(eq(intent))
- }
-
- @Test
- fun startSettings() {
- underTest.startSettings()
-
- verify(activityStarter).startActivity(any(), eq(true))
- }
-
- @Test
- fun startClickIntent() {
- doNothing().whenever(spyContext).startActivity(any())
- val intent = Intent()
- val expandable = mock<Expandable>()
-
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-
- mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
- underTest.startClickIntent(expandable, intent)
-
- verify(spyContext).startActivity(eq(intent))
- }
-
- companion object {
- private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
- private const val PACKAGE_NAME = "com.example.app"
- private const val SURFACE = 4
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
index 005424ba599e..faa62c2febc1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
@@ -23,7 +23,6 @@ import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel
import com.android.systemui.media.controls.ui.viewmodel.mediaControlViewModel
-import com.android.systemui.media.controls.ui.viewmodel.mediaRecommendationsViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.fail
@@ -56,25 +55,6 @@ class MediaDiffUtilTest : SysuiTestCase() {
}
@Test
- fun newMediaRecommendationsAdded() {
- val mediaRecs = createMediaRecommendations(KEY_MEDIA_SMARTSPACE, true)
- val oldList = listOf<MediaCommonViewModel>()
- val newList = listOf(mediaRecs)
- val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
- val mediaLoadedListUpdateCallback =
- MediaViewModelListUpdateCallback(
- oldList,
- newList,
- { commonViewModel, _ -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
- { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
- { fail("Unexpected to remove $it") },
- { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
- )
-
- DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
- }
-
- @Test
fun updateMediaControl_contentChanged() {
val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true)
val oldList = listOf(mediaControl)
@@ -94,25 +74,6 @@ class MediaDiffUtilTest : SysuiTestCase() {
}
@Test
- fun updateMediaRecommendations_contentChanged() {
- val mediaRecs = createMediaRecommendations(KEY_MEDIA_SMARTSPACE, true)
- val oldList = listOf(mediaRecs)
- val newList = listOf(mediaRecs.copy(key = KEY_MEDIA_SMARTSPACE_2))
- val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
- val mediaLoadedListUpdateCallback =
- MediaViewModelListUpdateCallback(
- oldList,
- newList,
- { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaRecs) },
- { fail("Unexpected to remove $it") },
- { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
- )
-
- DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
- }
-
- @Test
fun mediaControlMoved() {
val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123), true)
val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456), false)
@@ -133,27 +94,6 @@ class MediaDiffUtilTest : SysuiTestCase() {
}
@Test
- fun mediaRecommendationsMoved() {
- val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123), true)
- val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456), false)
- val mediaRecs = createMediaRecommendations(KEY_MEDIA_SMARTSPACE, true)
- val oldList = listOf(mediaRecs, mediaControl1, mediaControl2)
- val newList = listOf(mediaControl1, mediaControl2, mediaRecs)
- val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
- val mediaLoadedListUpdateCallback =
- MediaViewModelListUpdateCallback(
- oldList,
- newList,
- { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
- { fail("Unexpected to remove $it") },
- { commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
- )
-
- DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
- }
-
- @Test
fun mediaControlRemoved() {
val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true)
val oldList = listOf(mediaControl)
@@ -172,25 +112,6 @@ class MediaDiffUtilTest : SysuiTestCase() {
DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
}
- @Test
- fun mediaRecommendationsRemoved() {
- val mediaRecs = createMediaRecommendations(KEY_MEDIA_SMARTSPACE_2, false)
- val oldList = listOf(mediaRecs)
- val newList = listOf<MediaCommonViewModel>()
- val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
- val mediaLoadedListUpdateCallback =
- MediaViewModelListUpdateCallback(
- oldList,
- newList,
- { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
- { commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
- { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
- )
-
- DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
- }
-
private fun createMediaControl(
instanceId: InstanceId,
immediatelyUpdateUi: Boolean,
@@ -201,26 +122,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
controlViewModel = kosmos.mediaControlViewModel,
onAdded = {},
onRemoved = {},
- onUpdated = {}
- )
- }
-
- private fun createMediaRecommendations(
- key: String,
- loadingEnabled: Boolean,
- ): MediaCommonViewModel.MediaRecommendations {
- return MediaCommonViewModel.MediaRecommendations(
- key = key,
- loadingEnabled = loadingEnabled,
- recsViewModel = kosmos.mediaRecommendationsViewModel,
- onAdded = {},
- onRemoved = {},
- onUpdated = {}
+ onUpdated = {},
)
}
-
- companion object {
- private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
- private const val KEY_MEDIA_SMARTSPACE_2 = "MEDIA_SMARTSPACE_ID_2"
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
index fb5bbf452cfa..e56b114dc847 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
@@ -19,23 +19,18 @@ package com.android.systemui.media.controls.ui.viewmodel
import android.R
import android.content.packageManager
import android.content.pm.ApplicationInfo
-import android.graphics.drawable.Icon
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.MediaTestHelper
import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
-import com.android.systemui.media.controls.domain.pipeline.interactor.mediaRecommendationsInteractor
import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
import com.android.systemui.media.controls.shared.mediaLogger
import com.android.systemui.media.controls.shared.mockMediaLogger
import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
-import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider
import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
@@ -62,15 +57,7 @@ class MediaCarouselViewModelTest : SysuiTestCase() {
private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
private val packageManager = kosmos.packageManager
- private val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
private val drawable = context.getDrawable(R.drawable.ic_media_play)
- private val smartspaceMediaData: SmartspaceMediaData =
- SmartspaceMediaData(
- targetId = KEY_MEDIA_SMARTSPACE,
- isActive = true,
- packageName = PACKAGE_NAME,
- recommendations = MediaTestHelper.getValidRecommendationList(icon),
- )
private val underTest: MediaCarouselViewModel = kosmos.mediaCarouselViewModel
@@ -121,53 +108,6 @@ class MediaCarouselViewModelTest : SysuiTestCase() {
}
@Test
- fun loadMediaControlsAndRecommendations_mediaItemsAreUpdated() =
- testScope.runTest {
- val sortedMedia by collectLastValue(underTest.mediaItems)
- val instanceId1 = InstanceId.fakeInstanceId(123)
- val instanceId2 = InstanceId.fakeInstanceId(456)
-
- loadMediaControl(KEY, instanceId1)
- loadMediaControl(KEY_2, instanceId2)
- loadMediaRecommendations()
-
- val firstMediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
- val secondMediaControl = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
- val recsCard = sortedMedia?.get(2) as MediaCommonViewModel.MediaRecommendations
- assertThat(firstMediaControl.instanceId).isEqualTo(instanceId2)
- assertThat(secondMediaControl.instanceId).isEqualTo(instanceId1)
- assertThat(recsCard.key).isEqualTo(KEY_MEDIA_SMARTSPACE)
- }
-
- @Test
- fun recommendationClicked_switchToPlayer() =
- testScope.runTest {
- val sortedMedia by collectLastValue(underTest.mediaItems)
- kosmos.visualStabilityProvider.isReorderingAllowed = false
- val instanceId = InstanceId.fakeInstanceId(123)
-
- loadMediaRecommendations()
- kosmos.mediaRecommendationsInteractor.switchToMediaControl(PACKAGE_NAME)
-
- var recsCard = sortedMedia?.get(0) as MediaCommonViewModel.MediaRecommendations
- assertThat(sortedMedia).hasSize(1)
- assertThat(recsCard.key).isEqualTo(KEY_MEDIA_SMARTSPACE)
-
- loadMediaControl(KEY, instanceId, false)
-
- recsCard = sortedMedia?.get(0) as MediaCommonViewModel.MediaRecommendations
- assertThat(sortedMedia).hasSize(1)
- assertThat(recsCard.key).isEqualTo(KEY_MEDIA_SMARTSPACE)
-
- loadMediaControl(KEY, instanceId, true)
-
- val mediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
- assertThat(sortedMedia).hasSize(2)
- assertThat(mediaControl.instanceId).isEqualTo(instanceId)
- assertThat(mediaControl.isMediaFromRec).isTrue()
- }
-
- @Test
fun addMediaControlThenRemove_mediaEventsAreLogged() =
testScope.runTest {
val sortedMedia by collectLastValue(underTest.mediaItems)
@@ -199,31 +139,6 @@ class MediaCarouselViewModelTest : SysuiTestCase() {
verify(kosmos.mediaLogger).logMediaCardRemoved(eq(instanceId))
}
- @Test
- fun addMediaRecommendationThenRemove_mediaEventsAreLogged() =
- testScope.runTest {
- val sortedMedia by collectLastValue(underTest.mediaItems)
-
- loadMediaRecommendations()
-
- val mediaRecommendations =
- sortedMedia?.get(0) as MediaCommonViewModel.MediaRecommendations
- assertThat(mediaRecommendations.key).isEqualTo(KEY_MEDIA_SMARTSPACE)
-
- // when media recommendation is added to carousel
- mediaRecommendations.onAdded(mediaRecommendations)
-
- verify(kosmos.mediaLogger).logMediaRecommendationCardAdded(eq(KEY_MEDIA_SMARTSPACE))
-
- mediaDataFilter.onSmartspaceMediaDataRemoved(KEY, true)
- assertThat(sortedMedia).isEmpty()
-
- // when media recommendation is removed from carousel
- mediaRecommendations.onRemoved(true)
-
- verify(kosmos.mediaLogger).logMediaRecommendationCardRemoved(eq(KEY_MEDIA_SMARTSPACE))
- }
-
private fun loadMediaControl(key: String, instanceId: InstanceId, isPlaying: Boolean = true) {
whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
@@ -239,15 +154,10 @@ class MediaCarouselViewModelTest : SysuiTestCase() {
mediaDataFilter.onMediaDataLoaded(key, key, mediaData)
}
- private fun loadMediaRecommendations(key: String = KEY_MEDIA_SMARTSPACE) {
- mediaDataFilter.onSmartspaceMediaDataLoaded(key, smartspaceMediaData)
- }
-
companion object {
private const val USER_ID = 0
private const val KEY = "key"
private const val KEY_2 = "key2"
private const val PACKAGE_NAME = "com.example.app"
- private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelTest.kt
deleted file mode 100644
index 51b1911be5d5..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelTest.kt
+++ /dev/null
@@ -1,88 +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.media.controls.ui.viewmodel
-
-import android.R
-import android.content.packageManager
-import android.content.pm.ApplicationInfo
-import android.graphics.drawable.Icon
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.MediaTestHelper
-import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
-import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
-import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.Mockito
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class MediaRecommendationsViewModelTest : SysuiTestCase() {
-
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
-
- private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
- private val packageManager = kosmos.packageManager
- private val icon: Icon = Icon.createWithResource(context, R.drawable.ic_media_play)
- private val drawable = context.getDrawable(R.drawable.ic_media_play)
- private val smartspaceMediaData: SmartspaceMediaData =
- SmartspaceMediaData(
- targetId = KEY_MEDIA_SMARTSPACE,
- isActive = true,
- packageName = PACKAGE_NAME,
- recommendations = MediaTestHelper.getValidRecommendationList(icon),
- )
-
- private val underTest: MediaRecommendationsViewModel = kosmos.mediaRecommendationsViewModel
-
- @Test
- fun loadRecommendations_recsCardViewModelIsLoaded() =
- testScope.runTest {
- whenever(packageManager.getApplicationIcon(Mockito.anyString())).thenReturn(drawable)
- whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java)))
- .thenReturn(drawable)
- whenever(packageManager.getApplicationInfo(eq(PACKAGE_NAME), ArgumentMatchers.anyInt()))
- .thenReturn(ApplicationInfo())
- whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE_NAME)
- val recsCardViewModel by collectLastValue(underTest.mediaRecsCard)
-
- context.setMockPackageManager(packageManager)
-
- mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
-
- assertThat(recsCardViewModel).isNotNull()
- assertThat(recsCardViewModel?.mediaRecs?.size)
- .isEqualTo(smartspaceMediaData.recommendations.size)
- }
-
- companion object {
- private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
- private const val PACKAGE_NAME = "com.example.app"
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
index 31f8590c0378..46430afecbb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
@@ -70,6 +70,7 @@ import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -83,6 +84,7 @@ import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -155,7 +157,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
testScope = kosmos.testScope
shadeViewStateProvider = TestShadeViewStateProvider()
- Mockito.`when`(
+ whenever(
kosmos.mockStatusBarContentInsetsProvider
.getStatusBarContentInsetsForCurrentRotation()
)
@@ -163,9 +165,9 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
- Mockito.`when`(iconManagerFactory.create(ArgumentMatchers.any(), ArgumentMatchers.any()))
+ whenever(iconManagerFactory.create(ArgumentMatchers.any(), ArgumentMatchers.any()))
.thenReturn(iconManager)
- Mockito.`when`(statusBarContentInsetsProviderStore.forDisplay(context.displayId))
+ whenever(statusBarContentInsetsProviderStore.forDisplay(context.displayId))
.thenReturn(kosmos.mockStatusBarContentInsetsProvider)
allowTestableLooperAsMainThread()
looper.runWithLooper {
@@ -174,7 +176,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
LayoutInflater.from(mContext).inflate(R.layout.keyguard_status_bar, null)
as KeyguardStatusBarView
)
- Mockito.`when`(keyguardStatusBarView.getDisplay()).thenReturn(mContext.display)
+ whenever(keyguardStatusBarView.display).thenReturn(mContext.display)
}
controller = createController()
@@ -404,14 +406,14 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
fun updateViewState_alphaAndVisibilityGiven_viewUpdated() {
// Verify the initial values so we know the method triggers changes.
Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(1f)
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
val newAlpha = 0.5f
val newVisibility = View.INVISIBLE
controller.updateViewState(newAlpha, newVisibility)
Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(newAlpha)
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(newVisibility)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(newVisibility)
}
@Test
@@ -423,7 +425,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.updateViewState(1f, View.VISIBLE)
// Since we're disabled, we stay invisible
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
@@ -444,15 +446,15 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
fun updateViewState_bypassEnabledAndShouldListenForFace_viewHidden() {
controller.onViewAttached()
updateStateToKeyguard()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
- Mockito.`when`(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(true)
- Mockito.`when`(keyguardBypassController.bypassEnabled).thenReturn(true)
+ whenever(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(true)
+ whenever(keyguardBypassController.bypassEnabled).thenReturn(true)
onFinishedGoingToSleep()
controller.updateViewState()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
@@ -461,13 +463,13 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.onViewAttached()
updateStateToKeyguard()
- Mockito.`when`(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(true)
- Mockito.`when`(keyguardBypassController.bypassEnabled).thenReturn(false)
+ whenever(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(true)
+ whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
onFinishedGoingToSleep()
controller.updateViewState()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -476,13 +478,13 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.onViewAttached()
updateStateToKeyguard()
- Mockito.`when`(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(false)
- Mockito.`when`(keyguardBypassController.bypassEnabled).thenReturn(true)
+ whenever(keyguardUpdateMonitor.shouldListenForFace()).thenReturn(false)
+ whenever(keyguardBypassController.bypassEnabled).thenReturn(true)
onFinishedGoingToSleep()
controller.updateViewState()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -495,7 +497,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.updateViewState()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
@@ -508,7 +510,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.updateViewState()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
@@ -520,7 +522,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.updateViewState()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -532,7 +534,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.updateViewState()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
@@ -544,7 +546,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.updateViewState()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -556,7 +558,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.updateViewState()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
@@ -568,7 +570,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.setDozing(true)
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
@@ -580,7 +582,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.setDozing(false)
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -595,7 +597,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.updateViewState()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.GONE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.GONE)
Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(0.456f)
}
@@ -611,7 +613,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.updateViewState(0.789f, View.VISIBLE)
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.GONE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.GONE)
Truth.assertThat(keyguardStatusBarView.alpha).isEqualTo(0.456f)
}
@@ -635,13 +637,13 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.init()
controller.onViewAttached()
updateStateToKeyguard()
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
controller.setDozing(true)
// setDozing(true) should typically cause the view to hide. But since the flag is on, we
// should ignore these set dozing calls and stay the same visibility.
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -679,7 +681,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
shadeViewStateProvider.setShouldHeadsUpBeVisible(true)
controller.updateForHeadsUp(/* animate= */ false)
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
@@ -695,7 +697,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
shadeViewStateProvider.setShouldHeadsUpBeVisible(false)
controller.updateForHeadsUp(/* animate= */ false)
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -728,7 +730,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
val str = mContext.getString(com.android.internal.R.string.status_bar_volume)
// GIVEN the setting is off
- Mockito.`when`(secureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0))
+ whenever(secureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0))
.thenReturn(0)
// WHEN CollapsedStatusBarFragment builds the blocklist
@@ -744,7 +746,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
val str = mContext.getString(com.android.internal.R.string.status_bar_volume)
// GIVEN the setting is ON
- Mockito.`when`(
+ whenever(
secureSettings.getIntForUser(
Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
0,
@@ -779,42 +781,52 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
controller.onViewAttached()
updateStateToKeyguard()
setDisableSystemInfo(true)
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
controller.animateKeyguardStatusBarIn()
// Since we're disabled, we don't actually animate in and stay invisible
- Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
fun animateToGlanceableHub_affectsAlpha() =
testScope.runTest {
- controller.init()
- val transitionAlphaAmount = .5f
- ViewUtils.attachView(keyguardStatusBarView)
- looper.processAllMessages()
- updateStateToKeyguard()
- kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Communal)
- runCurrent()
- controller.updateCommunalAlphaTransition(transitionAlphaAmount)
- Truth.assertThat(keyguardStatusBarView.getAlpha()).isEqualTo(transitionAlphaAmount)
+ try {
+ controller.init()
+ val transitionAlphaAmount = .5f
+ ViewUtils.attachView(keyguardStatusBarView)
+
+ looper.processAllMessages()
+ updateStateToKeyguard()
+ kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Communal)
+ runCurrent()
+ controller.updateCommunalAlphaTransition(transitionAlphaAmount)
+ assertThat(keyguardStatusBarView.getAlpha()).isEqualTo(transitionAlphaAmount)
+ } finally {
+ ViewUtils.detachView(keyguardStatusBarView)
+ }
}
@Test
fun animateToGlanceableHub_alphaResetOnCommunalNotShowing() =
testScope.runTest {
- controller.init()
- val transitionAlphaAmount = .5f
- ViewUtils.attachView(keyguardStatusBarView)
- looper.processAllMessages()
- updateStateToKeyguard()
- kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Communal)
- runCurrent()
- controller.updateCommunalAlphaTransition(transitionAlphaAmount)
- kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Blank)
- runCurrent()
- Truth.assertThat(keyguardStatusBarView.getAlpha()).isNotEqualTo(transitionAlphaAmount)
+ try {
+ controller.init()
+ val transitionAlphaAmount = .5f
+ ViewUtils.attachView(keyguardStatusBarView)
+
+ looper.processAllMessages()
+ updateStateToKeyguard()
+ kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Communal)
+ runCurrent()
+ controller.updateCommunalAlphaTransition(transitionAlphaAmount)
+ kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Blank)
+ runCurrent()
+ assertThat(keyguardStatusBarView.getAlpha()).isNotEqualTo(transitionAlphaAmount)
+ } finally {
+ ViewUtils.detachView(keyguardStatusBarView)
+ }
}
/**
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml b/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml
deleted file mode 100644
index 495fbb893eac..000000000000
--- a/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 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.
--->
-
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <corners android:radius="24dp"/>
- <gradient
- android:angle="0"
- android:startColor="#00000000"
- android:endColor="#ff000000"
- android:type="linear" />
-</shape>
diff --git a/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml b/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml
deleted file mode 100644
index de0a6201cb09..000000000000
--- a/packages/SystemUI/res/drawable/qs_media_rec_scrim.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 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
- -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <!-- gradient from 25% in the center to 100% at edges -->
- <gradient
- android:type="radial"
- android:gradientRadius="40%p"
- android:startColor="#AE000000"
- android:endColor="#00000000" />
-</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_recommendation_view.xml b/packages/SystemUI/res/layout/media_recommendation_view.xml
deleted file mode 100644
index e63aa211f9f1..000000000000
--- a/packages/SystemUI/res/layout/media_recommendation_view.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 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
- -->
-<!-- Layout for media recommendation item inside QSPanel carousel -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- Album cover -->
- <ImageView
- android:id="@+id/media_cover"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:translationZ="0dp"
- android:scaleType="matrix"
- android:adjustViewBounds="true"
- android:clipToOutline="true"
- android:layerType="hardware"
- android:background="@drawable/bg_smartspace_media_item"/>
-
- <!-- App icon -->
- <com.android.internal.widget.CachingIconView
- android:id="@+id/media_rec_app_icon"
- android:layout_width="@dimen/qs_media_rec_album_icon_size"
- android:layout_height="@dimen/qs_media_rec_album_icon_size"
- android:minWidth="@dimen/qs_media_rec_album_icon_size"
- android:minHeight="@dimen/qs_media_rec_album_icon_size"
- android:layout_marginStart="@dimen/qs_media_info_spacing"
- android:layout_marginTop="@dimen/qs_media_info_spacing"/>
-
- <!-- Artist name -->
- <TextView
- android:id="@+id/media_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_info_spacing"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
- android:layout_marginBottom="@dimen/qs_media_rec_album_title_bottom_margin"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:singleLine="true"
- android:textSize="12sp"
- android:gravity="top"
- android:layout_gravity="bottom"
- android:importantForAccessibility="no"/>
-
- <!-- Album name -->
- <TextView
- android:id="@+id/media_subtitle"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_media_rec_album_subtitle_height"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
- android:layout_marginStart="@dimen/qs_media_info_spacing"
- android:layout_marginBottom="@dimen/qs_media_info_spacing"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:singleLine="true"
- android:textSize="11sp"
- android:gravity="center_vertical"
- android:layout_gravity="bottom"
- android:importantForAccessibility="no"/>
-
- <!-- Seek Bar -->
- <SeekBar
- android:id="@+id/media_progress_bar"
- android:layout_width="match_parent"
- android:layout_height="12dp"
- android:layout_gravity="bottom"
- android:maxHeight="@dimen/qs_media_enabled_seekbar_height"
- android:thumb="@android:color/transparent"
- android:splitTrack="false"
- android:clickable="false"
- android:progressTint="?android:attr/textColorPrimary"
- android:progressBackgroundTint="?android:attr/textColorTertiary"
- android:paddingTop="5dp"
- android:paddingBottom="5dp"
- android:paddingStart="0dp"
- android:paddingEnd="0dp"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
- android:layout_marginStart="@dimen/qs_media_info_spacing"
- android:layout_marginBottom="@dimen/qs_media_info_spacing"/>
-</merge> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_recommendations.xml b/packages/SystemUI/res/layout/media_recommendations.xml
deleted file mode 100644
index 65fc19c5b2a4..000000000000
--- a/packages/SystemUI/res/layout/media_recommendations.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 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
- -->
-
-<!-- Layout for media recommendations inside QSPanel carousel -->
-<com.android.systemui.util.animation.TransitionLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/media_recommendations_updated"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:forceHasOverlappingRendering="false"
- android:background="@drawable/qs_media_background"
- android:theme="@style/MediaPlayer">
-
- <!-- This view just ensures the full media player is a certain height. -->
- <View
- android:id="@+id/sizing_view"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_media_session_height_expanded" />
-
- <TextView
- android:id="@+id/media_rec_title"
- style="@style/MediaPlayer.Recommendation.Header"
- android:text="@string/controls_media_smartspace_rec_header"/>
-
- <FrameLayout
- android:id="@+id/media_cover1_container"
- style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
- >
-
- <include
- layout="@layout/media_recommendation_view"/>
-
- </FrameLayout>
-
-
- <FrameLayout
- android:id="@+id/media_cover2_container"
- style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
- >
-
- <include
- layout="@layout/media_recommendation_view"/>
-
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/media_cover3_container"
- style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
- >
-
- <include
- layout="@layout/media_recommendation_view"/>
-
- </FrameLayout>
-
- <include
- layout="@layout/media_long_press_menu" />
-
-</com.android.systemui.util.animation.TransitionLayout>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 41bb37efa623..f4f0424ade98 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -30,11 +30,6 @@
not appear immediately after user swipes to the side -->
<dimen name="qs_tiles_page_horizontal_margin">20dp</dimen>
- <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
- <dimen name="qs_media_rec_icon_top_margin">16dp</dimen>
- <dimen name="qs_media_rec_album_size">112dp</dimen>
- <dimen name="qs_media_rec_album_side_margin">16dp</dimen>
-
<dimen name="controls_panel_corner_radius">40dp</dimen>
<dimen name="lockscreen_shade_max_over_scroll_amount">42dp</dimen>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 4995858f95a4..78e719f6289a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -98,10 +98,6 @@
TODO (b/293252410) - change this comment/resource when flag is enabled -->
<integer name="small_land_lockscreen_quick_settings_max_rows">2</integer>
- <!-- If the dp width of the available space is <= this value, potentially adjust the number
- of media recommendation items-->
- <integer name="default_qs_media_rec_width_dp">380</integer>
-
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<!-- The default tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7c370d3bc064..c8e31079fca0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1342,19 +1342,6 @@
<dimen name="qs_media_session_collapsed_legacy_guideline">144dp</dimen>
<dimen name="qs_media_session_collapsed_guideline">168dp</dimen>
- <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
- <dimen name="qs_media_rec_default_width">380dp</dimen>
- <dimen name="qs_media_rec_icon_top_margin">16dp</dimen>
- <dimen name="qs_media_rec_album_icon_size">16dp</dimen>
- <dimen name="qs_media_rec_album_size">88dp</dimen>
- <dimen name="qs_media_rec_album_width">110dp</dimen>
- <dimen name="qs_media_rec_album_height_expanded">108dp</dimen>
- <dimen name="qs_media_rec_album_height_collapsed">77dp</dimen>
- <dimen name="qs_media_rec_album_side_margin">16dp</dimen>
- <dimen name="qs_media_rec_album_bottom_margin">8dp</dimen>
- <dimen name="qs_media_rec_album_title_bottom_margin">22dp</dimen>
- <dimen name="qs_media_rec_album_subtitle_height">12dp</dimen>
-
<!-- Chipbar -->
<!-- (Used for media tap-to-transfer chip for sender device and active unlock) -->
<dimen name="chipbar_outer_padding">16dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4431ddadc8de..7895ff7b90f6 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -895,57 +895,6 @@
<item name="android:textColor">@android:color/system_on_primary_dark</item>
</style>
- <style name="MediaPlayer.Recommendation"/>
-
- <style name="MediaPlayer.Recommendation.Header">
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:layout_marginTop">@dimen/qs_media_padding</item>
- <item name="android:layout_marginStart">@dimen/qs_media_padding</item>
- <item name="android:fontFamily">=@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:singleLine">true</item>
- <item name="android:textSize">14sp</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- </style>
-
- <style name="MediaPlayer.Recommendation.AlbumContainer">
- <item name="android:layout_width">@dimen/qs_media_rec_album_size</item>
- <item name="android:layout_height">@dimen/qs_media_rec_album_size</item>
- <item name="android:background">@drawable/qs_media_light_source</item>
- <item name="android:layout_marginTop">@dimen/qs_media_padding</item>
- <item name="android:layout_marginBottom">@dimen/qs_media_rec_album_bottom_margin</item>
- </style>
-
- <style name="MediaPlayer.Recommendation.AlbumContainer.Updated">
- <item name="android:layout_width">@dimen/qs_media_rec_album_width</item>
- <item name="android:minWidth">@dimen/qs_media_rec_album_width</item>
- <item name="android:minHeight">@dimen/qs_media_rec_album_height_collapsed</item>
- <item name="android:background">@drawable/qs_media_light_source</item>
- <item name="android:layout_marginTop">@dimen/qs_media_info_spacing</item>
- </style>
-
- <style name="MediaPlayer.Recommendation.Album">
- <item name="android:backgroundTint">@color/media_player_album_bg</item>
- </style>
-
- <style name="MediaPlayer.Recommendation.Text">
- <item name="android:layout_width">@dimen/qs_media_rec_album_size</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:maxLines">1</item>
- <item name="android:ellipsize">end</item>
- <item name="android:textSize">14sp</item>
- <item name="android:gravity">start</item>
- </style>
-
- <style name="MediaPlayer.Recommendation.Text.Title">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- </style>
-
- <style name="MediaPlayer.Recommendation.Text.Subtitle">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- </style>
-
-
<!-- Used to style charging animation AVD animation -->
<style name="ChargingAnim" />
diff --git a/packages/SystemUI/res/xml/media_recommendations_collapsed.xml b/packages/SystemUI/res/xml/media_recommendations_collapsed.xml
deleted file mode 100644
index d3be3c7de5ad..000000000000
--- a/packages/SystemUI/res/xml/media_recommendations_collapsed.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 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
- -->
-<ConstraintSet
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- >
-
- <Constraint
- android:id="@+id/sizing_view"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_media_session_height_collapsed"
- />
-
- <Constraint
- android:id="@+id/media_rec_title"
- style="@style/MediaPlayer.Recommendation.Header"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"/>
-
- <Constraint
- android:id="@+id/media_cover1_container"
- style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
- android:layout_height="@dimen/qs_media_rec_album_height_collapsed"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
- android:layout_marginStart="@dimen/qs_media_padding"
- app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/media_cover2_container"/>
-
-
- <Constraint
- android:id="@+id/media_cover2_container"
- style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
- android:layout_height="@dimen/qs_media_rec_album_height_collapsed"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
- app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
- app:layout_constraintStart_toEndOf="@id/media_cover1_container"
- app:layout_constraintEnd_toStartOf="@id/media_cover3_container"/>
-
- <Constraint
- android:id="@+id/media_cover3_container"
- style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
- android:layout_height="@dimen/qs_media_rec_album_height_collapsed"
- android:layout_marginEnd="@dimen/qs_media_padding"
- app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
- app:layout_constraintStart_toEndOf="@id/media_cover2_container"
- app:layout_constraintEnd_toEndOf="parent"/>
-
-
-</ConstraintSet>
diff --git a/packages/SystemUI/res/xml/media_recommendations_expanded.xml b/packages/SystemUI/res/xml/media_recommendations_expanded.xml
deleted file mode 100644
index 88c70552e9e8..000000000000
--- a/packages/SystemUI/res/xml/media_recommendations_expanded.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 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
- -->
-<ConstraintSet
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- >
-
- <Constraint
- android:id="@+id/sizing_view"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_media_session_height_expanded"
- />
-
- <Constraint
- android:id="@+id/media_rec_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qs_media_padding"
- android:layout_marginStart="@dimen/qs_media_padding"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:singleLine="true"
- android:textSize="14sp"
- android:textColor="@color/notification_primary_text_color"/>
-
- <Constraint
- android:id="@+id/media_cover1_container"
- style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
- android:layout_height="@dimen/qs_media_rec_album_height_expanded"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
- android:layout_marginStart="@dimen/qs_media_padding"
- app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/media_cover2_container"/>
-
-
- <Constraint
- android:id="@+id/media_cover2_container"
- style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
- android:layout_height="@dimen/qs_media_rec_album_height_expanded"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
- app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
- app:layout_constraintStart_toEndOf="@id/media_cover1_container"
- app:layout_constraintEnd_toStartOf="@id/media_cover3_container"/>
-
- <Constraint
- android:id="@+id/media_cover3_container"
- style="@style/MediaPlayer.Recommendation.AlbumContainer.Updated"
- android:layout_height="@dimen/qs_media_rec_album_height_expanded"
- android:layout_marginEnd="@dimen/qs_media_padding"
- app:layout_constraintTop_toBottomOf="@+id/media_rec_title"
- app:layout_constraintStart_toEndOf="@id/media_cover2_container"
- app:layout_constraintEnd_toEndOf="parent"/>
-
-
-</ConstraintSet>
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
index 097d50bb8f9d..9db7b50905f8 100644
--- a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
@@ -16,7 +16,9 @@
package com.android.systemui.common.domain.interactor
+import android.util.Log
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.display.data.repository.PerDisplayRepository
import com.android.systemui.model.StateChange
import com.android.systemui.model.SysUiState
@@ -26,20 +28,36 @@ import javax.inject.Inject
@SysUISingleton
class SysUIStateDisplaysInteractor
@Inject
-constructor(private val sysUIStateRepository: PerDisplayRepository<SysUiState>) {
+constructor(
+ private val sysUIStateRepository: PerDisplayRepository<SysUiState>,
+ private val displayRepository: DisplayRepository,
+) {
/**
* Sets the flags on the given [targetDisplayId] based on the [stateChanges], while making sure
* that those flags are not set in any other display.
*/
fun setFlagsExclusivelyToDisplay(targetDisplayId: Int, stateChanges: StateChange) {
- sysUIStateRepository.forEachInstance { displayId, instance ->
- if (displayId == targetDisplayId) {
- stateChanges.applyTo(instance)
- } else {
- stateChanges.clearAllChangedFlagsIn(instance)
- }
+ if (SysUiState.DEBUG) {
+ Log.d(TAG, "Setting flags $stateChanges only for display $targetDisplayId")
}
+ displayRepository.displays.value
+ .mapNotNull { sysUIStateRepository[it.displayId] }
+ .apply {
+ // Let's first modify all states, without committing changes ...
+ forEach { displaySysUIState ->
+ if (displaySysUIState.displayId == targetDisplayId) {
+ stateChanges.applyTo(displaySysUIState)
+ } else {
+ stateChanges.clearFrom(displaySysUIState)
+ }
+ }
+ // ... And commit changes at the end
+ forEach { sysuiState -> sysuiState.commitUpdate() }
+ }
}
-}
+ private companion object {
+ const val TAG = "SysUIStateInteractor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
index 04f245e91914..36d3eb51283a 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
@@ -91,18 +91,6 @@ interface PerDisplayRepository<T> {
/** Debug name for this repository, mainly for tracing and logging. */
val debugName: String
-
- /**
- * Invokes the specified action on each instance held by this repository.
- *
- * The action will receive the displayId and the instance associated with that display.
- * If there is no instance for the display, the action is not called.
- */
- fun forEachInstance(action: (Int, T) -> Unit) {
- displayIds.forEach { displayId ->
- get(displayId)?.let { instance -> action(displayId, instance) }
- }
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
deleted file mode 100644
index 0cb36edfd382..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
+++ /dev/null
@@ -1,156 +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.media.controls.domain.pipeline.interactor
-
-import android.content.Context
-import android.content.Intent
-import android.provider.Settings
-import android.util.Log
-import androidx.annotation.VisibleForTesting
-import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.animation.Expandable
-import com.android.systemui.broadcast.BroadcastSender
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.media.controls.data.repository.MediaFilterRepository
-import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
-import com.android.systemui.media.controls.shared.model.MediaRecModel
-import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
-import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
-import com.android.systemui.plugins.ActivityStarter
-import java.net.URISyntaxException
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-
-/** Encapsulates business logic for media recommendation */
-@SysUISingleton
-class MediaRecommendationsInteractor
-@Inject
-constructor(
- @Application applicationScope: CoroutineScope,
- @Application private val applicationContext: Context,
- private val repository: MediaFilterRepository,
- private val mediaDataProcessor: MediaDataProcessor,
- private val broadcastSender: BroadcastSender,
- private val activityStarter: ActivityStarter,
-) {
-
- val recommendations: Flow<MediaRecommendationsModel> =
- repository.smartspaceMediaData.map { toRecommendationsModel(it) }.distinctUntilChanged()
-
- /** Indicates whether the recommendations card is active. */
- val isActive: StateFlow<Boolean> =
- repository.smartspaceMediaData
- .map { it.isActive }
- .distinctUntilChanged()
- .stateIn(applicationScope, SharingStarted.WhileSubscribed(), false)
-
- fun removeMediaRecommendations(key: String, dismissIntent: Intent?, delayMs: Long) {
- mediaDataProcessor.dismissSmartspaceRecommendation(key, delayMs)
- if (dismissIntent == null) {
- Log.w(TAG, "Cannot create dismiss action click action: extras missing dismiss_intent.")
- return
- }
-
- val className = dismissIntent.component?.className
- if (className == EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME) {
- // Dismiss the card Smartspace data through Smartspace trampoline activity.
- applicationContext.startActivity(dismissIntent)
- } else {
- broadcastSender.sendBroadcast(dismissIntent)
- }
- }
-
- fun startSettings() {
- activityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */ true)
- }
-
- fun startClickIntent(expandable: Expandable, intent: Intent) {
- if (shouldActivityOpenInForeground(intent)) {
- // Request to unlock the device if the activity needs to be opened in foreground.
- activityStarter.postStartActivityDismissingKeyguard(
- intent,
- 0 /* delay */,
- expandable.activityTransitionController(
- InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER
- ),
- )
- } else {
- // Otherwise, open the activity in background directly.
- applicationContext.startActivity(intent)
- }
- }
-
- /** Returns if the action will open the activity in foreground. */
- private fun shouldActivityOpenInForeground(intent: Intent): Boolean {
- val intentString = intent.extras?.getString(EXTRAS_SMARTSPACE_INTENT) ?: return false
- try {
- val wrapperIntent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME)
- return wrapperIntent.getBooleanExtra(KEY_SMARTSPACE_OPEN_IN_FOREGROUND, false)
- } catch (e: URISyntaxException) {
- Log.wtf(TAG, "Failed to create intent from URI: $intentString")
- e.printStackTrace()
- }
- return false
- }
-
- private fun toRecommendationsModel(data: SmartspaceMediaData): MediaRecommendationsModel {
- val mediaRecs = ArrayList<MediaRecModel>()
- data.recommendations.forEach {
- with(it) { mediaRecs.add(MediaRecModel(intent, title, subtitle, icon, extras)) }
- }
- return with(data) {
- MediaRecommendationsModel(
- key = targetId,
- uid = getUid(applicationContext),
- packageName = packageName,
- instanceId = instanceId,
- appName = getAppName(applicationContext),
- dismissIntent = dismissIntent,
- areRecommendationsValid = isValid(),
- mediaRecs = mediaRecs,
- )
- }
- }
-
- fun switchToMediaControl(packageName: String) {
- repository.setMediaFromRecPackageName(packageName)
- }
-
- companion object {
-
- private const val TAG = "MediaRecommendationsInteractor"
-
- // TODO (b/237284176) : move AGSA reference out.
- private const val EXTRAS_SMARTSPACE_INTENT =
- "com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT"
- @VisibleForTesting
- const val EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME =
- "com.google.android.apps.gsa.staticplugins.opa.smartspace." +
- "ExportedSmartspaceTrampolineActivity"
-
- private const val KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND"
-
- private val SETTINGS_INTENT = Intent(Settings.ACTION_MEDIA_CONTROLS_SETTINGS)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
deleted file mode 100644
index 4877d18de7ab..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
+++ /dev/null
@@ -1,469 +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.media.controls.ui.binder
-
-import android.app.WallpaperColors
-import android.content.Context
-import android.content.res.ColorStateList
-import android.content.res.Configuration
-import android.graphics.Bitmap
-import android.graphics.Color
-import android.graphics.Matrix
-import android.graphics.drawable.BitmapDrawable
-import android.graphics.drawable.ColorDrawable
-import android.graphics.drawable.Drawable
-import android.graphics.drawable.GradientDrawable
-import android.graphics.drawable.Icon
-import android.graphics.drawable.LayerDrawable
-import android.os.Trace
-import android.util.TypedValue
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.content.res.AppCompatResources
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.animation.Expandable
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
-import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
-import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
-import com.android.systemui.media.controls.ui.animation.textSecondaryFromScheme
-import com.android.systemui.media.controls.ui.controller.MediaViewController
-import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
-import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
-import com.android.systemui.media.controls.ui.viewmodel.MediaRecViewModel
-import com.android.systemui.media.controls.ui.viewmodel.MediaRecommendationsViewModel
-import com.android.systemui.media.controls.ui.viewmodel.MediaRecsCardViewModel
-import com.android.systemui.monet.ColorScheme
-import com.android.systemui.monet.Style
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.res.R
-import com.android.systemui.util.animation.TransitionLayout
-import kotlin.math.min
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.flow.collectLatest
-import com.android.app.tracing.coroutines.launchTraced as launch
-import kotlinx.coroutines.withContext
-
-private const val TAG = "MediaRecommendationsViewBinder"
-private const val MEDIA_REC_SCRIM_START_ALPHA = 0.15f
-private const val MEDIA_REC_SCRIM_END_ALPHA = 1.0f
-
-object MediaRecommendationsViewBinder {
-
- /** Binds recommendations view holder to the given view-model */
- fun bind(
- viewHolder: RecommendationViewHolder,
- viewModel: MediaRecommendationsViewModel,
- mediaViewController: MediaViewController,
- falsingManager: FalsingManager,
- backgroundDispatcher: CoroutineDispatcher,
- mainDispatcher: CoroutineDispatcher,
- ) {
- mediaViewController.recsConfigurationChangeListener = this::updateRecommendationsVisibility
- val cardView = viewHolder.recommendations
- cardView.repeatWhenAttached {
- lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- viewModel.mediaRecsCard.collectLatest { viewModel ->
- viewModel?.let {
- bindRecsCard(
- viewHolder,
- it,
- mediaViewController,
- falsingManager,
- backgroundDispatcher,
- mainDispatcher,
- )
- }
- }
- }
- }
- }
- }
- }
-
- suspend fun bindRecsCard(
- viewHolder: RecommendationViewHolder,
- viewModel: MediaRecsCardViewModel,
- viewController: MediaViewController,
- falsingManager: FalsingManager,
- backgroundDispatcher: CoroutineDispatcher,
- mainDispatcher: CoroutineDispatcher,
- ) {
- // Set up media control location and its listener.
- viewModel.onLocationChanged(viewController.currentEndLocation)
- viewController.locationChangeListener = viewModel.onLocationChanged
-
- // Bind main card.
- viewHolder.recommendations.contentDescription =
- viewModel.contentDescription.invoke(viewController.isGutsVisible)
-
- viewHolder.recommendations.setOnClickListener {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
- viewModel.onClicked(Expandable.fromView(it))
- }
-
- viewHolder.recommendations.setOnLongClickListener {
- if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY))
- return@setOnLongClickListener true
- if (!viewController.isGutsVisible) {
- openGuts(viewHolder, viewModel, viewController)
- } else {
- closeGuts(viewHolder, viewModel, viewController)
- }
- return@setOnLongClickListener true
- }
-
- // Bind colors
- val appIcon = viewModel.mediaRecs.first().appIcon
- fetchAndUpdateColors(viewHolder, appIcon, backgroundDispatcher, mainDispatcher)
- // Bind all recommendations.
- bindRecommendationsList(
- viewHolder,
- viewModel.mediaRecs,
- falsingManager,
- backgroundDispatcher,
- mainDispatcher,
- )
- updateRecommendationsVisibility(viewController, viewHolder.recommendations)
-
- // Set visibility of recommendations.
- val expandedSet: ConstraintSet = viewController.expandedLayout
- val collapsedSet: ConstraintSet = viewController.collapsedLayout
- viewHolder.mediaTitles.forEach {
- setVisibleAndAlpha(expandedSet, it.id, viewModel.areTitlesVisible)
- setVisibleAndAlpha(collapsedSet, it.id, viewModel.areTitlesVisible)
- }
- viewHolder.mediaSubtitles.forEach {
- setVisibleAndAlpha(expandedSet, it.id, viewModel.areSubtitlesVisible)
- setVisibleAndAlpha(collapsedSet, it.id, viewModel.areSubtitlesVisible)
- }
-
- bindRecommendationsGuts(viewHolder, viewModel, viewController, falsingManager)
-
- viewController.refreshState()
- }
-
- private fun bindRecommendationsGuts(
- viewHolder: RecommendationViewHolder,
- viewModel: MediaRecsCardViewModel,
- viewController: MediaViewController,
- falsingManager: FalsingManager,
- ) {
- val gutsViewHolder = viewHolder.gutsViewHolder
- val gutsViewModel = viewModel.gutsMenu
-
- gutsViewHolder.gutsText.text = gutsViewModel.gutsText
- gutsViewHolder.dismissText.visibility = View.VISIBLE
- gutsViewHolder.dismiss.isEnabled = true
- gutsViewHolder.dismiss.setOnClickListener {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
- closeGuts(viewHolder, viewModel, viewController)
- gutsViewModel.onDismissClicked()
- }
-
- gutsViewHolder.cancelText.background = gutsViewModel.cancelTextBackground
- gutsViewHolder.cancel.setOnClickListener {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- closeGuts(viewHolder, viewModel, viewController)
- }
- }
-
- gutsViewHolder.settings.setOnClickListener {
- if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- gutsViewModel.onSettingsClicked.invoke()
- }
- }
-
- gutsViewHolder.setDismissible(gutsViewModel.isDismissEnabled)
- }
-
- private suspend fun bindRecommendationsList(
- viewHolder: RecommendationViewHolder,
- mediaRecs: List<MediaRecViewModel>,
- falsingManager: FalsingManager,
- backgroundDispatcher: CoroutineDispatcher,
- mainDispatcher: CoroutineDispatcher,
- ) {
- mediaRecs.forEachIndexed { index, mediaRecViewModel ->
- if (index >= NUM_REQUIRED_RECOMMENDATIONS) return@forEachIndexed
-
- val appIconView = viewHolder.mediaAppIcons[index]
- appIconView.clearColorFilter()
- appIconView.setImageDrawable(mediaRecViewModel.appIcon)
-
- val mediaCoverContainer = viewHolder.mediaCoverContainers[index]
- mediaCoverContainer.setOnClickListener {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
- mediaRecViewModel.onClicked(Expandable.fromView(it), index)
- }
- mediaCoverContainer.setOnLongClickListener {
- if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY))
- return@setOnLongClickListener true
- (it.parent as View).performLongClick()
- return@setOnLongClickListener true
- }
-
- val mediaCover = viewHolder.mediaCoverItems[index]
- bindRecommendationArtwork(
- mediaCover.context,
- viewHolder,
- mediaRecViewModel,
- index,
- backgroundDispatcher,
- mainDispatcher,
- )
- mediaCover.contentDescription = mediaRecViewModel.contentDescription
-
- val title = viewHolder.mediaTitles[index]
- title.text = mediaRecViewModel.title
-
- val subtitle = viewHolder.mediaSubtitles[index]
- subtitle.text = mediaRecViewModel.subtitle
-
- val progressBar = viewHolder.mediaProgressBars[index]
- progressBar.progress = mediaRecViewModel.progress
- if (mediaRecViewModel.progress == 0) {
- progressBar.visibility = View.GONE
- }
- }
- }
-
- private fun openGuts(
- viewHolder: RecommendationViewHolder,
- viewModel: MediaRecsCardViewModel,
- mediaViewController: MediaViewController,
- ) {
- viewHolder.marquee(true, MediaViewController.GUTS_ANIMATION_DURATION)
- mediaViewController.openGuts()
- viewHolder.recommendations.contentDescription = viewModel.contentDescription.invoke(true)
- viewModel.onLongClicked.invoke()
- }
-
- private fun closeGuts(
- viewHolder: RecommendationViewHolder,
- mediaRecsCardViewModel: MediaRecsCardViewModel,
- mediaViewController: MediaViewController,
- ) {
- viewHolder.marquee(false, MediaViewController.GUTS_ANIMATION_DURATION)
- mediaViewController.closeGuts(false)
- viewHolder.recommendations.contentDescription =
- mediaRecsCardViewModel.contentDescription.invoke(false)
- }
-
- private fun setVisibleAndAlpha(set: ConstraintSet, resId: Int, visible: Boolean) {
- set.setVisibility(resId, if (visible) ConstraintSet.VISIBLE else ConstraintSet.GONE)
- set.setAlpha(resId, if (visible) 1.0f else 0.0f)
- }
-
- fun updateRecommendationsVisibility(
- mediaViewController: MediaViewController,
- cardView: TransitionLayout,
- ) {
- val fittedRecsNum = getNumberOfFittedRecommendations(cardView.context)
- val expandedSet = mediaViewController.expandedLayout
- val collapsedSet = mediaViewController.collapsedLayout
- val mediaCoverContainers = getMediaCoverContainers(cardView)
- // Hide media cover that cannot fit in the recommendation card.
- mediaCoverContainers.forEachIndexed { index, container ->
- setVisibleAndAlpha(expandedSet, container.id, index < fittedRecsNum)
- setVisibleAndAlpha(collapsedSet, container.id, index < fittedRecsNum)
- }
- }
-
- private fun getMediaCoverContainers(cardView: TransitionLayout): List<ViewGroup> {
- return listOf<ViewGroup>(
- cardView.requireViewById(R.id.media_cover1_container),
- cardView.requireViewById(R.id.media_cover2_container),
- cardView.requireViewById(R.id.media_cover3_container),
- )
- }
-
- private fun getNumberOfFittedRecommendations(context: Context): Int {
- val res = context.resources
- val config = res.configuration
- val defaultDpWidth = res.getInteger(R.integer.default_qs_media_rec_width_dp)
- val recCoverWidth =
- (res.getDimensionPixelSize(R.dimen.qs_media_rec_album_width) +
- res.getDimensionPixelSize(R.dimen.qs_media_info_spacing) * 2)
-
- // On landscape, media controls should take half of the screen width.
- val displayAvailableDpWidth =
- if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
- config.screenWidthDp / 2
- } else {
- config.screenWidthDp
- }
- val fittedNum =
- if (displayAvailableDpWidth > defaultDpWidth) {
- val recCoverDefaultWidth =
- res.getDimensionPixelSize(R.dimen.qs_media_rec_default_width)
- recCoverDefaultWidth / recCoverWidth
- } else {
- val displayAvailableWidth =
- TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- displayAvailableDpWidth.toFloat(),
- res.displayMetrics,
- )
- .toInt()
- displayAvailableWidth / recCoverWidth
- }
- return min(fittedNum.toDouble(), NUM_REQUIRED_RECOMMENDATIONS.toDouble()).toInt()
- }
-
- private suspend fun bindRecommendationArtwork(
- context: Context,
- viewHolder: RecommendationViewHolder,
- viewModel: MediaRecViewModel,
- index: Int,
- backgroundDispatcher: CoroutineDispatcher,
- mainDispatcher: CoroutineDispatcher,
- ) {
- val traceCookie = viewHolder.hashCode()
- val traceName = "MediaRecommendationsViewBinder#bindRecommendationArtwork"
- Trace.beginAsyncSection(traceName, traceCookie)
-
- // Capture width & height from views in foreground for artwork scaling in background
- val width = context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_width)
- val height =
- context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_height_expanded)
-
- withContext(backgroundDispatcher) {
- val artwork =
- getRecCoverBackground(
- context,
- viewModel.albumIcon,
- width,
- height,
- backgroundDispatcher,
- )
- withContext(mainDispatcher) {
- val mediaCover = viewHolder.mediaCoverItems[index]
- val coverMatrix = Matrix(mediaCover.imageMatrix)
- coverMatrix.postScale(1.25f, 1.25f, 0.5f * width, 0.5f * height)
- mediaCover.imageMatrix = coverMatrix
- mediaCover.setImageDrawable(artwork)
- }
- }
- }
-
- /** Returns the recommendation album cover of [width]x[height] size. */
- private suspend fun getRecCoverBackground(
- context: Context,
- icon: Icon?,
- width: Int,
- height: Int,
- backgroundDispatcher: CoroutineDispatcher,
- ): Drawable =
- withContext(backgroundDispatcher) {
- return@withContext MediaArtworkHelper.getWallpaperColor(
- context,
- backgroundDispatcher,
- icon,
- TAG,
- )
- ?.let { wallpaperColors ->
- addGradientToRecommendationAlbum(
- context,
- icon!!,
- ColorScheme(wallpaperColors, true, Style.CONTENT),
- width,
- height,
- )
- } ?: ColorDrawable(Color.TRANSPARENT)
- }
-
- private fun addGradientToRecommendationAlbum(
- context: Context,
- artworkIcon: Icon,
- mutableColorScheme: ColorScheme,
- width: Int,
- height: Int,
- ): LayerDrawable {
- // First try scaling rec card using bitmap drawable.
- // If returns null, set drawable bounds.
- val albumArt =
- getScaledRecommendationCover(context, artworkIcon, width, height)
- ?: MediaArtworkHelper.getScaledBackground(context, artworkIcon, width, height)
- val gradient =
- AppCompatResources.getDrawable(context, R.drawable.qs_media_rec_scrim)?.mutate()
- as GradientDrawable
- return MediaArtworkHelper.setUpGradientColorOnDrawable(
- albumArt,
- gradient,
- mutableColorScheme,
- MEDIA_REC_SCRIM_START_ALPHA,
- MEDIA_REC_SCRIM_END_ALPHA,
- )
- }
-
- /** Returns a [Drawable] of a given [artworkIcon] scaled to [width]x[height] size, . */
- private fun getScaledRecommendationCover(
- context: Context,
- artworkIcon: Icon,
- width: Int,
- height: Int,
- ): Drawable? {
- check(width > 0) { "Width must be a positive number but was $width" }
- check(height > 0) { "Height must be a positive number but was $height" }
-
- return if (
- artworkIcon.type == Icon.TYPE_BITMAP || artworkIcon.type == Icon.TYPE_ADAPTIVE_BITMAP
- ) {
- artworkIcon.bitmap?.let {
- val bitmap = Bitmap.createScaledBitmap(it, width, height, false)
- BitmapDrawable(context.resources, bitmap)
- }
- } else {
- null
- }
- }
-
- private suspend fun fetchAndUpdateColors(
- viewHolder: RecommendationViewHolder,
- appIcon: Drawable,
- backgroundDispatcher: CoroutineDispatcher,
- mainDispatcher: CoroutineDispatcher,
- ) =
- withContext(backgroundDispatcher) {
- val colorScheme =
- ColorScheme(WallpaperColors.fromDrawable(appIcon), /* darkTheme= */ true)
- withContext(mainDispatcher) {
- val backgroundColor = surfaceFromScheme(colorScheme)
- val textPrimaryColor = textPrimaryFromScheme(colorScheme)
- val textSecondaryColor = textSecondaryFromScheme(colorScheme)
-
- viewHolder.cardTitle.setTextColor(textPrimaryColor)
- viewHolder.recommendations.setBackgroundTintList(
- ColorStateList.valueOf(backgroundColor)
- )
-
- viewHolder.mediaTitles.forEach { it.setTextColor(textPrimaryColor) }
- viewHolder.mediaSubtitles.forEach { it.setTextColor(textSecondaryColor) }
- viewHolder.mediaProgressBars.forEach {
- it.progressTintList = ColorStateList.valueOf(textPrimaryColor)
- }
-
- viewHolder.gutsViewHolder.setColors(colorScheme)
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 7b1ae57ed421..ac6343c6bb64 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -61,14 +61,12 @@ import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.ui.binder.MediaControlViewBinder
-import com.android.systemui.media.controls.ui.binder.MediaRecommendationsViewBinder
import com.android.systemui.media.controls.ui.util.MediaViewModelCallback
import com.android.systemui.media.controls.ui.util.MediaViewModelListUpdateCallback
import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.controls.ui.view.MediaScrollView
import com.android.systemui.media.controls.ui.view.MediaViewHolder
-import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.ui.viewmodel.MediaCarouselViewModel
import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel
import com.android.systemui.media.controls.util.MediaUiEventLogger
@@ -478,41 +476,10 @@ constructor(
MediaPlayerData.isSwipedAway = false
}
- override fun onSmartspaceMediaDataLoaded(
- key: String,
- data: SmartspaceMediaData,
- shouldPrioritize: Boolean,
- ) {
- debugLogger.logRecommendationLoaded(key, data.isActive)
- // Log the case where the hidden media carousel with the existed inactive resume
- // media is shown by the Smartspace signal.
- if (data.isActive) {
- addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
- } else {
- // Handle update to inactive as a removal
- onSmartspaceMediaDataRemoved(data.targetId, immediately = true)
- }
- MediaPlayerData.isSwipedAway = false
- }
-
override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
debugLogger.logMediaRemoved(key, userInitiated)
removePlayer(key, userInitiated = userInitiated)
}
-
- override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
- debugLogger.logRecommendationRemoved(key, immediately)
- if (immediately || isReorderingAllowed) {
- removePlayer(key)
- if (!immediately) {
- // Although it wasn't requested, we were able to process the removal
- // immediately since reordering is allowed. So, notify hosts to update
- updateHostVisibility()
- }
- } else {
- keysNeedRemoval.add(key)
- }
- }
}
)
}
@@ -655,22 +622,6 @@ constructor(
mediaContent.addView(viewHolder.player, position)
controllerById[commonViewModel.instanceId.toString()] = viewController
}
- is MediaCommonViewModel.MediaRecommendations -> {
- val viewHolder =
- RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent)
- viewController.attachRecommendations(viewHolder)
- viewController.recommendationViewHolder?.recommendations?.layoutParams = lp
- MediaRecommendationsViewBinder.bind(
- viewHolder,
- commonViewModel.recsViewModel,
- viewController,
- falsingManager,
- backgroundDispatcher,
- mainDispatcher,
- )
- mediaContent.addView(viewHolder.recommendations, position)
- controllerById[commonViewModel.key] = viewController
- }
}
viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded)
updateViewControllerToState(viewController, noAnimation = true)
@@ -695,21 +646,10 @@ constructor(
}
private fun onRemoved(commonViewModel: MediaCommonViewModel) {
- val id =
- when (commonViewModel) {
- is MediaCommonViewModel.MediaControl -> commonViewModel.instanceId.toString()
- is MediaCommonViewModel.MediaRecommendations -> commonViewModel.key
- }
+ val id = (commonViewModel as MediaCommonViewModel.MediaControl).instanceId.toString()
controllerById.remove(id)?.let {
- when (commonViewModel) {
- is MediaCommonViewModel.MediaControl -> {
- mediaCarouselScrollHandler.onPrePlayerRemoved(it.mediaViewHolder!!.player)
- mediaContent.removeView(it.mediaViewHolder!!.player)
- }
- is MediaCommonViewModel.MediaRecommendations -> {
- mediaContent.removeView(it.recommendationViewHolder!!.recommendations)
- }
- }
+ mediaCarouselScrollHandler.onPrePlayerRemoved(it.mediaViewHolder!!.player)
+ mediaContent.removeView(it.mediaViewHolder!!.player)
it.onDestroy()
mediaCarouselScrollHandler.onPlayersChanged()
updatePageIndicator()
@@ -718,21 +658,10 @@ constructor(
}
private fun onMoved(commonViewModel: MediaCommonViewModel, from: Int, to: Int) {
- val id =
- when (commonViewModel) {
- is MediaCommonViewModel.MediaControl -> commonViewModel.instanceId.toString()
- is MediaCommonViewModel.MediaRecommendations -> commonViewModel.key
- }
+ val id = (commonViewModel as MediaCommonViewModel.MediaControl).instanceId.toString()
controllerById[id]?.let {
mediaContent.removeViewAt(from)
- when (commonViewModel) {
- is MediaCommonViewModel.MediaControl -> {
- mediaContent.addView(it.mediaViewHolder!!.player, to)
- }
- is MediaCommonViewModel.MediaRecommendations -> {
- mediaContent.addView(it.recommendationViewHolder!!.recommendations, to)
- }
- }
+ mediaContent.addView(it.mediaViewHolder!!.player, to)
}
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
@@ -746,11 +675,9 @@ constructor(
val viewIds =
viewModels
.map { mediaCommonViewModel ->
- when (mediaCommonViewModel) {
- is MediaCommonViewModel.MediaControl ->
- mediaCommonViewModel.instanceId.toString()
- is MediaCommonViewModel.MediaRecommendations -> mediaCommonViewModel.key
- }
+ (mediaCommonViewModel as MediaCommonViewModel.MediaControl)
+ .instanceId
+ .toString()
}
.toHashSet()
controllerById
@@ -758,7 +685,6 @@ constructor(
.forEach {
mediaCarouselScrollHandler.onPrePlayerRemoved(it.value.mediaViewHolder?.player)
mediaContent.removeView(it.value.mediaViewHolder?.player)
- mediaContent.removeView(it.value.recommendationViewHolder?.recommendations)
it.value.onDestroy()
mediaCarouselScrollHandler.onPlayersChanged()
updatePageIndicator()
@@ -808,9 +734,6 @@ constructor(
mediaContent.removeAllViews()
for (mediaPlayer in MediaPlayerData.players()) {
mediaPlayer.mediaViewHolder?.let { mediaContent.addView(it.player) }
- ?: mediaPlayer.recommendationViewHolder?.let {
- mediaContent.addView(it.recommendations)
- }
}
mediaCarouselScrollHandler.onPlayersChanged()
mediaControlChipInteractor.updateMediaControlChipModelLegacy(
@@ -980,67 +903,6 @@ constructor(
return MediaViewHolder.create(LayoutInflater.from(context), mediaContent)
}
- private fun addSmartspaceMediaRecommendations(
- key: String,
- data: SmartspaceMediaData,
- shouldPrioritize: Boolean,
- ) =
- traceSection("MediaCarouselController#addSmartspaceMediaRecommendations") {
- if (DEBUG) Log.d(TAG, "Updating smartspace target in carousel")
- MediaPlayerData.getMediaPlayer(key)?.let {
- Log.w(TAG, "Skip adding smartspace target in carousel")
- return
- }
-
- val existingSmartspaceMediaKey = MediaPlayerData.smartspaceMediaKey()
- existingSmartspaceMediaKey?.let {
- val removedPlayer =
- removePlayer(existingSmartspaceMediaKey, dismissMediaData = false)
- removedPlayer?.run {
- debugLogger.logPotentialMemoryLeak(existingSmartspaceMediaKey)
- onDestroy()
- }
- }
-
- val newRecs = mediaControlPanelFactory.get()
- newRecs.attachRecommendation(
- RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent)
- )
- newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
- val lp =
- LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- )
- newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp)
- newRecs.bindRecommendation(data)
- val curVisibleMediaKey =
- MediaPlayerData.visiblePlayerKeys()
- .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
- MediaPlayerData.addMediaRecommendation(
- key,
- data,
- newRecs,
- shouldPrioritize,
- systemClock,
- debugLogger,
- )
- updateViewControllerToState(newRecs.mediaViewController, noAnimation = true)
- reorderAllPlayers(curVisibleMediaKey)
- updatePageIndicator()
- mediaFrame.requiresRemeasuring = true
- // Check postcondition: mediaContent should have the same number of children as there
- // are elements in mediaPlayers.
- if (MediaPlayerData.players().size != mediaContent.childCount) {
- Log.e(
- TAG,
- "Size of players list and number of views in carousel are out of sync. " +
- "Players size is ${MediaPlayerData.players().size}. " +
- "View count is ${mediaContent.childCount}.",
- )
- }
- }
-
fun removePlayer(
key: String,
dismissMediaData: Boolean = true,
@@ -1057,7 +919,6 @@ constructor(
return removed?.apply {
mediaCarouselScrollHandler.onPrePlayerRemoved(removed.mediaViewHolder?.player)
mediaContent.removeView(removed.mediaViewHolder?.player)
- mediaContent.removeView(removed.recommendationViewHolder?.recommendations)
removed.onDestroy()
mediaCarouselScrollHandler.onPlayersChanged()
mediaControlChipInteractor.updateMediaControlChipModelLegacy(
@@ -1095,31 +956,18 @@ constructor(
val mediaDataList = MediaPlayerData.mediaData()
// Do not loop through the original list of media data because the re-addition of media data
// is being executed in background thread.
- mediaDataList.forEach { (key, data, isSsMediaRec) ->
- if (isSsMediaRec) {
- val smartspaceMediaData = MediaPlayerData.smartspaceMediaData
+ mediaDataList.forEach { (key, data, _) ->
+ val isSsReactivated = MediaPlayerData.isSsReactivated(key)
+ if (recreateMedia) {
removePlayer(key, dismissMediaData = false, dismissRecommendation = false)
- smartspaceMediaData?.let {
- addSmartspaceMediaRecommendations(
- it.targetId,
- it,
- MediaPlayerData.shouldPrioritizeSs,
- )
- }
- onUiExecutionEnd.run()
- } else {
- val isSsReactivated = MediaPlayerData.isSsReactivated(key)
- if (recreateMedia) {
- removePlayer(key, dismissMediaData = false, dismissRecommendation = false)
- }
- addOrUpdatePlayer(
- key = key,
- oldKey = null,
- data = data,
- isSsReactivated = isSsReactivated,
- onUiExecutionEnd = onUiExecutionEnd,
- )
}
+ addOrUpdatePlayer(
+ key = key,
+ oldKey = null,
+ data = data,
+ isSsReactivated = isSsReactivated,
+ onUiExecutionEnd = onUiExecutionEnd,
+ )
}
}
@@ -1129,12 +977,8 @@ constructor(
if (recreateMedia) {
mediaContent.removeAllViews()
commonViewModels.forEachIndexed { index, viewModel ->
- when (viewModel) {
- is MediaCommonViewModel.MediaControl ->
- controllerById[viewModel.instanceId.toString()]?.onDestroy()
- is MediaCommonViewModel.MediaRecommendations ->
- controllerById[viewModel.key]?.onDestroy()
- }
+ val mediaControlViewModel = (viewModel as MediaCommonViewModel.MediaControl)
+ controllerById[mediaControlViewModel.instanceId.toString()]?.onDestroy()
onAdded(viewModel, index, configChanged = true)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
index 5d62c022efba..365389107648 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
@@ -64,28 +64,6 @@ constructor(@MediaCarouselControllerLog private val buffer: LogBuffer) {
{ "removing player $str1, by user $bool1" },
)
- fun logRecommendationLoaded(key: String, isActive: Boolean) =
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- {
- str1 = key
- bool1 = isActive
- },
- { "add recommendation $str1, active $bool1" },
- )
-
- fun logRecommendationRemoved(key: String, immediately: Boolean) =
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- {
- str1 = key
- bool1 = immediately
- },
- { "removing recommendation $str1, immediate=$bool1" },
- )
-
fun logCarouselHidden() = buffer.log(TAG, LogLevel.DEBUG, {}, { "hiding carousel" })
fun logCarouselVisible() = buffer.log(TAG, LogLevel.DEBUG, {}, { "showing carousel" })
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index a6bf5f43698b..006eb203a669 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -22,7 +22,6 @@ import static com.android.settingslib.flags.Flags.legacyLeAudioSharing;
import static com.android.systemui.Flags.communalHub;
import static com.android.systemui.Flags.mediaLockscreenLaunchAnimation;
import static com.android.systemui.media.controls.domain.pipeline.MediaActionsKt.getNotificationActions;
-import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_END_ALPHA;
import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY;
import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_START_ALPHA;
@@ -35,24 +34,17 @@ import android.app.ActivityOptions;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.app.WallpaperColors;
-import android.app.smartspace.SmartspaceAction;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
@@ -69,14 +61,12 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.ImageButton;
import android.widget.ImageView;
-import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -105,7 +95,6 @@ import com.android.systemui.media.controls.shared.model.MediaAction;
import com.android.systemui.media.controls.shared.model.MediaButton;
import com.android.systemui.media.controls.shared.model.MediaData;
import com.android.systemui.media.controls.shared.model.MediaDeviceData;
-import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
import com.android.systemui.media.controls.ui.animation.AnimationBindHandler;
import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition;
import com.android.systemui.media.controls.ui.animation.MediaColorSchemesKt;
@@ -113,7 +102,6 @@ import com.android.systemui.media.controls.ui.animation.MetadataAnimationHandler
import com.android.systemui.media.controls.ui.binder.SeekBarObserver;
import com.android.systemui.media.controls.ui.view.GutsViewHolder;
import com.android.systemui.media.controls.ui.view.MediaViewHolder;
-import com.android.systemui.media.controls.ui.view.RecommendationViewHolder;
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel;
import com.android.systemui.media.controls.util.MediaDataUtils;
import com.android.systemui.media.controls.util.MediaUiEventLogger;
@@ -143,14 +131,12 @@ import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.animation.TransitionLayout;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.GlobalSettings;
-import com.android.systemui.util.time.SystemClock;
import dagger.Lazy;
import kotlin.Triple;
import kotlin.Unit;
-import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@@ -165,17 +151,6 @@ public class MediaControlPanel {
protected static final String TAG = "MediaControlPanel";
private static final float DISABLED_ALPHA = 0.38f;
- private static final String EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME = "com.google"
- + ".android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity";
- private static final String EXTRAS_SMARTSPACE_INTENT =
- "com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT";
- private static final String KEY_SMARTSPACE_ARTIST_NAME = "artist_name";
- private static final String KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND";
-
- private static final float REC_MEDIA_COVER_SCALE_FACTOR = 1.25f;
- private static final float MEDIA_REC_SCRIM_START_ALPHA = 0.15f;
- private static final float MEDIA_REC_SCRIM_END_ALPHA = 1.0f;
-
private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS);
// Buttons to show in small player when using semantic actions
@@ -215,17 +190,14 @@ public class MediaControlPanel {
private Context mContext;
private MediaViewHolder mMediaViewHolder;
- private RecommendationViewHolder mRecommendationViewHolder;
private String mKey;
private MediaData mMediaData;
- private SmartspaceMediaData mRecommendationData;
private MediaViewController mMediaViewController;
private MediaSession.Token mToken;
private MediaController mController;
private Lazy<MediaDataManager> mMediaDataManagerLazy;
// Uid for the media app.
protected int mUid = Process.INVALID_UID;
- private int mSmartspaceMediaItemsCount;
private MediaCarouselController mMediaCarouselController;
private final MediaOutputDialogManager mMediaOutputDialogManager;
private final FalsingManager mFalsingManager;
@@ -241,7 +213,6 @@ public class MediaControlPanel {
private final NotificationLockscreenUserManager mLockscreenUserManager;
// Used for logging.
- private SystemClock mSystemClock;
private MediaUiEventLogger mLogger;
private InstanceId mInstanceId;
private String mPackageName;
@@ -310,7 +281,6 @@ public class MediaControlPanel {
MediaOutputDialogManager mediaOutputDialogManager,
MediaCarouselController mediaCarouselController,
FalsingManager falsingManager,
- SystemClock systemClock,
MediaUiEventLogger logger,
KeyguardStateController keyguardStateController,
ActivityIntentHelper activityIntentHelper,
@@ -330,7 +300,6 @@ public class MediaControlPanel {
mMediaOutputDialogManager = mediaOutputDialogManager;
mMediaCarouselController = mediaCarouselController;
mFalsingManager = falsingManager;
- mSystemClock = systemClock;
mLogger = logger;
mKeyguardStateController = keyguardStateController;
mActivityIntentHelper = activityIntentHelper;
@@ -373,16 +342,6 @@ public class MediaControlPanel {
}
/**
- * Get the recommendation view holder used to display Smartspace media recs.
- *
- * @return the recommendation view holder
- */
- @Nullable
- public RecommendationViewHolder getRecommendationViewHolder() {
- return mRecommendationViewHolder;
- }
-
- /**
* Get the view controller used to display media controls
*
* @return the media view controller
@@ -465,7 +424,7 @@ public class MediaControlPanel {
mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
mSeekBarViewModel.setScrubbingChangeListener(mScrubbingChangeListener);
mSeekBarViewModel.setEnabledChangeListener(mEnabledChangeListener);
- mMediaViewController.attach(player, MediaViewController.TYPE.PLAYER);
+ mMediaViewController.attach(player);
vh.getPlayer().setOnLongClickListener(v -> {
if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true;
@@ -522,26 +481,6 @@ public class MediaControlPanel {
return result;
}
- /** Attaches the recommendations to the recommendation view holder. */
- public void attachRecommendation(RecommendationViewHolder vh) {
- mRecommendationViewHolder = vh;
- TransitionLayout recommendations = vh.getRecommendations();
-
- mMediaViewController.attach(recommendations, MediaViewController.TYPE.RECOMMENDATION);
- mMediaViewController.configurationChangeListener = this::updateRecommendationsVisibility;
-
- mRecommendationViewHolder.getRecommendations().setOnLongClickListener(v -> {
- if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true;
- if (!mMediaViewController.isGutsVisible()) {
- openGuts();
- return true;
- } else {
- closeGuts();
- return true;
- }
- });
- }
-
/** Bind this player view based on the data given. */
public void bindPlayer(@NonNull MediaData data, String key) {
SceneContainerFlag.assertInLegacyMode();
@@ -868,24 +807,6 @@ public class MediaControlPanel {
mMediaViewHolder.getPlayer().setContentDescription(contentDescription);
}
- private void bindRecommendationContentDescription(SmartspaceMediaData data) {
- if (mRecommendationViewHolder == null) {
- return;
- }
-
- CharSequence contentDescription;
- if (mMediaViewController.isGutsVisible()) {
- contentDescription =
- mRecommendationViewHolder.getGutsViewHolder().getGutsText().getText();
- } else if (data != null) {
- contentDescription = mContext.getString(R.string.controls_media_smartspace_rec_header);
- } else {
- contentDescription = null;
- }
-
- mRecommendationViewHolder.getRecommendations().setContentDescription(contentDescription);
- }
-
private void bindArtworkAndColors(MediaData data, String key, boolean updateBackground) {
final int traceCookie = data.hashCode();
final String traceName = "MediaControlPanel#bindArtworkAndColors<" + key + ">";
@@ -993,62 +914,6 @@ public class MediaControlPanel {
});
}
- private void bindRecommendationArtwork(
- SmartspaceAction recommendation,
- String packageName,
- int itemIndex
- ) {
- final int traceCookie = recommendation.hashCode();
- final String traceName =
- "MediaControlPanel#bindRecommendationArtwork<" + packageName + ">";
- Trace.beginAsyncSection(traceName, traceCookie);
-
- // Capture width & height from views in foreground for artwork scaling in background
- int width = mContext.getResources().getDimensionPixelSize(R.dimen.qs_media_rec_album_width);
- int height = mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_media_rec_album_height_expanded);
-
- mBackgroundExecutor.execute(() -> {
- // Album art
- ColorScheme mutableColorScheme = null;
- Drawable artwork;
- Icon artworkIcon = recommendation.getIcon();
- WallpaperColors wallpaperColors = getWallpaperColor(artworkIcon);
- if (wallpaperColors != null) {
- mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
- artwork = addGradientToRecommendationAlbum(artworkIcon, mutableColorScheme, width,
- height);
- } else {
- artwork = new ColorDrawable(Color.TRANSPARENT);
- }
-
- mMainExecutor.execute(() -> {
- // Bind the artwork drawable to media cover.
- ImageView mediaCover =
- mRecommendationViewHolder.getMediaCoverItems().get(itemIndex);
- // Rescale media cover
- Matrix coverMatrix = new Matrix(mediaCover.getImageMatrix());
- coverMatrix.postScale(REC_MEDIA_COVER_SCALE_FACTOR, REC_MEDIA_COVER_SCALE_FACTOR,
- 0.5f * width, 0.5f * height);
- mediaCover.setImageMatrix(coverMatrix);
- mediaCover.setImageDrawable(artwork);
-
- // Set up the app icon.
- ImageView appIconView = mRecommendationViewHolder.getMediaAppIcons().get(itemIndex);
- appIconView.clearColorFilter();
- try {
- Drawable icon = mContext.getPackageManager()
- .getApplicationIcon(packageName);
- appIconView.setImageDrawable(icon);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Cannot find icon for package " + packageName, e);
- appIconView.setImageResource(R.drawable.ic_music_note);
- }
- Trace.endAsyncSection(traceName, traceCookie);
- });
- });
- }
-
// This method should be called from a background thread. WallpaperColors.fromBitmap takes a
// good amount of time. We do that work on the background executor to avoid stalling animations
// on the UI Thread.
@@ -1088,21 +953,6 @@ public class MediaControlPanel {
MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY, MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY);
}
- @VisibleForTesting
- protected LayerDrawable addGradientToRecommendationAlbum(Icon artworkIcon,
- ColorScheme mutableColorScheme, int width, int height) {
- // First try scaling rec card using bitmap drawable.
- // If returns null, set drawable bounds.
- Drawable albumArt = getScaledRecommendationCover(artworkIcon, width, height);
- if (albumArt == null) {
- albumArt = getScaledBackground(artworkIcon, width, height);
- }
- GradientDrawable gradient = (GradientDrawable) mContext.getDrawable(
- R.drawable.qs_media_rec_scrim).mutate();
- return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme,
- MEDIA_REC_SCRIM_START_ALPHA, MEDIA_REC_SCRIM_END_ALPHA);
- }
-
private LayerDrawable setupGradientColorOnDrawable(Drawable albumArt, GradientDrawable gradient,
ColorScheme mutableColorScheme, float startAlpha, float endAlpha) {
int startColor;
@@ -1465,258 +1315,6 @@ public class MediaControlPanel {
return controller;
}
- /** Bind this recommendation view based on the given data. */
- public void bindRecommendation(@NonNull SmartspaceMediaData data) {
- if (mRecommendationViewHolder == null) {
- return;
- }
-
- if (!data.isValid()) {
- Log.e(TAG, "Received an invalid recommendation list; returning");
- return;
- }
-
- if (Trace.isEnabled()) {
- Trace.traceBegin(Trace.TRACE_TAG_APP,
- "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">");
- }
-
- mRecommendationData = data;
- mPackageName = data.getPackageName();
- mInstanceId = data.getInstanceId();
-
- // Set up recommendation card's header.
- ApplicationInfo applicationInfo;
- try {
- applicationInfo = mContext.getPackageManager()
- .getApplicationInfo(data.getPackageName(), 0 /* flags */);
- mUid = applicationInfo.uid;
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Fail to get media recommendation's app info", e);
- Trace.endSection();
- return;
- }
-
- CharSequence appName = data.getAppName(mContext);
- if (appName == null) {
- Log.w(TAG, "Fail to get media recommendation's app name");
- Trace.endSection();
- return;
- }
-
- PackageManager packageManager = mContext.getPackageManager();
- // Set up media source app's logo.
- Drawable icon = packageManager.getApplicationIcon(applicationInfo);
- fetchAndUpdateRecommendationColors(icon);
-
- // Set up media rec card's tap action if applicable.
- TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
- setSmartspaceRecItemOnClickListener(recommendationCard, data.getCardAction(),
- /* interactedSubcardRank */ -1);
- bindRecommendationContentDescription(data);
-
- List<ImageView> mediaCoverItems = mRecommendationViewHolder.getMediaCoverItems();
- List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers();
- List<SmartspaceAction> recommendations = data.getValidRecommendations();
-
- boolean hasTitle = false;
- boolean hasSubtitle = false;
- int fittedRecsNum = getNumberOfFittedRecommendations();
- for (int itemIndex = 0; itemIndex < NUM_REQUIRED_RECOMMENDATIONS; itemIndex++) {
- SmartspaceAction recommendation = recommendations.get(itemIndex);
-
- // Set up media item cover.
- ImageView mediaCoverImageView = mediaCoverItems.get(itemIndex);
- bindRecommendationArtwork(recommendation, data.getPackageName(), itemIndex);
-
- // Set up the media item's click listener if applicable.
- ViewGroup mediaCoverContainer = mediaCoverContainers.get(itemIndex);
- setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation, itemIndex);
- // Bubble up the long-click event to the card.
- mediaCoverContainer.setOnLongClickListener(v -> {
- if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) return true;
- View parent = (View) v.getParent();
- if (parent != null) {
- parent.performLongClick();
- }
- return true;
- });
-
- // Set up the accessibility label for the media item.
- String artistName = recommendation.getExtras()
- .getString(KEY_SMARTSPACE_ARTIST_NAME, "");
- if (artistName.isEmpty()) {
- mediaCoverImageView.setContentDescription(
- mContext.getString(
- R.string.controls_media_smartspace_rec_item_no_artist_description,
- recommendation.getTitle(), appName));
- } else {
- mediaCoverImageView.setContentDescription(
- mContext.getString(
- R.string.controls_media_smartspace_rec_item_description,
- recommendation.getTitle(), artistName, appName));
- }
-
- // Set up title
- CharSequence title = recommendation.getTitle();
- hasTitle |= !TextUtils.isEmpty(title);
- TextView titleView = mRecommendationViewHolder.getMediaTitles().get(itemIndex);
- titleView.setText(title);
-
- // Set up subtitle
- // It would look awkward to show a subtitle if we don't have a title.
- boolean shouldShowSubtitleText = !TextUtils.isEmpty(title);
- CharSequence subtitle = shouldShowSubtitleText ? recommendation.getSubtitle() : "";
- hasSubtitle |= !TextUtils.isEmpty(subtitle);
- TextView subtitleView = mRecommendationViewHolder.getMediaSubtitles().get(itemIndex);
- subtitleView.setText(subtitle);
-
- // Set up progress bar
- SeekBar mediaProgressBar =
- mRecommendationViewHolder.getMediaProgressBars().get(itemIndex);
- TextView mediaSubtitle = mRecommendationViewHolder.getMediaSubtitles().get(itemIndex);
- // show progress bar if the recommended album is played.
- Double progress = MediaDataUtils.getDescriptionProgress(recommendation.getExtras());
- if (progress == null || progress <= 0.0) {
- mediaProgressBar.setVisibility(View.GONE);
- mediaSubtitle.setVisibility(View.VISIBLE);
- } else {
- mediaProgressBar.setProgress((int) (progress * 100));
- mediaProgressBar.setVisibility(View.VISIBLE);
- mediaSubtitle.setVisibility(View.GONE);
- }
- }
- mSmartspaceMediaItemsCount = NUM_REQUIRED_RECOMMENDATIONS;
-
- // If there's no subtitles and/or titles for any of the albums, hide those views.
- ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
- ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
- final boolean titlesVisible = hasTitle;
- final boolean subtitlesVisible = hasSubtitle;
- mRecommendationViewHolder.getMediaTitles().forEach((titleView) -> {
- setVisibleAndAlpha(expandedSet, titleView.getId(), titlesVisible);
- setVisibleAndAlpha(collapsedSet, titleView.getId(), titlesVisible);
- });
- mRecommendationViewHolder.getMediaSubtitles().forEach((subtitleView) -> {
- setVisibleAndAlpha(expandedSet, subtitleView.getId(), subtitlesVisible);
- setVisibleAndAlpha(collapsedSet, subtitleView.getId(), subtitlesVisible);
- });
-
- // Media covers visibility.
- setMediaCoversVisibility(fittedRecsNum);
-
- // Guts
- Runnable onDismissClickedRunnable = () -> {
- closeGuts();
- mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
- data.getTargetId(), MediaViewController.GUTS_ANIMATION_DURATION + 100L);
-
- Intent dismissIntent = data.getDismissIntent();
- if (dismissIntent == null) {
- Log.w(TAG, "Cannot create dismiss action click action: "
- + "extras missing dismiss_intent.");
- return;
- }
-
- if (dismissIntent.getComponent() != null
- && dismissIntent.getComponent().getClassName()
- .equals(EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME)) {
- // Dismiss the card Smartspace data through Smartspace trampoline activity.
- mContext.startActivity(dismissIntent);
- } else {
- mBroadcastSender.sendBroadcast(dismissIntent);
- }
- };
- bindGutsMenuCommon(
- /* isDismissible= */ true,
- appName.toString(),
- mRecommendationViewHolder.getGutsViewHolder(),
- onDismissClickedRunnable);
-
- mController = null;
- if (mMetadataAnimationHandler == null || !mMetadataAnimationHandler.isRunning()) {
- mMediaViewController.refreshState();
- }
- Trace.endSection();
- }
-
- private Unit updateRecommendationsVisibility() {
- int fittedRecsNum = getNumberOfFittedRecommendations();
- setMediaCoversVisibility(fittedRecsNum);
- return Unit.INSTANCE;
- }
-
- private void setMediaCoversVisibility(int fittedRecsNum) {
- ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
- ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
- List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers();
- // Hide media cover that cannot fit in the recommendation card.
- for (int itemIndex = 0; itemIndex < NUM_REQUIRED_RECOMMENDATIONS; itemIndex++) {
- setVisibleAndAlpha(expandedSet, mediaCoverContainers.get(itemIndex).getId(),
- itemIndex < fittedRecsNum);
- setVisibleAndAlpha(collapsedSet, mediaCoverContainers.get(itemIndex).getId(),
- itemIndex < fittedRecsNum);
- }
- }
-
- @VisibleForTesting
- protected int getNumberOfFittedRecommendations() {
- Resources res = mContext.getResources();
- Configuration config = res.getConfiguration();
- int defaultDpWidth = res.getInteger(R.integer.default_qs_media_rec_width_dp);
- int recCoverWidth = res.getDimensionPixelSize(R.dimen.qs_media_rec_album_width)
- + res.getDimensionPixelSize(R.dimen.qs_media_info_spacing) * 2;
-
- // On landscape, media controls should take half of the screen width.
- int displayAvailableDpWidth = config.screenWidthDp;
- if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
- displayAvailableDpWidth = displayAvailableDpWidth / 2;
- }
- int fittedNum;
- if (displayAvailableDpWidth > defaultDpWidth) {
- int recCoverDefaultWidth = res.getDimensionPixelSize(
- R.dimen.qs_media_rec_default_width);
- fittedNum = recCoverDefaultWidth / recCoverWidth;
- } else {
- int displayAvailableWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- displayAvailableDpWidth, res.getDisplayMetrics());
- fittedNum = displayAvailableWidth / recCoverWidth;
- }
- return Math.min(fittedNum, NUM_REQUIRED_RECOMMENDATIONS);
- }
-
- private void fetchAndUpdateRecommendationColors(Drawable appIcon) {
- mBackgroundExecutor.execute(() -> {
- ColorScheme colorScheme = new ColorScheme(
- WallpaperColors.fromDrawable(appIcon), /* darkTheme= */ true);
- mMainExecutor.execute(() -> setRecommendationColors(colorScheme));
- });
- }
-
- private void setRecommendationColors(ColorScheme colorScheme) {
- if (mRecommendationViewHolder == null) {
- return;
- }
-
- int backgroundColor = MediaColorSchemesKt.surfaceFromScheme(colorScheme);
- int textPrimaryColor = MediaColorSchemesKt.textPrimaryFromScheme(colorScheme);
- int textSecondaryColor = MediaColorSchemesKt.textSecondaryFromScheme(colorScheme);
-
- mRecommendationViewHolder.getCardTitle().setTextColor(textPrimaryColor);
-
- mRecommendationViewHolder.getRecommendations()
- .setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
- mRecommendationViewHolder.getMediaTitles().forEach(
- (title) -> title.setTextColor(textPrimaryColor));
- mRecommendationViewHolder.getMediaSubtitles().forEach(
- (subtitle) -> subtitle.setTextColor(textSecondaryColor));
- mRecommendationViewHolder.getMediaProgressBars().forEach(
- (progressBar) -> progressBar.setProgressTintList(
- ColorStateList.valueOf(textPrimaryColor)));
-
- mRecommendationViewHolder.getGutsViewHolder().setColors(colorScheme);
- }
-
private void bindGutsMenuCommon(
boolean isDismissible,
String appName,
@@ -1772,14 +1370,10 @@ public class MediaControlPanel {
public void closeGuts(boolean immediate) {
if (mMediaViewHolder != null) {
mMediaViewHolder.marquee(false, mMediaViewController.GUTS_ANIMATION_DURATION);
- } else if (mRecommendationViewHolder != null) {
- mRecommendationViewHolder.marquee(false, mMediaViewController.GUTS_ANIMATION_DURATION);
}
mMediaViewController.closeGuts(immediate);
if (mMediaViewHolder != null) {
bindPlayerContentDescription(mMediaData);
- } else if (mRecommendationViewHolder != null) {
- bindRecommendationContentDescription(mRecommendationData);
}
}
@@ -1790,14 +1384,10 @@ public class MediaControlPanel {
private void openGuts() {
if (mMediaViewHolder != null) {
mMediaViewHolder.marquee(true, mMediaViewController.GUTS_ANIMATION_DURATION);
- } else if (mRecommendationViewHolder != null) {
- mRecommendationViewHolder.marquee(true, mMediaViewController.GUTS_ANIMATION_DURATION);
}
mMediaViewController.openGuts();
if (mMediaViewHolder != null) {
bindPlayerContentDescription(mMediaData);
- } else if (mRecommendationViewHolder != null) {
- bindRecommendationContentDescription(mRecommendationData);
}
mLogger.logLongPressOpen(mUid, mPackageName, mInstanceId);
}
@@ -1822,29 +1412,6 @@ public class MediaControlPanel {
}
/**
- * Scale artwork to fill the background of media covers in recommendation card.
- */
- @UiThread
- private Drawable getScaledRecommendationCover(Icon artworkIcon, int width, int height) {
- if (width == 0 || height == 0) {
- return null;
- }
- if (artworkIcon != null) {
- Bitmap bitmap;
- if (artworkIcon.getType() == Icon.TYPE_BITMAP
- || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
- Bitmap artworkBitmap = artworkIcon.getBitmap();
- if (artworkBitmap != null) {
- bitmap = Bitmap.createScaledBitmap(artworkIcon.getBitmap(), width,
- height, false);
- return new BitmapDrawable(mContext.getResources(), bitmap);
- }
- }
- }
- return null;
- }
-
- /**
* Get the current media controller
*
* @return the controller
@@ -1896,64 +1463,5 @@ public class MediaControlPanel {
set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : notVisibleValue);
set.setAlpha(actionId, visible ? 1.0f : 0.0f);
}
-
- private void setSmartspaceRecItemOnClickListener(
- @NonNull View view,
- @NonNull SmartspaceAction action,
- int interactedSubcardRank) {
- if (view == null || action == null || action.getIntent() == null
- || action.getIntent().getExtras() == null) {
- Log.e(TAG, "No tap action can be set up");
- return;
- }
-
- view.setOnClickListener(v -> {
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
-
- if (interactedSubcardRank == -1) {
- mLogger.logRecommendationCardTap(mPackageName, mInstanceId);
- } else {
- mLogger.logRecommendationItemTap(mPackageName, mInstanceId, interactedSubcardRank);
- }
-
- if (shouldSmartspaceRecItemOpenInForeground(action)) {
- // Request to unlock the device if the activity needs to be opened in foreground.
- mActivityStarter.postStartActivityDismissingKeyguard(
- action.getIntent(),
- 0 /* delay */,
- buildLaunchAnimatorController(
- mRecommendationViewHolder.getRecommendations()));
- } else {
- // Otherwise, open the activity in background directly.
- view.getContext().startActivity(action.getIntent());
- }
-
- // Automatically scroll to the active player once the media is loaded.
- mMediaCarouselController.setShouldScrollToKey(true);
- });
- }
-
- /** Returns if the Smartspace action will open the activity in foreground. */
- private boolean shouldSmartspaceRecItemOpenInForeground(SmartspaceAction action) {
- if (action == null || action.getIntent() == null
- || action.getIntent().getExtras() == null) {
- return false;
- }
-
- String intentString = action.getIntent().getExtras().getString(EXTRAS_SMARTSPACE_INTENT);
- if (intentString == null) {
- return false;
- }
-
- try {
- Intent wrapperIntent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
- return wrapperIntent.getBooleanExtra(KEY_SMARTSPACE_OPEN_IN_FOREGROUND, false);
- } catch (URISyntaxException e) {
- Log.wtf(TAG, "Failed to create intent from URI: " + intentString);
- e.printStackTrace();
- }
-
- return false;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index b687dce20b06..dba190022c8b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -38,7 +38,6 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition
import com.android.systemui.media.controls.ui.animation.MetadataAnimationHandler
import com.android.systemui.media.controls.ui.binder.MediaControlViewBinder
-import com.android.systemui.media.controls.ui.binder.MediaRecommendationsViewBinder
import com.android.systemui.media.controls.ui.binder.SeekBarObserver
import com.android.systemui.media.controls.ui.controller.MediaCarouselController.Companion.calculateAlpha
import com.android.systemui.media.controls.ui.view.GutsViewHolder
@@ -48,7 +47,6 @@ import com.android.systemui.media.controls.ui.view.MediaViewHolder.Companion.hea
import com.android.systemui.media.controls.ui.view.MediaViewHolder.Companion.labelLargeTF
import com.android.systemui.media.controls.ui.view.MediaViewHolder.Companion.labelMediumTF
import com.android.systemui.media.controls.ui.view.MediaViewHolder.Companion.titleMediumTF
-import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
@@ -90,15 +88,6 @@ constructor(
private val globalSettings: GlobalSettings,
) {
- /**
- * Indicating that the media view controller is for a notification-based player, session-based
- * player, or recommendation
- */
- enum class TYPE {
- PLAYER,
- RECOMMENDATION,
- }
-
companion object {
@JvmField val GUTS_ANIMATION_DURATION = 234L
}
@@ -115,7 +104,6 @@ constructor(
private var animationDuration: Long = 0
private var animateNextStateChange: Boolean = false
private val measurement = MeasurementOutput(0, 0)
- private var type: TYPE = TYPE.PLAYER
/** A map containing all viewStates for all locations of this mediaState */
private val viewStates: MutableMap<CacheKey, TransitionViewState?> = mutableMapOf()
@@ -203,7 +191,6 @@ constructor(
private var isNextButtonAvailable = false
/** View holders for controller */
- var recommendationViewHolder: RecommendationViewHolder? = null
var mediaViewHolder: MediaViewHolder? = null
private lateinit var seekBarObserver: SeekBarObserver
@@ -417,13 +404,9 @@ constructor(
/** Set the height of UMO background constraints. */
private fun setBackgroundHeights(height: Int) {
- val backgroundIds =
- if (type == TYPE.PLAYER) {
- MediaViewHolder.backgroundIds
- } else {
- setOf(RecommendationViewHolder.backgroundId)
- }
- backgroundIds.forEach { id -> expandedLayout.getConstraint(id).layout.mHeight = height }
+ MediaViewHolder.backgroundIds.forEach { id ->
+ expandedLayout.getConstraint(id).layout.mHeight = height
+ }
}
/**
@@ -431,11 +414,7 @@ constructor(
* [TransitionViewState].
*/
private fun setGutsViewState(viewState: TransitionViewState) {
- val controlsIds =
- when (type) {
- TYPE.PLAYER -> MediaViewHolder.controlsIds
- TYPE.RECOMMENDATION -> RecommendationViewHolder.controlsIds
- }
+ val controlsIds = MediaViewHolder.controlsIds
val gutsIds = GutsViewHolder.ids
controlsIds.forEach { id ->
viewState.widgetStates.get(id)?.let { state ->
@@ -467,7 +446,6 @@ constructor(
squishedViewState.widgetStates.get(id)?.let { state -> state.height = squishedHeight }
}
- // media player
calculateWidgetGroupAlphaForSquishiness(
MediaViewHolder.expandedBottomActionIds,
squishedViewState.measureHeight.toFloat(),
@@ -480,20 +458,6 @@ constructor(
squishedViewState,
squishFraction,
)
- // recommendation card
- val titlesTop =
- calculateWidgetGroupAlphaForSquishiness(
- RecommendationViewHolder.mediaTitlesAndSubtitlesIds,
- squishedViewState.measureHeight.toFloat(),
- squishedViewState,
- squishFraction,
- )
- calculateWidgetGroupAlphaForSquishiness(
- RecommendationViewHolder.mediaContainersIds,
- titlesTop,
- squishedViewState,
- squishFraction,
- )
return squishedViewState
}
@@ -661,10 +625,10 @@ constructor(
* Attach a view to this controller. This may perform measurements if it's not available yet and
* should therefore be done carefully.
*/
- fun attach(transitionLayout: TransitionLayout, type: TYPE) =
+ fun attach(transitionLayout: TransitionLayout) =
traceSection("MediaViewController#attach") {
- loadLayoutForType(type)
- logger.logMediaLocation("attach $type", currentStartLocation, currentEndLocation)
+ loadLayoutConstraints()
+ logger.logMediaLocation("attach", currentStartLocation, currentEndLocation)
this.transitionLayout = transitionLayout
layoutController.attach(transitionLayout)
if (currentEndLocation == MediaHierarchyManager.LOCATION_UNKNOWN) {
@@ -691,7 +655,7 @@ constructor(
seekBarViewModel.setEnabledChangeListener(enabledChangeListener)
val mediaCard = mediaViewHolder.player
- attach(mediaViewHolder.player, TYPE.PLAYER)
+ attach(mediaViewHolder.player)
val turbulenceNoiseView = mediaViewHolder.turbulenceNoiseView
turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView)
@@ -813,15 +777,6 @@ constructor(
}
}
- fun attachRecommendations(recommendationViewHolder: RecommendationViewHolder) {
- if (!SceneContainerFlag.isEnabled) return
- this.recommendationViewHolder = recommendationViewHolder
-
- attach(recommendationViewHolder.recommendations, TYPE.RECOMMENDATION)
- recsConfigurationChangeListener =
- MediaRecommendationsViewBinder::updateRecommendationsVisibility
- }
-
fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) {
if (!SceneContainerFlag.isEnabled) return
seekBarViewModel.logSeek = onSeek
@@ -1026,20 +981,10 @@ constructor(
return result
}
- private fun loadLayoutForType(type: TYPE) {
- this.type = type
-
- // These XML resources contain ConstraintSets that will apply to this player type's layout
- when (type) {
- TYPE.PLAYER -> {
- collapsedLayout.load(context, R.xml.media_session_collapsed)
- expandedLayout.load(context, R.xml.media_session_expanded)
- }
- TYPE.RECOMMENDATION -> {
- collapsedLayout.load(context, R.xml.media_recommendations_collapsed)
- expandedLayout.load(context, R.xml.media_recommendations_expanded)
- }
- }
+ private fun loadLayoutConstraints() {
+ // These XML resources contain ConstraintSets that will apply to this player's layout
+ collapsedLayout.load(context, R.xml.media_session_collapsed)
+ expandedLayout.load(context, R.xml.media_session_expanded)
readjustUIUpdateConstraints()
refreshState()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
index f28edd638b10..2fc44ad3cce6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
@@ -42,8 +42,7 @@ class MediaViewModelCallback(
) {
oldItem.instanceId == newItem.instanceId
} else {
- oldItem is MediaCommonViewModel.MediaRecommendations &&
- newItem is MediaCommonViewModel.MediaRecommendations
+ false
}
}
@@ -56,11 +55,6 @@ class MediaViewModelCallback(
) {
oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi &&
oldItem.updateTime == newItem.updateTime
- } else if (
- oldItem is MediaCommonViewModel.MediaRecommendations &&
- newItem is MediaCommonViewModel.MediaRecommendations
- ) {
- oldItem.key == newItem.key && oldItem.loadingEnabled == newItem.loadingEnabled
} else {
false
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt
deleted file mode 100644
index 2d028d0213ff..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt
+++ /dev/null
@@ -1,122 +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.media.controls.ui.view
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import android.widget.SeekBar
-import android.widget.TextView
-import com.android.internal.widget.CachingIconView
-import com.android.systemui.media.controls.ui.drawable.IlluminationDrawable
-import com.android.systemui.res.R
-import com.android.systemui.util.animation.TransitionLayout
-
-private const val TAG = "RecommendationViewHolder"
-
-/** ViewHolder for a Smartspace media recommendation. */
-class RecommendationViewHolder private constructor(itemView: View) {
-
- val recommendations = itemView as TransitionLayout
-
- // Recommendation screen
- val cardTitle: TextView = itemView.requireViewById(R.id.media_rec_title)
-
- val mediaCoverContainers =
- listOf<ViewGroup>(
- itemView.requireViewById(R.id.media_cover1_container),
- itemView.requireViewById(R.id.media_cover2_container),
- itemView.requireViewById(R.id.media_cover3_container)
- )
- val mediaAppIcons: List<CachingIconView> =
- mediaCoverContainers.map { it.requireViewById(R.id.media_rec_app_icon) }
- val mediaTitles: List<TextView> =
- mediaCoverContainers.map { it.requireViewById(R.id.media_title) }
- val mediaSubtitles: List<TextView> =
- mediaCoverContainers.map { it.requireViewById(R.id.media_subtitle) }
- val mediaProgressBars: List<SeekBar> =
- mediaCoverContainers.map {
- it.requireViewById<SeekBar?>(R.id.media_progress_bar).apply {
- // Media playback is in the direction of tape, not time, so it stays LTR
- layoutDirection = View.LAYOUT_DIRECTION_LTR
- }
- }
-
- val mediaCoverItems: List<ImageView> =
- mediaCoverContainers.map { it.requireViewById(R.id.media_cover) }
- val gutsViewHolder = GutsViewHolder(itemView)
-
- init {
- (recommendations.background as IlluminationDrawable).let { background ->
- mediaCoverContainers.forEach { background.registerLightSource(it) }
- background.registerLightSource(gutsViewHolder.cancel)
- background.registerLightSource(gutsViewHolder.dismiss)
- background.registerLightSource(gutsViewHolder.settings)
- }
- }
-
- fun marquee(start: Boolean, delay: Long) {
- gutsViewHolder.marquee(start, delay, TAG)
- }
-
- companion object {
- /**
- * Creates a RecommendationViewHolder.
- *
- * @param inflater LayoutInflater to use to inflate the layout.
- * @param parent Parent of inflated view.
- */
- @JvmStatic
- fun create(inflater: LayoutInflater, parent: ViewGroup): RecommendationViewHolder {
- val itemView =
- inflater.inflate(R.layout.media_recommendations, parent, false /* attachToRoot */)
- // Because this media view (a TransitionLayout) is used to measure and layout the views
- // in various states before being attached to its parent, we can't depend on the default
- // LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction.
- itemView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
- return RecommendationViewHolder(itemView)
- }
-
- // Res Ids for the control components on the recommendation view.
- val controlsIds =
- setOf(
- R.id.media_rec_title,
- R.id.media_cover,
- R.id.media_cover1_container,
- R.id.media_cover2_container,
- R.id.media_cover3_container,
- R.id.media_title,
- R.id.media_subtitle,
- )
-
- val mediaTitlesAndSubtitlesIds =
- setOf(
- R.id.media_title,
- R.id.media_subtitle,
- )
-
- val mediaContainersIds =
- setOf(
- R.id.media_cover1_container,
- R.id.media_cover2_container,
- R.id.media_cover3_container
- )
-
- val backgroundId = R.id.sizing_view
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index e5f1766fbb28..dfaee4434bcf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -49,7 +49,6 @@ constructor(
private val visualStabilityProvider: VisualStabilityProvider,
private val interactor: MediaCarouselInteractor,
private val controlInteractorFactory: MediaControlInteractorFactory,
- private val recommendationsViewModel: MediaRecommendationsViewModel,
private val logger: MediaUiEventLogger,
private val mediaLogger: MediaLogger,
) {
@@ -69,7 +68,7 @@ constructor(
when (commonModel) {
is MediaCommonModel.MediaControl -> add(toViewModel(commonModel))
is MediaCommonModel.MediaRecommendations ->
- add(toViewModel(commonModel))
+ return@forEach // TODO(b/382680767): remove
}
}
}
@@ -95,8 +94,6 @@ constructor(
private val mediaControlByInstanceId =
mutableMapOf<InstanceId, MediaCommonViewModel.MediaControl>()
- private var mediaRecs: MediaCommonViewModel.MediaRecommendations? = null
-
private var modelsPendingRemoval: MutableSet<MediaCommonModel> = mutableSetOf()
private var allowReorder = false
@@ -149,37 +146,6 @@ constructor(
)
}
- private fun toViewModel(
- commonModel: MediaCommonModel.MediaRecommendations
- ): MediaCommonViewModel.MediaRecommendations {
- return mediaRecs?.copy(
- key = commonModel.recsLoadingModel.key,
- loadingEnabled = interactor.isRecommendationActive(),
- )
- ?: MediaCommonViewModel.MediaRecommendations(
- key = commonModel.recsLoadingModel.key,
- loadingEnabled = interactor.isRecommendationActive(),
- recsViewModel = recommendationsViewModel,
- onAdded = { commonViewModel ->
- mediaLogger.logMediaRecommendationCardAdded(
- commonModel.recsLoadingModel.key
- )
- onMediaRecommendationAddedOrUpdated(
- commonViewModel as MediaCommonViewModel.MediaRecommendations
- )
- },
- onRemoved = { immediatelyRemove ->
- onMediaRecommendationRemoved(commonModel, immediatelyRemove)
- },
- onUpdated = { commonViewModel ->
- onMediaRecommendationAddedOrUpdated(
- commonViewModel as MediaCommonViewModel.MediaRecommendations
- )
- },
- )
- .also { mediaRecs = it }
- }
-
private fun onMediaControlAddedOrUpdated(
commonViewModel: MediaCommonViewModel,
commonModel: MediaCommonModel.MediaControl,
@@ -197,32 +163,6 @@ constructor(
}
}
- private fun onMediaRecommendationAddedOrUpdated(
- commonViewModel: MediaCommonViewModel.MediaRecommendations
- ) {
- if (!interactor.isRecommendationActive()) {
- commonViewModel.onRemoved(true)
- }
- }
-
- private fun onMediaRecommendationRemoved(
- commonModel: MediaCommonModel.MediaRecommendations,
- immediatelyRemove: Boolean,
- ) {
- mediaLogger.logMediaRecommendationCardRemoved(commonModel.recsLoadingModel.key)
- if (immediatelyRemove || isReorderingAllowed()) {
- interactor.dismissSmartspaceRecommendation(commonModel.recsLoadingModel.key, 0L)
- mediaRecs = null
- if (!immediatelyRemove) {
- // Although it wasn't requested, we were able to process the removal
- // immediately since reordering is allowed. So, notify hosts to update
- updateHostVisibility()
- }
- } else {
- modelsPendingRemoval.add(commonModel)
- }
- }
-
private fun isReorderingAllowed(): Boolean {
return visualStabilityProvider.isReorderingAllowed
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
index 52cb173b39cb..d493d57051f7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
@@ -35,13 +35,4 @@ sealed class MediaCommonViewModel {
val isMediaFromRec: Boolean = false,
val updateTime: Long = 0,
) : MediaCommonViewModel()
-
- data class MediaRecommendations(
- val key: String,
- val loadingEnabled: Boolean,
- val recsViewModel: MediaRecommendationsViewModel,
- override val onAdded: (MediaCommonViewModel) -> Unit,
- override val onRemoved: (Boolean) -> Unit,
- override val onUpdated: (MediaCommonViewModel) -> Unit,
- ) : MediaCommonViewModel()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt
deleted file mode 100644
index 77add4035067..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt
+++ /dev/null
@@ -1,33 +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.media.controls.ui.viewmodel
-
-import android.graphics.drawable.Drawable
-import android.graphics.drawable.Icon
-import com.android.systemui.animation.Expandable
-
-/** Models UI state for media recommendation item */
-data class MediaRecViewModel(
- val contentDescription: CharSequence,
- val title: CharSequence = "",
- val subtitle: CharSequence = "",
- /** track progress [0 - 100] for the recommendation album. */
- val progress: Int = 0,
- val albumIcon: Icon? = null,
- val appIcon: Drawable,
- val onClicked: ((Expandable, Int) -> Unit),
-)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
deleted file mode 100644
index 90313ddc736e..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
+++ /dev/null
@@ -1,238 +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.media.controls.ui.viewmodel
-
-import android.content.Context
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.graphics.drawable.Drawable
-import android.os.Process
-import android.util.Log
-import com.android.internal.logging.InstanceId
-import com.android.systemui.animation.Expandable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor
-import com.android.systemui.media.controls.shared.model.MediaRecModel
-import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
-import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.controller.MediaLocation
-import com.android.systemui.media.controls.ui.controller.MediaViewController.Companion.GUTS_ANIMATION_DURATION
-import com.android.systemui.media.controls.util.MediaDataUtils
-import com.android.systemui.media.controls.util.MediaUiEventLogger
-import com.android.systemui.res.R
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-
-/** Models UI state and handles user input for media recommendations */
-@SysUISingleton
-class MediaRecommendationsViewModel
-@Inject
-constructor(
- @Application private val applicationContext: Context,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
- private val interactor: MediaRecommendationsInteractor,
- private val logger: MediaUiEventLogger,
-) {
-
- val mediaRecsCard: Flow<MediaRecsCardViewModel?> =
- interactor.recommendations
- .map { recsCard -> toRecsViewModel(recsCard) }
- .distinctUntilChanged()
- .flowOn(backgroundDispatcher)
-
- @MediaLocation private var location = MediaHierarchyManager.LOCATION_UNKNOWN
-
- /**
- * Called whenever the recommendation has been expired or removed by the user. This method
- * removes the recommendation card entirely from the carousel.
- */
- private fun onMediaRecommendationsDismissed(
- key: String,
- uid: Int,
- packageName: String,
- dismissIntent: Intent?,
- instanceId: InstanceId?,
- ) {
- logger.logLongPressDismiss(uid, packageName, instanceId)
- interactor.removeMediaRecommendations(key, dismissIntent, GUTS_DISMISS_DELAY_MS_DURATION)
- }
-
- private fun onClicked(
- expandable: Expandable,
- intent: Intent?,
- packageName: String,
- instanceId: InstanceId?,
- index: Int,
- ) {
- if (intent == null || intent.extras == null) {
- Log.e(TAG, "No tap action can be set up")
- return
- }
-
- if (index == -1) {
- logger.logRecommendationCardTap(packageName, instanceId)
- } else {
- logger.logRecommendationItemTap(packageName, instanceId, index)
- }
-
- // set the package name of the player added by recommendation once the media is loaded.
- interactor.switchToMediaControl(packageName)
-
- interactor.startClickIntent(expandable, intent)
- }
-
- private suspend fun toRecsViewModel(model: MediaRecommendationsModel): MediaRecsCardViewModel? {
- if (!model.areRecommendationsValid) {
- Log.e(TAG, "Received an invalid recommendation list")
- return null
- }
- if (model.appName == null || model.uid == Process.INVALID_UID) {
- Log.w(TAG, "Fail to get media recommendation's app info")
- return null
- }
-
- val appIcon = getIconFromApp(model.packageName) ?: return null
-
- var areTitlesVisible = false
- var areSubtitlesVisible = false
- val mediaRecs =
- model.mediaRecs.map { mediaRecModel ->
- areTitlesVisible = areTitlesVisible || !mediaRecModel.title.isNullOrEmpty()
- areSubtitlesVisible = areSubtitlesVisible || !mediaRecModel.subtitle.isNullOrEmpty()
- val progress = MediaDataUtils.getDescriptionProgress(mediaRecModel.extras) ?: 0.0
- MediaRecViewModel(
- contentDescription =
- setUpMediaRecContentDescription(mediaRecModel, model.appName),
- title = mediaRecModel.title ?: "",
- subtitle = mediaRecModel.subtitle ?: "",
- progress = (progress * 100).toInt(),
- albumIcon = mediaRecModel.icon,
- appIcon = appIcon,
- onClicked = { expandable, index ->
- onClicked(
- expandable,
- mediaRecModel.intent,
- model.packageName,
- model.instanceId,
- index,
- )
- },
- )
- }
- // Subtitles should only be visible if titles are visible.
- areSubtitlesVisible = areTitlesVisible && areSubtitlesVisible
-
- return MediaRecsCardViewModel(
- contentDescription = { gutsVisible ->
- if (gutsVisible) {
- applicationContext.getString(
- R.string.controls_media_close_session,
- model.appName,
- )
- } else {
- applicationContext.getString(R.string.controls_media_smartspace_rec_header)
- }
- },
- onClicked = { expandable ->
- onClicked(
- expandable,
- model.dismissIntent,
- model.packageName,
- model.instanceId,
- index = -1,
- )
- },
- onLongClicked = {
- logger.logLongPressOpen(model.uid, model.packageName, model.instanceId)
- },
- mediaRecs = mediaRecs,
- areTitlesVisible = areTitlesVisible,
- areSubtitlesVisible = areSubtitlesVisible,
- gutsMenu = toGutsViewModel(model),
- onLocationChanged = { location = it },
- )
- }
-
- private fun toGutsViewModel(model: MediaRecommendationsModel): GutsViewModel {
- return GutsViewModel(
- gutsText =
- applicationContext.getString(R.string.controls_media_close_session, model.appName),
- onDismissClicked = {
- onMediaRecommendationsDismissed(
- model.key,
- model.uid,
- model.packageName,
- model.dismissIntent,
- model.instanceId,
- )
- },
- cancelTextBackground =
- applicationContext.getDrawable(R.drawable.qs_media_outline_button),
- onSettingsClicked = {
- logger.logLongPressSettings(model.uid, model.packageName, model.instanceId)
- interactor.startSettings()
- },
- )
- }
-
- private fun setUpMediaRecContentDescription(
- mediaRec: MediaRecModel,
- appName: CharSequence?,
- ): CharSequence {
- // Set up the accessibility label for the media item.
- val artistName = mediaRec.extras?.getString(KEY_SMARTSPACE_ARTIST_NAME, "")
- return if (artistName.isNullOrEmpty()) {
- applicationContext.getString(
- R.string.controls_media_smartspace_rec_item_no_artist_description,
- mediaRec.title,
- appName,
- )
- } else {
- applicationContext.getString(
- R.string.controls_media_smartspace_rec_item_description,
- mediaRec.title,
- artistName,
- appName,
- )
- }
- }
-
- private fun getIconFromApp(packageName: String): Drawable? {
- return try {
- applicationContext.packageManager.getApplicationIcon(packageName)
- } catch (e: PackageManager.NameNotFoundException) {
- Log.w(TAG, "Cannot find icon for package $packageName", e)
- null
- }
- }
-
- companion object {
- private const val TAG = "MediaRecommendationsViewModel"
- private const val KEY_SMARTSPACE_ARTIST_NAME = "artist_name"
- /**
- * Delay duration is based on [GUTS_ANIMATION_DURATION], it should have 100 ms increase in
- * order to let the animation end.
- */
- private const val GUTS_DISMISS_DELAY_MS_DURATION = 334L
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
deleted file mode 100644
index f1f7dc2195d5..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
+++ /dev/null
@@ -1,31 +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.media.controls.ui.viewmodel
-
-import com.android.systemui.animation.Expandable
-
-/** Models UI state for media recommendations card. */
-data class MediaRecsCardViewModel(
- val contentDescription: (Boolean) -> CharSequence,
- val onClicked: (Expandable) -> Unit,
- val onLongClicked: () -> Unit,
- val mediaRecs: List<MediaRecViewModel>,
- val areTitlesVisible: Boolean,
- val areSubtitlesVisible: Boolean,
- val gutsMenu: GutsViewModel,
- val onLocationChanged: (Int) -> Unit,
-)
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUIStateChange.kt b/packages/SystemUI/src/com/android/systemui/model/SysUIStateChange.kt
index f29b15730986..aaed606f8fb2 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUIStateChange.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUIStateChange.kt
@@ -16,6 +16,8 @@
package com.android.systemui.model
+import com.android.systemui.shared.system.QuickStepContract.getSystemUiStateString
+
/**
* Represents a set of state changes. A bit can either be set to `true` or `false`.
*
@@ -43,13 +45,16 @@ class StateChange {
fun hasChanges() = flagsToSet != 0L || flagsToClear != 0L
- /** Applies all changed flags to [sysUiState]. */
+ /**
+ * Applies all changed flags to [sysUiState].
+ *
+ * Note this doesn't call [SysUiState.commitUpdate].
+ */
fun applyTo(sysUiState: SysUiState) {
iterateBits(flagsToSet or flagsToClear) { bit ->
val isBitSetInNewState = flagsToSet and bit != 0L
sysUiState.setFlag(bit, isBitSetInNewState)
}
- sysUiState.commitUpdate()
}
fun applyTo(sysUiState: Long): Long {
@@ -69,14 +74,25 @@ class StateChange {
}
}
- /** Clears all the flags changed in a [sysUiState] */
- fun clearAllChangedFlagsIn(sysUiState: SysUiState) {
+ /**
+ * Clears all the flags changed in a [sysUiState].
+ *
+ * Note this doesn't call [SysUiState.commitUpdate].
+ */
+ fun clearFrom(sysUiState: SysUiState) {
iterateBits(flagsToSet or flagsToClear) { bit -> sysUiState.setFlag(bit, false) }
- sysUiState.commitUpdate()
}
fun clear() {
flagsToSet = 0
flagsToClear = 0
}
+
+ override fun toString(): String {
+ return """StateChange(flagsToSet=${getSystemUiStateString(flagsToSet)}, flagsToClear=${
+ getSystemUiStateString(
+ flagsToClear
+ )
+ })"""
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
index 53105b2c0f6a..663355941613 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
@@ -74,6 +74,9 @@ interface SysUiState : Dumpable {
*/
fun destroy()
+ /** The display ID this instances is associated with */
+ val displayId: Int
+
companion object {
const val DEBUG: Boolean = false
}
@@ -84,7 +87,7 @@ private const val TAG = "SysUIState"
class SysUiStateImpl
@AssistedInject
constructor(
- @Assisted private val displayId: Int,
+ @Assisted override val displayId: Int,
private val sceneContainerPlugin: SceneContainerPlugin?,
private val dumpManager: DumpManager,
private val stateDispatcher: SysUIStateDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 85658bb71c8b..50012abc69d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -26,12 +26,14 @@ import androidx.compose.animation.graphics.res.animatedVectorResource
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.Image
+import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@@ -39,6 +41,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
@@ -49,8 +52,16 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.BlendMode
+import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.ColorProducer
+import androidx.compose.ui.graphics.CompositingStrategy
+import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
@@ -60,7 +71,7 @@ import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.semantics.toggleableState
-import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.modifiers.size
@@ -73,6 +84,9 @@ import com.android.systemui.common.ui.compose.load
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.SideIconHeight
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.SideIconWidth
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TILE_INITIAL_DELAY_MILLIS
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TILE_MARQUEE_ITERATIONS
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileLabelBlurWidth
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState
import com.android.systemui.qs.ui.compose.borderOnFocus
@@ -104,30 +118,31 @@ fun LargeTileContent(
val focusBorderColor = MaterialTheme.colorScheme.secondary
Box(
modifier =
- Modifier.size(CommonTileDefaults.ToggleTargetSize).thenIf(toggleClick != null) {
- Modifier.borderOnFocus(color = focusBorderColor, iconShape.topEnd)
- .clip(iconShape)
- .verticalSquish(squishiness)
- .drawBehind { drawRect(animatedBackgroundColor) }
- .combinedClickable(
- onClick = toggleClick!!,
- onLongClick = onLongClick,
- onLongClickLabel = longPressLabel,
- hapticFeedbackEnabled = !Flags.msdlFeedback(),
- )
- .thenIf(accessibilityUiState != null) {
- Modifier.semantics {
- accessibilityUiState as AccessibilityUiState
- contentDescription = accessibilityUiState.contentDescription
- stateDescription = accessibilityUiState.stateDescription
- accessibilityUiState.toggleableState?.let {
- toggleableState = it
+ Modifier.size(CommonTileDefaults.ToggleTargetSize)
+ .clip(iconShape)
+ .verticalSquish(squishiness)
+ .drawBehind { drawRect(animatedBackgroundColor) }
+ .thenIf(toggleClick != null) {
+ Modifier.borderOnFocus(color = focusBorderColor, iconShape.topEnd)
+ .combinedClickable(
+ onClick = toggleClick!!,
+ onLongClick = onLongClick,
+ onLongClickLabel = longPressLabel,
+ hapticFeedbackEnabled = !Flags.msdlFeedback(),
+ )
+ .thenIf(accessibilityUiState != null) {
+ Modifier.semantics {
+ accessibilityUiState as AccessibilityUiState
+ contentDescription = accessibilityUiState.contentDescription
+ stateDescription = accessibilityUiState.stateDescription
+ accessibilityUiState.toggleableState?.let {
+ toggleableState = it
+ }
+ role = Role.Switch
}
- role = Role.Switch
- }
- .sysuiResTag(TEST_TAG_TOGGLE)
- }
- }
+ .sysuiResTag(TEST_TAG_TOGGLE)
+ }
+ }
) {
SmallTileContent(
iconProvider = iconProvider,
@@ -167,18 +182,15 @@ fun LargeTileLabels(
val animatedSecondaryLabelColor by
animateColorAsState(colors.secondaryLabel, label = "QSTileSecondaryLabelColor")
Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) {
- BasicText(
- label,
+ TileLabel(
+ text = label,
style = MaterialTheme.typography.labelLarge,
color = { animatedLabelColor },
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
)
if (!TextUtils.isEmpty(secondaryLabel)) {
- BasicText(
+ TileLabel(
secondaryLabel ?: "",
color = { animatedSecondaryLabelColor },
- maxLines = 1,
style = MaterialTheme.typography.bodyMedium,
modifier =
Modifier.thenIf(
@@ -194,9 +206,9 @@ fun LargeTileLabels(
@Composable
fun SmallTileContent(
- modifier: Modifier = Modifier,
iconProvider: Context.() -> Icon,
color: Color,
+ modifier: Modifier = Modifier,
size: () -> Dp = { CommonTileDefaults.IconSize },
animateToEnd: Boolean = false,
) {
@@ -212,31 +224,39 @@ fun SmallTileContent(
}
}
if (loadedDrawable is Animatable) {
+ // Skip initial animation, icons should animate only as the state change
+ // and not when first composed
+ var shouldSkipInitialAnimation by remember { mutableStateOf(true) }
+ LaunchedEffect(Unit) { shouldSkipInitialAnimation = animateToEnd }
+
val painter =
when (icon) {
is Icon.Resource -> {
val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
key(icon) {
- if (animateToEnd) {
- rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true)
- } else {
- var atEnd by remember(icon.res) { mutableStateOf(false) }
- LaunchedEffect(key1 = icon.res) { atEnd = true }
- rememberAnimatedVectorPainter(
- animatedImageVector = image,
- atEnd = atEnd,
- )
- }
+ var atEnd by remember(icon) { mutableStateOf(shouldSkipInitialAnimation) }
+ LaunchedEffect(key1 = icon.res) { atEnd = true }
+
+ rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd)
}
}
is Icon.Loaded -> {
- LaunchedEffect(loadedDrawable) {
+ val painter = rememberDrawablePainter(loadedDrawable)
+
+ // rememberDrawablePainter automatically starts the animation. Using
+ // SideEffect here to immediately stop it if needed
+ DisposableEffect(painter) {
if (loadedDrawable is AnimatedVectorDrawable) {
loadedDrawable.forceAnimationOnUI()
}
+ if (shouldSkipInitialAnimation) {
+ loadedDrawable.stop()
+ }
+ onDispose {}
}
- rememberDrawablePainter(loadedDrawable)
+
+ painter
}
}
@@ -251,6 +271,45 @@ fun SmallTileContent(
}
}
+@Composable
+private fun TileLabel(
+ text: String,
+ color: ColorProducer,
+ style: TextStyle,
+ modifier: Modifier = Modifier,
+) {
+ BasicText(
+ text = text,
+ color = color,
+ style = style,
+ maxLines = 1,
+ modifier =
+ modifier
+ .fillMaxWidth()
+ .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
+ .drawWithContent {
+ drawContent()
+ // Draw a blur over the end of the text
+ val edgeWidthPx = TileLabelBlurWidth.toPx()
+ drawRect(
+ topLeft = Offset(size.width - edgeWidthPx, 0f),
+ size = Size(edgeWidthPx, size.height),
+ brush =
+ Brush.horizontalGradient(
+ colors = listOf(Color.Transparent, Color.Black),
+ startX = size.width,
+ endX = size.width - edgeWidthPx,
+ ),
+ blendMode = BlendMode.DstIn,
+ )
+ }
+ .basicMarquee(
+ iterations = TILE_MARQUEE_ITERATIONS,
+ initialDelayMillis = TILE_INITIAL_DELAY_MILLIS,
+ ),
+ )
+}
+
object CommonTileDefaults {
val IconSize = 32.dp
val LargeTileIconSize = 28.dp
@@ -258,9 +317,13 @@ object CommonTileDefaults {
val SideIconHeight = 20.dp
val ToggleTargetSize = 56.dp
val TileHeight = 72.dp
- val TilePadding = 8.dp
+ val TileStartPadding = 8.dp
+ val TileEndPadding = 16.dp
val TileArrangementPadding = 6.dp
val InactiveCornerRadius = 50.dp
+ val TileLabelBlurWidth = 32.dp
+ const val TILE_MARQUEE_ITERATIONS = 1
+ const val TILE_INITIAL_DELAY_MILLIS = 2000
@Composable fun longPressLabel() = stringResource(id = R.string.accessibility_long_click_tile)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 6e6c0b61d85b..69b967a68c3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -713,7 +713,7 @@ private fun AvailableTileGridCell(
// Displays the tile as an icon tile with the label underneath
Column(
horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = spacedBy(CommonTileDefaults.TilePadding, Alignment.Top),
+ verticalArrangement = spacedBy(CommonTileDefaults.TileStartPadding, Alignment.Top),
modifier =
modifier
.graphicsLayer { this.alpha = alpha }
@@ -813,7 +813,7 @@ fun EditTile(
placeable.place(startPadding.roundToInt(), 0)
}
}
- .tilePadding(),
+ .largeTilePadding(),
) {
// Icon
Box(Modifier.size(ToggleTargetSize)) {
@@ -847,7 +847,7 @@ private fun toAvailableTiles(
private fun MeasureScope.iconHorizontalCenter(containerSize: Int): Float {
return (containerSize - ToggleTargetSize.roundToPx()) / 2f -
- CommonTileDefaults.TilePadding.toPx()
+ CommonTileDefaults.TileStartPadding.toPx()
}
private fun Modifier.tileBackground(color: Color): Modifier {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index d73dc870756b..a56fabcc7dc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -84,7 +84,9 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.qs.panels.ui.compose.BounceableInfo
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileEndPadding
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileStartPadding
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
@@ -270,7 +272,7 @@ fun TileContainer(
iconOnly = iconOnly,
)
.sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE)
- .tilePadding(),
+ .thenIf(!iconOnly) { Modifier.largeTilePadding() }, // Icon tiles are center aligned
content = content,
)
}
@@ -284,7 +286,7 @@ fun LargeStaticTile(uiState: TileUiState, modifier: Modifier = Modifier) {
.clip(TileDefaults.animateTileShapeAsState(state = uiState.state).value)
.background(colors.background)
.height(TileHeight)
- .tilePadding()
+ .largeTilePadding()
) {
LargeTileContent(
label = uiState.label,
@@ -311,8 +313,8 @@ fun tileHorizontalArrangement(): Arrangement.Horizontal {
return spacedBy(space = CommonTileDefaults.TileArrangementPadding, alignment = Alignment.Start)
}
-fun Modifier.tilePadding(): Modifier {
- return padding(CommonTileDefaults.TilePadding)
+fun Modifier.largeTilePadding(): Modifier {
+ return padding(start = TileStartPadding, end = TileEndPadding)
}
@Composable
@@ -356,10 +358,10 @@ private object TileDefaults {
val ActiveIconCornerRadius = 16.dp
val ActiveTileCornerRadius = 24.dp
- /** An active tile without dual target uses the active color as background */
+ /** An active icon tile uses the active color as background */
@Composable
@ReadOnlyComposable
- fun activeTileColors(): TileColors =
+ fun activeIconTileColors(): TileColors =
TileColors(
background = MaterialTheme.colorScheme.primary,
iconBackground = MaterialTheme.colorScheme.primary,
@@ -418,10 +420,10 @@ private object TileDefaults {
fun getColorForState(uiState: TileUiState, iconOnly: Boolean): TileColors {
return when (uiState.state) {
STATE_ACTIVE -> {
- if (uiState.handlesSecondaryClick && !iconOnly) {
+ if (!iconOnly) {
activeDualTargetTileColors()
} else {
- activeTileColors()
+ activeIconTileColors()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index bfd512fa6a2d..ef0660fbcd1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -566,12 +566,11 @@ public final class KeyboardShortcutListSearch {
Arrays.asList(
Pair.create(KeyEvent.KEYCODE_TAB, KeyEvent.META_META_ON))),
/* Back: go back to previous state (back button) */
- /* Meta + Escape, Meta + backspace, Meta + left arrow */
+ /* Meta + Escape, Meta + left arrow */
new ShortcutKeyGroupMultiMappingInfo(
context.getString(R.string.group_system_go_back),
Arrays.asList(
Pair.create(KeyEvent.KEYCODE_ESCAPE, KeyEvent.META_META_ON),
- Pair.create(KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON),
Pair.create(KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.META_META_ON))),
/* Take a full screenshot: Meta + S */
new ShortcutKeyGroupMultiMappingInfo(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 2001a3ea7ca0..dad08e014c0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -422,25 +422,6 @@ class MediaCarouselControllerTest(flags: FlagsParameterization) : SysuiTestCase(
assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
}
- @DisableSceneContainer
- @Test
- fun testOrderWithSmartspace_prioritized_updatingVisibleMediaPlayers() {
- verify(mediaDataManager).addListener(capture(listener))
-
- testPlayerOrdering()
-
- // If smartspace is prioritized
- listener.value.onSmartspaceMediaDataLoaded(
- SMARTSPACE_KEY,
- EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
- true,
- )
-
- // Then it should be shown immediately after any actively playing controls
- assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
- assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(2).isSsMediaRec)
- }
-
@Test
fun testOrderWithSmartspace_notPrioritized() {
testPlayerOrdering()
@@ -571,146 +552,6 @@ class MediaCarouselControllerTest(flags: FlagsParameterization) : SysuiTestCase(
verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
}
- @DisableSceneContainer
- @Test
- fun testMediaLoaded_ScrollToActivePlayer() {
- verify(mediaDataManager).addListener(capture(listener))
-
- listener.value.onMediaDataLoaded(
- PLAYING_LOCAL,
- null,
- DATA.copy(
- active = true,
- isPlaying = true,
- playbackLocation = MediaData.PLAYBACK_LOCAL,
- resumption = false,
- ),
- )
- listener.value.onMediaDataLoaded(
- PAUSED_LOCAL,
- null,
- DATA.copy(
- active = true,
- isPlaying = false,
- playbackLocation = MediaData.PLAYBACK_LOCAL,
- resumption = false,
- ),
- )
- runAllReady()
- // adding a media recommendation card.
- listener.value.onSmartspaceMediaDataLoaded(
- SMARTSPACE_KEY,
- EMPTY_SMARTSPACE_MEDIA_DATA,
- false,
- )
- mediaCarouselController.shouldScrollToKey = true
- // switching between media players.
- listener.value.onMediaDataLoaded(
- PLAYING_LOCAL,
- PLAYING_LOCAL,
- DATA.copy(
- active = true,
- isPlaying = false,
- playbackLocation = MediaData.PLAYBACK_LOCAL,
- resumption = true,
- ),
- )
- listener.value.onMediaDataLoaded(
- PAUSED_LOCAL,
- PAUSED_LOCAL,
- DATA.copy(
- active = true,
- isPlaying = true,
- playbackLocation = MediaData.PLAYBACK_LOCAL,
- resumption = false,
- ),
- )
- runAllReady()
-
- assertEquals(
- MediaPlayerData.getMediaPlayerIndex(PAUSED_LOCAL),
- mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex,
- )
- }
-
- @DisableSceneContainer
- @Test
- fun testMediaLoadedFromRecommendationCard_ScrollToActivePlayer() {
- verify(mediaDataManager).addListener(capture(listener))
-
- listener.value.onSmartspaceMediaDataLoaded(
- SMARTSPACE_KEY,
- EMPTY_SMARTSPACE_MEDIA_DATA.copy(packageName = "PACKAGE_NAME", isActive = true),
- false,
- )
- listener.value.onMediaDataLoaded(
- PLAYING_LOCAL,
- null,
- DATA.copy(
- active = true,
- isPlaying = true,
- playbackLocation = MediaData.PLAYBACK_LOCAL,
- resumption = false,
- ),
- )
- runAllReady()
-
- var playerIndex = MediaPlayerData.getMediaPlayerIndex(PLAYING_LOCAL)
- assertEquals(
- playerIndex,
- mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex,
- )
- assertEquals(playerIndex, 0)
-
- // Replaying the same media player one more time.
- // And check that the card stays in its position.
- mediaCarouselController.shouldScrollToKey = true
- listener.value.onMediaDataLoaded(
- PLAYING_LOCAL,
- null,
- DATA.copy(
- active = true,
- isPlaying = true,
- playbackLocation = MediaData.PLAYBACK_LOCAL,
- resumption = false,
- packageName = "PACKAGE_NAME",
- ),
- )
- runAllReady()
- playerIndex = MediaPlayerData.getMediaPlayerIndex(PLAYING_LOCAL)
- assertEquals(playerIndex, 0)
- }
-
- @DisableSceneContainer
- @Test
- fun testRecommendationRemovedWhileNotVisible_updateHostVisibility() {
- verify(mediaDataManager).addListener(capture(listener))
-
- var result = false
- mediaCarouselController.updateHostVisibility = { result = true }
-
- whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(true)
- listener.value.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY, false)
-
- assertEquals(true, result)
- }
-
- @DisableSceneContainer
- @Test
- fun testRecommendationRemovedWhileVisible_thenReorders_updateHostVisibility() {
- verify(mediaDataManager).addListener(capture(listener))
-
- var result = false
- mediaCarouselController.updateHostVisibility = { result = true }
-
- whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(false)
- listener.value.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY, false)
- assertEquals(false, result)
-
- visualStabilityCallback.value.onReorderingAllowed()
- assertEquals(true, result)
- }
-
@Test
fun testGetCurrentVisibleMediaContentIntent() {
val clickIntent1 = mock(PendingIntent::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 9543032ef5ec..88fcc706f072 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -19,16 +19,12 @@ package com.android.systemui.media.controls.ui.controller
import android.animation.Animator
import android.animation.AnimatorSet
import android.app.PendingIntent
-import android.app.smartspace.SmartspaceAction
-import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
-import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
-import android.graphics.Matrix
import android.graphics.drawable.Animatable2
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
@@ -47,7 +43,6 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.Settings
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
import android.testing.TestableLooper
-import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.view.animation.Interpolator
@@ -59,7 +54,6 @@ import android.widget.TextView
import androidx.constraintlayout.widget.Barrier
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.LiveData
-import androidx.media.utils.MediaConstants
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
@@ -72,19 +66,15 @@ import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
-import com.android.systemui.media.controls.shared.model.KEY_SMARTSPACE_APP_NAME
import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.shared.model.MediaNotificationAction
-import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.ui.binder.SeekBarObserver
import com.android.systemui.media.controls.ui.view.GutsViewHolder
import com.android.systemui.media.controls.ui.view.MediaViewHolder
-import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.dialog.MediaOutputDialogManager
@@ -216,32 +206,9 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor
- @Mock private lateinit var recommendationViewHolder: RecommendationViewHolder
- @Mock private lateinit var smartspaceAction: SmartspaceAction
- private lateinit var smartspaceData: SmartspaceMediaData
- @Mock private lateinit var coverContainer1: ViewGroup
- @Mock private lateinit var coverContainer2: ViewGroup
- @Mock private lateinit var coverContainer3: ViewGroup
- @Mock private lateinit var recAppIconItem: CachingIconView
- @Mock private lateinit var recCardTitle: TextView
- @Mock private lateinit var coverItem: ImageView
- @Mock private lateinit var matrix: Matrix
- private lateinit var recTitle1: TextView
- private lateinit var recTitle2: TextView
- private lateinit var recTitle3: TextView
- private lateinit var recSubtitle1: TextView
- private lateinit var recSubtitle2: TextView
- private lateinit var recSubtitle3: TextView
- @Mock private lateinit var recProgressBar1: SeekBar
- @Mock private lateinit var recProgressBar2: SeekBar
- @Mock private lateinit var recProgressBar3: SeekBar
@Mock private lateinit var globalSettings: GlobalSettings
- private val intent =
- Intent().apply {
- putExtras(Bundle().also { it.putString(KEY_SMARTSPACE_APP_NAME, REC_APP_NAME) })
- setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- }
+ private val intent = Intent().apply { setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }
private val pendingIntent =
PendingIntent.getActivity(
mContext,
@@ -282,7 +249,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
mediaOutputDialogManager,
mediaCarouselController,
falsingManager,
- clock,
logger,
keyguardStateController,
activityIntentHelper,
@@ -304,27 +270,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
initMediaViewHolderMocks()
initDeviceMediaData(false, DEVICE_NAME)
-
- // Set up recommendation view
- initRecommendationViewHolderMocks()
-
- // Set valid recommendation data
- val extras = Bundle()
- extras.putString(KEY_SMARTSPACE_APP_NAME, REC_APP_NAME)
- val intent =
- Intent().apply {
- putExtras(extras)
- setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- }
- whenever(smartspaceAction.intent).thenReturn(intent)
- whenever(smartspaceAction.extras).thenReturn(extras)
- smartspaceData =
- EMPTY_SMARTSPACE_MEDIA_DATA.copy(
- packageName = PACKAGE,
- instanceId = instanceId,
- recommendations = listOf(smartspaceAction, smartspaceAction, smartspaceAction),
- cardAction = smartspaceAction,
- )
}
private fun initGutsViewHolderMocks() {
@@ -471,49 +416,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.loadingEffectView).thenReturn(loadingEffectView)
}
- /** Initialize elements for the recommendation view holder */
- private fun initRecommendationViewHolderMocks() {
- recTitle1 = TextView(context)
- recTitle2 = TextView(context)
- recTitle3 = TextView(context)
- recSubtitle1 = TextView(context)
- recSubtitle2 = TextView(context)
- recSubtitle3 = TextView(context)
-
- whenever(recommendationViewHolder.recommendations).thenReturn(view)
- whenever(recommendationViewHolder.mediaAppIcons)
- .thenReturn(listOf(recAppIconItem, recAppIconItem, recAppIconItem))
- whenever(recommendationViewHolder.cardTitle).thenReturn(recCardTitle)
- whenever(recommendationViewHolder.mediaCoverItems)
- .thenReturn(listOf(coverItem, coverItem, coverItem))
- whenever(recommendationViewHolder.mediaCoverContainers)
- .thenReturn(listOf(coverContainer1, coverContainer2, coverContainer3))
- whenever(recommendationViewHolder.mediaTitles)
- .thenReturn(listOf(recTitle1, recTitle2, recTitle3))
- whenever(recommendationViewHolder.mediaSubtitles)
- .thenReturn(listOf(recSubtitle1, recSubtitle2, recSubtitle3))
- whenever(recommendationViewHolder.mediaProgressBars)
- .thenReturn(listOf(recProgressBar1, recProgressBar2, recProgressBar3))
- whenever(coverItem.imageMatrix).thenReturn(matrix)
-
- // set ids for recommendation containers
- whenever(coverContainer1.id).thenReturn(1)
- whenever(coverContainer2.id).thenReturn(2)
- whenever(coverContainer3.id).thenReturn(3)
-
- whenever(recommendationViewHolder.gutsViewHolder).thenReturn(gutsViewHolder)
-
- val actionIcon = Icon.createWithResource(context, R.drawable.ic_android)
- whenever(smartspaceAction.icon).thenReturn(actionIcon)
-
- // Needed for card and item action click
- val mockContext = mock(Context::class.java)
- whenever(view.context).thenReturn(mockContext)
- whenever(coverContainer1.context).thenReturn(mockContext)
- whenever(coverContainer2.context).thenReturn(mockContext)
- whenever(coverContainer3.context).thenReturn(mockContext)
- }
-
@After
fun tearDown() {
session.release()
@@ -1488,169 +1390,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
/* ***** END guts tests for the player ***** */
- /* ***** Guts tests for the recommendations ***** */
-
- @Test
- fun recommendations_longClick_isFalse() {
- whenever(falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)).thenReturn(true)
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
-
- val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
- verify(viewHolder.player).onLongClickListener = captor.capture()
-
- captor.value.onLongClick(viewHolder.player)
- verify(mediaViewController, never()).openGuts()
- verify(mediaViewController, never()).closeGuts()
- }
-
- @Test
- fun recommendations_longClickWhenGutsClosed_gutsOpens() {
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
- whenever(mediaViewController.isGutsVisible).thenReturn(false)
-
- val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
- verify(viewHolder.player).onLongClickListener = captor.capture()
-
- captor.value.onLongClick(viewHolder.player)
- verify(mediaViewController).openGuts()
- verify(logger).logLongPressOpen(anyInt(), eq(PACKAGE), eq(instanceId))
- }
-
- @Test
- fun recommendations_longClickWhenGutsOpen_gutsCloses() {
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
- whenever(mediaViewController.isGutsVisible).thenReturn(true)
-
- val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
- verify(viewHolder.player).onLongClickListener = captor.capture()
-
- captor.value.onLongClick(viewHolder.player)
- verify(mediaViewController, never()).openGuts()
- verify(mediaViewController).closeGuts(false)
- }
-
- @Test
- fun recommendations_cancelButtonClick_animation() {
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
-
- cancel.callOnClick()
-
- verify(mediaViewController).closeGuts(false)
- }
-
- @Test
- fun recommendations_settingsButtonClick() {
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
-
- settings.callOnClick()
- verify(logger).logLongPressSettings(anyInt(), eq(PACKAGE), eq(instanceId))
-
- val captor = ArgumentCaptor.forClass(Intent::class.java)
- verify(activityStarter).startActivity(captor.capture(), eq(true))
-
- assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS)
- }
-
- @Test
- fun recommendations_dismissButtonClick() {
- val mediaKey = "key for dismissal"
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData.copy(targetId = mediaKey))
-
- assertThat(dismiss.isEnabled).isEqualTo(true)
- dismiss.callOnClick()
- verify(logger).logLongPressDismiss(anyInt(), eq(PACKAGE), eq(instanceId))
- verify(mediaDataManager).dismissSmartspaceRecommendation(eq(mediaKey), anyLong())
- }
-
- @Test
- fun recommendation_gutsOpen_contentDescriptionIsForGuts() {
- whenever(mediaViewController.isGutsVisible).thenReturn(true)
- player.attachRecommendation(recommendationViewHolder)
-
- val gutsTextString = "gutsText"
- whenever(gutsText.text).thenReturn(gutsTextString)
- player.bindRecommendation(smartspaceData)
-
- val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
- verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
- val description = descriptionCaptor.value.toString()
-
- assertThat(description).isEqualTo(gutsTextString)
- }
-
- @Test
- fun recommendation_gutsClosed_contentDescriptionIsForPlayer() {
- whenever(mediaViewController.isGutsVisible).thenReturn(false)
- player.attachRecommendation(recommendationViewHolder)
-
- player.bindRecommendation(smartspaceData)
-
- val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
- verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
- val description = descriptionCaptor.value.toString()
-
- assertThat(description)
- .isEqualTo(context.getString(R.string.controls_media_smartspace_rec_header))
- }
-
- @Test
- fun recommendation_gutsChangesFromOpenToClosed_contentDescriptionUpdated() {
- // Start out open
- whenever(mediaViewController.isGutsVisible).thenReturn(true)
- whenever(gutsText.text).thenReturn("gutsText")
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
-
- // Update to closed by long pressing
- val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
- verify(viewHolder.player).onLongClickListener = captor.capture()
- reset(viewHolder.player)
-
- whenever(mediaViewController.isGutsVisible).thenReturn(false)
- captor.value.onLongClick(viewHolder.player)
-
- // Then content description is now the player content description
- val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
- verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
- val description = descriptionCaptor.value.toString()
-
- assertThat(description)
- .isEqualTo(context.getString(R.string.controls_media_smartspace_rec_header))
- }
-
- @Test
- fun recommendation_gutsChangesFromClosedToOpen_contentDescriptionUpdated() {
- // Start out closed
- whenever(mediaViewController.isGutsVisible).thenReturn(false)
- val gutsTextString = "gutsText"
- whenever(gutsText.text).thenReturn(gutsTextString)
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
-
- // Update to open by long pressing
- val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
- verify(viewHolder.player).onLongClickListener = captor.capture()
- reset(viewHolder.player)
-
- whenever(mediaViewController.isGutsVisible).thenReturn(true)
- captor.value.onLongClick(viewHolder.player)
-
- // Then content description is now the guts content description
- val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
- verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
- val description = descriptionCaptor.value.toString()
-
- assertThat(description).isEqualTo(gutsTextString)
- }
-
- /* ***** END guts tests for the recommendations ***** */
-
@Test
fun actionPlayPauseClick_isLogged() {
val semanticActions =
@@ -1887,578 +1626,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun recommendation_gutsClosed_longPressOpens() {
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
- whenever(mediaViewController.isGutsVisible).thenReturn(false)
-
- val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
- verify(recommendationViewHolder.recommendations).setOnLongClickListener(captor.capture())
-
- captor.value.onLongClick(recommendationViewHolder.recommendations)
- verify(mediaViewController).openGuts()
- verify(logger).logLongPressOpen(anyInt(), eq(PACKAGE), eq(instanceId))
- }
-
- @Test
- fun recommendation_settingsButtonClick_isLogged() {
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
-
- settings.callOnClick()
- verify(logger).logLongPressSettings(anyInt(), eq(PACKAGE), eq(instanceId))
-
- val captor = ArgumentCaptor.forClass(Intent::class.java)
- verify(activityStarter).startActivity(captor.capture(), eq(true))
-
- assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS)
- }
-
- @Test
- fun recommendation_dismissButton_isLogged() {
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
-
- dismiss.callOnClick()
- verify(logger).logLongPressDismiss(anyInt(), eq(PACKAGE), eq(instanceId))
- }
-
- @Test
- fun recommendation_tapOnCard_isLogged() {
- val captor = ArgumentCaptor.forClass(View.OnClickListener::class.java)
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
-
- verify(recommendationViewHolder.recommendations).setOnClickListener(captor.capture())
- captor.value.onClick(recommendationViewHolder.recommendations)
-
- verify(logger).logRecommendationCardTap(eq(PACKAGE), eq(instanceId))
- }
-
- @Test
- fun recommendation_tapOnItem_isLogged() {
- val captor = ArgumentCaptor.forClass(View.OnClickListener::class.java)
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(smartspaceData)
-
- verify(coverContainer1).setOnClickListener(captor.capture())
- captor.value.onClick(recommendationViewHolder.recommendations)
-
- verify(logger).logRecommendationItemTap(eq(PACKAGE), eq(instanceId), eq(0))
- }
-
- @Test
- fun bindRecommendation_listHasTooFewRecs_notDisplayed() {
- player.attachRecommendation(recommendationViewHolder)
- val icon =
- Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata)
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "title1")
- .setSubtitle("subtitle1")
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "title2")
- .setSubtitle("subtitle2")
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
-
- player.bindRecommendation(data)
-
- assertThat(recTitle1.text).isEqualTo("")
- verify(mediaViewController, never()).refreshState()
- }
-
- @Test
- fun bindRecommendation_listHasTooFewRecsWithIcons_notDisplayed() {
- player.attachRecommendation(recommendationViewHolder)
- val icon =
- Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata)
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "title1")
- .setSubtitle("subtitle1")
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "title2")
- .setSubtitle("subtitle2")
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "empty icon 1")
- .setSubtitle("subtitle2")
- .setIcon(null)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "empty icon 2")
- .setSubtitle("subtitle2")
- .setIcon(null)
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
-
- player.bindRecommendation(data)
-
- assertThat(recTitle1.text).isEqualTo("")
- verify(mediaViewController, never()).refreshState()
- }
-
- @Test
- fun bindRecommendation_hasTitlesAndSubtitles() {
- player.attachRecommendation(recommendationViewHolder)
-
- val title1 = "Title1"
- val title2 = "Title2"
- val title3 = "Title3"
- val subtitle1 = "Subtitle1"
- val subtitle2 = "Subtitle2"
- val subtitle3 = "Subtitle3"
- val icon =
- Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata)
-
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", title1)
- .setSubtitle(subtitle1)
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", title2)
- .setSubtitle(subtitle2)
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id3", title3)
- .setSubtitle(subtitle3)
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
- player.bindRecommendation(data)
-
- assertThat(recTitle1.text).isEqualTo(title1)
- assertThat(recTitle2.text).isEqualTo(title2)
- assertThat(recTitle3.text).isEqualTo(title3)
- assertThat(recSubtitle1.text).isEqualTo(subtitle1)
- assertThat(recSubtitle2.text).isEqualTo(subtitle2)
- assertThat(recSubtitle3.text).isEqualTo(subtitle3)
- }
-
- @Test
- fun bindRecommendation_noTitle_subtitleNotShown() {
- player.attachRecommendation(recommendationViewHolder)
-
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "")
- .setSubtitle("fake subtitle")
- .setIcon(
- Icon.createWithResource(
- context,
- com.android.settingslib.R.drawable.ic_1x_mobiledata,
- )
- )
- .setExtras(Bundle.EMPTY)
- .build()
- )
- )
- player.bindRecommendation(data)
-
- assertThat(recSubtitle1.text).isEqualTo("")
- }
-
- @Test
- fun bindRecommendation_someHaveTitles_allTitleViewsShown() {
- useRealConstraintSets()
- player.attachRecommendation(recommendationViewHolder)
-
- val icon =
- Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata)
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "")
- .setSubtitle("fake subtitle")
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "title2")
- .setSubtitle("fake subtitle")
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id3", "")
- .setSubtitle("fake subtitle")
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
- player.bindRecommendation(data)
-
- assertThat(expandedSet.getVisibility(recTitle1.id)).isEqualTo(ConstraintSet.VISIBLE)
- assertThat(expandedSet.getVisibility(recTitle2.id)).isEqualTo(ConstraintSet.VISIBLE)
- assertThat(expandedSet.getVisibility(recTitle3.id)).isEqualTo(ConstraintSet.VISIBLE)
- }
-
- @Test
- fun bindRecommendation_someHaveSubtitles_allSubtitleViewsShown() {
- useRealConstraintSets()
- player.attachRecommendation(recommendationViewHolder)
-
- val icon =
- Icon.createWithResource(context, com.android.settingslib.R.drawable.ic_1x_mobiledata)
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "")
- .setSubtitle("")
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "title2")
- .setSubtitle("subtitle2")
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id3", "title3")
- .setSubtitle("")
- .setIcon(icon)
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
- player.bindRecommendation(data)
-
- assertThat(expandedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.VISIBLE)
- assertThat(expandedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.VISIBLE)
- assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.VISIBLE)
- }
-
- @Test
- fun bindRecommendation_noneHaveSubtitles_subtitleViewsGone() {
- useRealConstraintSets()
- player.attachRecommendation(recommendationViewHolder)
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "title1")
- .setSubtitle("")
- .setIcon(
- Icon.createWithResource(
- context,
- com.android.settingslib.R.drawable.ic_1x_mobiledata,
- )
- )
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "title2")
- .setSubtitle("")
- .setIcon(Icon.createWithResource(context, R.drawable.ic_alarm))
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id3", "title3")
- .setSubtitle("")
- .setIcon(
- Icon.createWithResource(
- context,
- com.android.settingslib.R.drawable.ic_3g_mobiledata,
- )
- )
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
-
- player.bindRecommendation(data)
-
- assertThat(expandedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(expandedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE)
- }
-
- @Test
- fun bindRecommendation_noneHaveTitles_titleAndSubtitleViewsGone() {
- useRealConstraintSets()
- player.attachRecommendation(recommendationViewHolder)
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "")
- .setSubtitle("subtitle1")
- .setIcon(
- Icon.createWithResource(
- context,
- com.android.settingslib.R.drawable.ic_1x_mobiledata,
- )
- )
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "")
- .setSubtitle("subtitle2")
- .setIcon(Icon.createWithResource(context, R.drawable.ic_alarm))
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id3", "")
- .setSubtitle("subtitle3")
- .setIcon(
- Icon.createWithResource(
- context,
- com.android.settingslib.R.drawable.ic_3g_mobiledata,
- )
- )
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
-
- player.bindRecommendation(data)
-
- assertThat(expandedSet.getVisibility(recTitle1.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(expandedSet.getVisibility(recTitle2.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(expandedSet.getVisibility(recTitle3.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(expandedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(expandedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(expandedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(collapsedSet.getVisibility(recTitle1.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(collapsedSet.getVisibility(recTitle2.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(collapsedSet.getVisibility(recTitle3.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(collapsedSet.getVisibility(recSubtitle1.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(collapsedSet.getVisibility(recSubtitle2.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(collapsedSet.getVisibility(recSubtitle3.id)).isEqualTo(ConstraintSet.GONE)
- }
-
- @Test
- fun bindRecommendation_setAfterExecutors() {
- val albumArt = getColorIcon(Color.RED)
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "title1")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "title2")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id3", "title3")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
-
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(data)
- bgExecutor.runAllReady()
- mainExecutor.runAllReady()
-
- verify(recCardTitle).setTextColor(any<Int>())
- verify(recAppIconItem, times(3)).setImageDrawable(any<Drawable>())
- verify(coverItem, times(3)).setImageDrawable(any<Drawable>())
- verify(coverItem, times(3)).imageMatrix = any()
- }
-
- @Test
- fun bindRecommendationWithProgressBars() {
- useRealConstraintSets()
- val albumArt = getColorIcon(Color.RED)
- val bundle =
- Bundle().apply {
- putInt(
- MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
- MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED,
- )
- putDouble(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.5)
- }
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "title1")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(bundle)
- .build(),
- SmartspaceAction.Builder("id2", "title2")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id3", "title3")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
-
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(data)
-
- verify(recProgressBar1).setProgress(50)
- verify(recProgressBar1).visibility = View.VISIBLE
- verify(recProgressBar2).visibility = View.GONE
- verify(recProgressBar3).visibility = View.GONE
- assertThat(recSubtitle1.visibility).isEqualTo(View.GONE)
- assertThat(recSubtitle2.visibility).isEqualTo(View.VISIBLE)
- assertThat(recSubtitle3.visibility).isEqualTo(View.VISIBLE)
- }
-
- @Test
- fun bindRecommendation_carouselNotFitThreeRecs_OrientationPortrait() {
- useRealConstraintSets()
- val albumArt = getColorIcon(Color.RED)
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "title1")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "title2")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id3", "title3")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
-
- // set the screen width less than the width of media controls.
- player.context.resources.configuration.screenWidthDp = 350
- player.context.resources.configuration.orientation = Configuration.ORIENTATION_PORTRAIT
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(data)
-
- val res = player.context.resources
- val displayAvailableWidth =
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 350f, res.displayMetrics).toInt()
- val recCoverWidth: Int =
- (res.getDimensionPixelSize(R.dimen.qs_media_rec_album_width) +
- res.getDimensionPixelSize(R.dimen.qs_media_info_spacing) * 2)
- val numOfRecs = displayAvailableWidth / recCoverWidth
-
- assertThat(player.numberOfFittedRecommendations).isEqualTo(numOfRecs)
- recommendationViewHolder.mediaCoverContainers.forEachIndexed { index, container ->
- if (index < numOfRecs) {
- assertThat(expandedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.VISIBLE)
- assertThat(collapsedSet.getVisibility(container.id))
- .isEqualTo(ConstraintSet.VISIBLE)
- } else {
- assertThat(expandedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(collapsedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.GONE)
- }
- }
- }
-
- @Test
- fun bindRecommendation_carouselNotFitThreeRecs_OrientationLandscape() {
- useRealConstraintSets()
- val albumArt = getColorIcon(Color.RED)
- val data =
- smartspaceData.copy(
- recommendations =
- listOf(
- SmartspaceAction.Builder("id1", "title1")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id2", "title2")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- SmartspaceAction.Builder("id3", "title3")
- .setSubtitle("subtitle1")
- .setIcon(albumArt)
- .setExtras(Bundle.EMPTY)
- .build(),
- )
- )
-
- // set the screen width less than the width of media controls.
- // We should have dp width less than 378 to test. In landscape we should have 2x.
- player.context.resources.configuration.screenWidthDp = 700
- player.context.resources.configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
- player.attachRecommendation(recommendationViewHolder)
- player.bindRecommendation(data)
-
- val res = player.context.resources
- val displayAvailableWidth =
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 350f, res.displayMetrics).toInt()
- val recCoverWidth: Int =
- (res.getDimensionPixelSize(R.dimen.qs_media_rec_album_width) +
- res.getDimensionPixelSize(R.dimen.qs_media_info_spacing) * 2)
- val numOfRecs = displayAvailableWidth / recCoverWidth
-
- assertThat(player.numberOfFittedRecommendations).isEqualTo(numOfRecs)
- recommendationViewHolder.mediaCoverContainers.forEachIndexed { index, container ->
- if (index < numOfRecs) {
- assertThat(expandedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.VISIBLE)
- assertThat(collapsedSet.getVisibility(container.id))
- .isEqualTo(ConstraintSet.VISIBLE)
- } else {
- assertThat(expandedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.GONE)
- assertThat(collapsedSet.getVisibility(container.id)).isEqualTo(ConstraintSet.GONE)
- }
- }
- }
-
- @Test
- fun addTwoRecommendationGradients_differentStates() {
- // Setup redArtwork and its color scheme.
- val redArt = getColorIcon(Color.RED)
- val redWallpaperColor = player.getWallpaperColor(redArt)
- val redColorScheme = ColorScheme(redWallpaperColor, true, Style.CONTENT)
-
- // Setup greenArt and its color scheme.
- val greenArt = getColorIcon(Color.GREEN)
- val greenWallpaperColor = player.getWallpaperColor(greenArt)
- val greenColorScheme = ColorScheme(greenWallpaperColor, true, Style.CONTENT)
-
- // Add gradient to both icons.
- val redArtwork = player.addGradientToRecommendationAlbum(redArt, redColorScheme, 10, 10)
- val greenArtwork =
- player.addGradientToRecommendationAlbum(greenArt, greenColorScheme, 10, 10)
-
- // They should have different constant states as they have different gradient color.
- assertThat(redArtwork.getDrawable(1).constantState)
- .isNotEqualTo(greenArtwork.getDrawable(1).constantState)
- }
-
- @Test
fun onButtonClick_playsTouchRipple() {
val semanticActions =
MediaButton(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
index e765b6f77155..760f73c726a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
@@ -42,7 +42,6 @@ import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.media.controls.ui.view.GutsViewHolder
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaViewHolder
-import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
@@ -79,7 +78,6 @@ class MediaViewControllerTest : SysuiTestCase() {
private val configurationController =
com.android.systemui.statusbar.phone.ConfigurationControllerImpl(context)
private var player = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0)
- private var recommendation = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0)
private val clock = FakeSystemClock()
private lateinit var mainExecutor: FakeExecutor
private lateinit var seekBar: SeekBar
@@ -110,9 +108,6 @@ class MediaViewControllerTest : SysuiTestCase() {
@Mock private lateinit var mockCopiedState: TransitionViewState
@Mock private lateinit var detailWidgetState: WidgetState
@Mock private lateinit var controlWidgetState: WidgetState
- @Mock private lateinit var mediaTitleWidgetState: WidgetState
- @Mock private lateinit var mediaSubTitleWidgetState: WidgetState
- @Mock private lateinit var mediaContainerWidgetState: WidgetState
@Mock private lateinit var seekBarViewModel: SeekBarViewModel
@Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
@Mock private lateinit var globalSettings: GlobalSettings
@@ -145,7 +140,7 @@ class MediaViewControllerTest : SysuiTestCase() {
context: Context,
animId: Int,
motionInterpolator: Interpolator?,
- vararg targets: View?
+ vararg targets: View?,
): AnimatorSet {
return mockAnimator
}
@@ -158,7 +153,7 @@ class MediaViewControllerTest : SysuiTestCase() {
fun testOrientationChanged_heightOfPlayerIsUpdated() {
val newConfig = Configuration()
- mediaViewController.attach(player, MediaViewController.TYPE.PLAYER)
+ mediaViewController.attach(player)
// Change the height to see the effect of orientation change.
MediaViewHolder.backgroundIds.forEach { id ->
mediaViewController.expandedLayout.getConstraint(id).layout.mHeight = 10
@@ -177,30 +172,8 @@ class MediaViewControllerTest : SysuiTestCase() {
}
@Test
- fun testOrientationChanged_heightOfRecCardIsUpdated() {
- val newConfig = Configuration()
-
- mediaViewController.attach(recommendation, MediaViewController.TYPE.RECOMMENDATION)
- // Change the height to see the effect of orientation change.
- mediaViewController.expandedLayout
- .getConstraint(RecommendationViewHolder.backgroundId)
- .layout
- .mHeight = 10
- newConfig.orientation = ORIENTATION_LANDSCAPE
- configurationController.onConfigurationChanged(newConfig)
-
- assertTrue(
- mediaViewController.expandedLayout
- .getConstraint(RecommendationViewHolder.backgroundId)
- .layout
- .mHeight ==
- context.resources.getDimensionPixelSize(R.dimen.qs_media_session_height_expanded)
- )
- }
-
- @Test
fun testObtainViewState_applySquishFraction_toPlayerTransitionViewState_height() {
- mediaViewController.attach(player, MediaViewController.TYPE.PLAYER)
+ mediaViewController.attach(player)
player.measureState =
TransitionViewState().apply {
this.height = 100
@@ -224,29 +197,8 @@ class MediaViewControllerTest : SysuiTestCase() {
}
@Test
- fun testObtainViewState_applySquishFraction_toRecommendationTransitionViewState_height() {
- mediaViewController.attach(recommendation, MediaViewController.TYPE.RECOMMENDATION)
- recommendation.measureState = TransitionViewState().apply { this.height = 100 }
- mediaHostStateHolder.expansion = 1f
- val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
- val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
- mediaHostStateHolder.measurementInput =
- MeasurementInput(widthMeasureSpec, heightMeasureSpec)
-
- // Test no squish
- mediaHostStateHolder.squishFraction = 1f
- assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 100)
- assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.measureHeight == 100)
-
- // Test half squish
- mediaHostStateHolder.squishFraction = 0.5f
- assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 50)
- assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.measureHeight == 100)
- }
-
- @Test
fun testObtainViewState_expandedMatchesParentHeight() {
- mediaViewController.attach(player, MediaViewController.TYPE.PLAYER)
+ mediaViewController.attach(player)
player.measureState =
TransitionViewState().apply {
this.height = 100
@@ -283,7 +235,7 @@ class MediaViewControllerTest : SysuiTestCase() {
.thenReturn(
mutableMapOf(
R.id.media_progress_bar to controlWidgetState,
- R.id.header_artist to detailWidgetState
+ R.id.header_artist to detailWidgetState,
)
)
whenever(mockCopiedState.measureHeight).thenReturn(200)
@@ -311,7 +263,7 @@ class MediaViewControllerTest : SysuiTestCase() {
.thenReturn(
mutableMapOf(
R.id.media_progress_bar to controlWidgetState,
- R.id.header_artist to detailWidgetState
+ R.id.header_artist to detailWidgetState,
)
)
whenever(mockCopiedState.measureHeight).thenReturn(200)
@@ -332,46 +284,6 @@ class MediaViewControllerTest : SysuiTestCase() {
verify(detailWidgetState, never()).alpha = floatThat { it > 0 }
}
- @Test
- fun testSquishViewState_applySquishFraction_toTransitionViewState_alpha_forRecommendation() {
- whenever(mockViewState.copy()).thenReturn(mockCopiedState)
- whenever(mockCopiedState.widgetStates)
- .thenReturn(
- mutableMapOf(
- R.id.media_title to mediaTitleWidgetState,
- R.id.media_subtitle to mediaSubTitleWidgetState,
- R.id.media_cover1_container to mediaContainerWidgetState
- )
- )
- whenever(mockCopiedState.measureHeight).thenReturn(360)
- // media container widgets occupy [20, 300]
- whenever(mediaContainerWidgetState.y).thenReturn(20F)
- whenever(mediaContainerWidgetState.height).thenReturn(280)
- whenever(mediaContainerWidgetState.alpha).thenReturn(1F)
- // media title widgets occupy [320, 330]
- whenever(mediaTitleWidgetState.y).thenReturn(320F)
- whenever(mediaTitleWidgetState.height).thenReturn(10)
- whenever(mediaTitleWidgetState.alpha).thenReturn(1F)
- // media subtitle widgets occupy [340, 350]
- whenever(mediaSubTitleWidgetState.y).thenReturn(340F)
- whenever(mediaSubTitleWidgetState.height).thenReturn(10)
- whenever(mediaSubTitleWidgetState.alpha).thenReturn(1F)
-
- // in current beizer, when the progress reach 0.38, the result will be 0.5
- mediaViewController.squishViewState(mockViewState, 307.6F / 360F)
- verify(mediaContainerWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
- mediaViewController.squishViewState(mockViewState, 320F / 360F)
- verify(mediaContainerWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
- // media title and media subtitle are in same widget group, should be calculate together and
- // have same alpha
- mediaViewController.squishViewState(mockViewState, 353.8F / 360F)
- verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
- verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
- mediaViewController.squishViewState(mockViewState, 360F / 360F)
- verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
- verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
- }
-
@EnableSceneContainer
@Test
fun attachPlayer_seekBarDisabled_seekBarVisibilityIsSetToInvisible() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractorKosmos.kt
deleted file mode 100644
index 1edd405f4af6..000000000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractorKosmos.kt
+++ /dev/null
@@ -1,37 +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.media.controls.domain.pipeline.interactor
-
-import android.content.applicationContext
-import com.android.systemui.broadcast.broadcastSender
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.media.controls.data.repository.mediaFilterRepository
-import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor
-import com.android.systemui.plugins.activityStarter
-
-val Kosmos.mediaRecommendationsInteractor by
- Kosmos.Fixture {
- MediaRecommendationsInteractor(
- applicationScope = applicationCoroutineScope,
- applicationContext = applicationContext,
- repository = mediaFilterRepository,
- mediaDataProcessor = mediaDataProcessor,
- broadcastSender = broadcastSender,
- activityStarter = activityStarter,
- )
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelKosmos.kt
index 5e6434d84538..976b4046f58d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelKosmos.kt
@@ -37,7 +37,6 @@ val Kosmos.mediaCarouselViewModel by
visualStabilityProvider = visualStabilityProvider,
interactor = mediaCarouselInteractor,
controlInteractorFactory = mediaControlInteractorFactory,
- recommendationsViewModel = mediaRecommendationsViewModel,
logger = mediaUiEventLogger,
mediaLogger = mediaLogger,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelKosmos.kt
deleted file mode 100644
index 34a527781979..000000000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModelKosmos.kt
+++ /dev/null
@@ -1,33 +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.media.controls.ui.viewmodel
-
-import android.content.applicationContext
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.media.controls.domain.pipeline.interactor.mediaRecommendationsInteractor
-import com.android.systemui.media.controls.util.mediaUiEventLogger
-
-val Kosmos.mediaRecommendationsViewModel by
- Kosmos.Fixture {
- MediaRecommendationsViewModel(
- applicationContext = applicationContext,
- backgroundDispatcher = testDispatcher,
- interactor = mediaRecommendationsInteractor,
- logger = mediaUiEventLogger,
- )
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
index 00deaafd7009..dbd1c9ce56fe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
@@ -19,6 +19,7 @@ package com.android.systemui.model
import android.view.Display
import com.android.systemui.common.domain.interactor.SysUIStateDisplaysInteractor
import com.android.systemui.display.data.repository.FakePerDisplayRepository
+import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -45,5 +46,5 @@ val Kosmos.sysUiStateFactory by Fixture {
val Kosmos.fakeSysUIStatePerDisplayRepository by Fixture { FakePerDisplayRepository<SysUiState>() }
val Kosmos.sysuiStateInteractor by Fixture {
- SysUIStateDisplaysInteractor(fakeSysUIStatePerDisplayRepository)
+ SysUIStateDisplaysInteractor(fakeSysUIStatePerDisplayRepository, displayRepository)
}
diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp
index 97574e6e35e3..aad534c3289c 100644
--- a/packages/Vcn/service-b/Android.bp
+++ b/packages/Vcn/service-b/Android.bp
@@ -29,7 +29,10 @@ filegroup {
"vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java",
],
}),
- visibility: ["//frameworks/base/services/core"],
+ visibility: [
+ "//frameworks/base/services/core",
+ "//packages/modules/Connectivity/service-t",
+ ],
}
// TODO: b/374174952 This library is only used in "service-connectivity-b-platform"
diff --git a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
index b9dcc6160d68..aac217b3cc7a 100644
--- a/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
+++ b/packages/Vcn/service-b/src/com/android/server/ConnectivityServiceInitializerB.java
@@ -37,9 +37,27 @@ public final class ConnectivityServiceInitializerB extends SystemService {
private static final String TAG = ConnectivityServiceInitializerB.class.getSimpleName();
private final VcnManagementService mVcnManagementService;
+ // STOPSHIP: b/385203616 This static flag is for handling a temporary case when the mainline
+ // module prebuilt has updated to register the VCN but the platform change to remove
+ // registration is not merged. After mainline prebuilt is updated, we should merge the platform
+ // ASAP and remove this static check. This check is safe because both mainline and platform
+ // registration are triggered from the same method on the same thread.
+ private static boolean sIsRegistered = false;
+
public ConnectivityServiceInitializerB(Context context) {
super(context);
- mVcnManagementService = VcnManagementService.create(context);
+
+ if (!sIsRegistered) {
+ mVcnManagementService = VcnManagementService.create(context);
+ sIsRegistered = true;
+ } else {
+ mVcnManagementService = null;
+ Log.e(
+ TAG,
+ "Ignore this registration since VCN is already registered. It will happen when"
+ + " the mainline module prebuilt has updated to register the VCN but the"
+ + " platform change to remove registration is not merged.");
+ }
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
index 57bbb4a7a0a7..90ddc43ae3ed 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
@@ -86,13 +86,6 @@ public class AutoclickTypePanel {
})
public @interface Corner {}
- private static final @Corner int[] CORNER_ROTATION_ORDER = {
- CORNER_BOTTOM_RIGHT,
- CORNER_BOTTOM_LEFT,
- CORNER_TOP_LEFT,
- CORNER_TOP_RIGHT
- };
-
// An interface exposed to {@link AutoclickController) to handle different actions on the panel,
// including changing autoclick type, pausing/resuming autoclick.
public interface ClickPanelControllerInterface {
@@ -136,10 +129,9 @@ public class AutoclickTypePanel {
// Whether autoclick is paused.
private boolean mPaused = false;
- // Tracks the current corner position of the panel using an index into CORNER_ROTATION_ORDER
- // array. This allows the panel to cycle through screen corners in a defined sequence when
- // repositioned.
- private int mCurrentCornerIndex = 0;
+
+ // The current corner position of the panel, default to bottom right.
+ private @Corner int mCurrentCorner = CORNER_BOTTOM_RIGHT;
private final LinearLayout mLeftClickButton;
private final LinearLayout mRightClickButton;
@@ -257,13 +249,13 @@ public class AutoclickTypePanel {
params.gravity = Gravity.START | Gravity.TOP;
// Set the current corner to be bottom-left to ensure that the subsequent reposition
// action rotates the panel clockwise from bottom-left towards top-left.
- mCurrentCornerIndex = 1;
+ mCurrentCorner = CORNER_BOTTOM_LEFT;
} else {
// Snap to right edge. Set params.gravity to make sure x, y offsets from correct anchor.
params.gravity = Gravity.END | Gravity.TOP;
// Set the current corner to be top-right to ensure that the subsequent reposition
// action rotates the panel clockwise from top-right towards bottom-right.
- mCurrentCornerIndex = 3;
+ mCurrentCorner = CORNER_TOP_RIGHT;
}
// Apply final position: set params.x to be edge margin, params.y to maintain vertical
@@ -415,10 +407,10 @@ public class AutoclickTypePanel {
/** Moves the panel to the next corner in clockwise direction. */
private void moveToNextCorner() {
- @Corner int nextCornerIndex = (mCurrentCornerIndex + 1) % CORNER_ROTATION_ORDER.length;
- mCurrentCornerIndex = nextCornerIndex;
+ @Corner int nextCorner = (mCurrentCorner + 1) % 4;
+ mCurrentCorner = nextCorner;
- setPanelPositionForCorner(mParams, mCurrentCornerIndex);
+ setPanelPositionForCorner(mParams, mCurrentCorner);
mWindowManager.updateViewLayout(mContentView, mParams);
}
@@ -457,7 +449,7 @@ public class AutoclickTypePanel {
String.valueOf(mParams.gravity),
String.valueOf(mParams.x),
String.valueOf(mParams.y),
- String.valueOf(mCurrentCornerIndex)
+ String.valueOf(mCurrentCorner)
});
Settings.Secure.putStringForUser(mContext.getContentResolver(),
ACCESSIBILITY_AUTOCLICK_PANEL_POSITION, positionString, mUserId);
@@ -473,7 +465,7 @@ public class AutoclickTypePanel {
ACCESSIBILITY_AUTOCLICK_PANEL_POSITION, mUserId);
if (savedPosition == null) {
setPanelPositionForCorner(mParams, CORNER_BOTTOM_RIGHT);
- mCurrentCornerIndex = 0;
+ mCurrentCorner = CORNER_BOTTOM_RIGHT;
return;
}
@@ -481,7 +473,7 @@ public class AutoclickTypePanel {
String[] parts = TextUtils.split(savedPosition, POSITION_DELIMITER);
if (!isValidPositionParts(parts)) {
setPanelPositionForCorner(mParams, CORNER_BOTTOM_RIGHT);
- mCurrentCornerIndex = 0;
+ mCurrentCorner = CORNER_BOTTOM_RIGHT;
return;
}
@@ -489,7 +481,7 @@ public class AutoclickTypePanel {
mParams.gravity = Integer.parseInt(parts[0]);
mParams.x = Integer.parseInt(parts[1]);
mParams.y = Integer.parseInt(parts[2]);
- mCurrentCornerIndex = Integer.parseInt(parts[3]);
+ mCurrentCorner = Integer.parseInt(parts[3]);
}
private boolean isValidPositionParts(String[] parts) {
@@ -538,8 +530,8 @@ public class AutoclickTypePanel {
@VisibleForTesting
@Corner
- int getCurrentCornerIndexForTesting() {
- return mCurrentCornerIndex;
+ int getCurrentCornerForTesting() {
+ return mCurrentCorner;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 13d367a95942..336a35e7a7e3 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -3433,8 +3433,12 @@ public class OomAdjuster {
// Process has user visible activities.
return PROCESS_CAPABILITY_CPU_TIME;
}
- if (app.mServices.hasUndemotedShortForegroundService(nowUptime)) {
- // It running a short fgs, just give it cpu time.
+ if (Flags.prototypeAggressiveFreezing()) {
+ if (app.mServices.hasUndemotedShortForegroundService(nowUptime)) {
+ // Grant cpu time for short FGS even when aggressively freezing.
+ return PROCESS_CAPABILITY_CPU_TIME;
+ }
+ } else if (app.mServices.hasForegroundServices()) {
return PROCESS_CAPABILITY_CPU_TIME;
}
if (app.mReceivers.numberOfCurReceivers() > 0) {
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index 7f853844c326..67e1ccc6a850 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -138,11 +138,6 @@ final class InputGestureManager {
KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
),
createKeyGesture(
- KeyEvent.KEYCODE_DEL,
- KeyEvent.META_META_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_BACK
- ),
- createKeyGesture(
KeyEvent.KEYCODE_ESCAPE,
KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_BACK
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index f40d0dd18213..2d937bdcc683 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -16,6 +16,8 @@
package com.android.server.location.contexthub;
+import static com.android.server.location.contexthub.ContextHubTransactionManager.RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT;
+
import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.Context;
@@ -44,6 +46,9 @@ import com.android.internal.annotations.GuardedBy;
import java.util.Collection;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@@ -100,7 +105,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
private final Object mOpenSessionLock = new Object();
- static class SessionInfo {
+ static class Session {
enum SessionState {
/* The session is pending acceptance from the remote endpoint. */
PENDING,
@@ -119,7 +124,15 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
*/
private final Set<Integer> mPendingSequenceNumbers = new HashSet<>();
- SessionInfo(HubEndpointInfo remoteEndpointInfo, boolean remoteInitiated) {
+ /**
+ * Stores the history of received messages that are timestamped. We use a LinkedHashMap to
+ * guarantee insertion ordering for easier manipulation of removing expired entries.
+ *
+ * <p>The key is the sequence number, and the value is the timestamp in milliseconds.
+ */
+ private final LinkedHashMap<Integer, Long> mRxMessageHistoryMap = new LinkedHashMap<>();
+
+ Session(HubEndpointInfo remoteEndpointInfo, boolean remoteInitiated) {
mRemoteEndpointInfo = remoteEndpointInfo;
mRemoteInitiated = remoteInitiated;
}
@@ -157,11 +170,43 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
consumer.accept(sequenceNumber);
}
}
+
+ public boolean isInMessageHistory(HubMessage message) {
+ // Clean up the history
+ Iterator<Map.Entry<Integer, Long>> iterator =
+ mRxMessageHistoryMap.entrySet().iterator();
+ long nowMillis = System.currentTimeMillis();
+ while (iterator.hasNext()) {
+ Map.Entry<Integer, Long> nextEntry = iterator.next();
+ long expiryMillis = RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT.toMillis();
+ if (nowMillis >= nextEntry.getValue() + expiryMillis) {
+ iterator.remove();
+ }
+ break;
+ }
+
+ return mRxMessageHistoryMap.containsKey(message.getMessageSequenceNumber());
+ }
+
+ public void addMessageToHistory(HubMessage message) {
+ if (mRxMessageHistoryMap.containsKey(message.getMessageSequenceNumber())) {
+ long value = mRxMessageHistoryMap.get(message.getMessageSequenceNumber());
+ Log.w(
+ TAG,
+ "Message already exists in history (inserted @ "
+ + value
+ + " ms): "
+ + message);
+ return;
+ }
+ mRxMessageHistoryMap.put(
+ message.getMessageSequenceNumber(), System.currentTimeMillis());
+ }
}
/** A map between a session ID which maps to its current state. */
@GuardedBy("mOpenSessionLock")
- private final SparseArray<SessionInfo> mSessionInfoMap = new SparseArray<>();
+ private final SparseArray<Session> mSessionMap = new SparseArray<>();
/** The package name of the app that created the endpoint */
private final String mPackageName;
@@ -232,7 +277,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
synchronized (mOpenSessionLock) {
try {
- mSessionInfoMap.put(sessionId, new SessionInfo(destination, false));
+ mSessionMap.put(sessionId, new Session(destination, false));
mHubInterface.openEndpointSession(
sessionId, halEndpointInfo.id, mHalEndpointInfo.id, serviceDescriptor);
} catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
@@ -263,8 +308,8 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
super.unregister_enforcePermission();
synchronized (mOpenSessionLock) {
// Iterate in reverse since cleanupSessionResources will remove the entry
- for (int i = mSessionInfoMap.size() - 1; i >= 0; i--) {
- int id = mSessionInfoMap.keyAt(i);
+ for (int i = mSessionMap.size() - 1; i >= 0; i--) {
+ int id = mSessionMap.keyAt(i);
halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
cleanupSessionResources(id);
}
@@ -290,14 +335,14 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
public void openSessionRequestComplete(int sessionId) {
super.openSessionRequestComplete_enforcePermission();
synchronized (mOpenSessionLock) {
- SessionInfo info = mSessionInfoMap.get(sessionId);
+ Session info = mSessionMap.get(sessionId);
if (info == null) {
throw new IllegalArgumentException(
"openSessionRequestComplete for invalid session id=" + sessionId);
}
try {
mHubInterface.endpointSessionOpenComplete(sessionId);
- info.setSessionState(SessionInfo.SessionState.ACTIVE);
+ info.setSessionState(Session.SessionState.ACTIVE);
} catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
Log.e(TAG, "Exception while calling endpointSessionOpenComplete", e);
}
@@ -310,7 +355,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
int sessionId, HubMessage message, IContextHubTransactionCallback callback) {
super.sendMessage_enforcePermission();
synchronized (mOpenSessionLock) {
- SessionInfo info = mSessionInfoMap.get(sessionId);
+ Session info = mSessionMap.get(sessionId);
if (info == null) {
throw new IllegalArgumentException(
"sendMessage for invalid session id=" + sessionId);
@@ -393,9 +438,9 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
} else {
synchronized (mOpenSessionLock) {
// Iterate in reverse since cleanupSessionResources will remove the entry
- for (int i = mSessionInfoMap.size() - 1; i >= 0; i--) {
- int id = mSessionInfoMap.keyAt(i);
- HubEndpointInfo target = mSessionInfoMap.get(id).getRemoteEndpointInfo();
+ for (int i = mSessionMap.size() - 1; i >= 0; i--) {
+ int id = mSessionMap.keyAt(i);
+ HubEndpointInfo target = mSessionMap.get(id).getRemoteEndpointInfo();
if (!hasEndpointPermissions(target)) {
halCloseEndpointSessionNoThrow(id, Reason.PERMISSION_DENIED);
onCloseEndpointSession(id, Reason.PERMISSION_DENIED);
@@ -415,13 +460,13 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
sb.append("wakelock: ").append(mWakeLock);
}
synchronized (mOpenSessionLock) {
- if (mSessionInfoMap.size() != 0) {
+ if (mSessionMap.size() != 0) {
sb.append(System.lineSeparator());
sb.append(" sessions: ");
sb.append(System.lineSeparator());
}
- for (int i = 0; i < mSessionInfoMap.size(); i++) {
- int id = mSessionInfoMap.keyAt(i);
+ for (int i = 0; i < mSessionMap.size(); i++) {
+ int id = mSessionMap.keyAt(i);
int count = i + 1;
sb.append(
" "
@@ -429,7 +474,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
+ ". id="
+ id
+ ", remote:"
- + mSessionInfoMap.get(id).getRemoteEndpointInfo());
+ + mSessionMap.get(id).getRemoteEndpointInfo());
sb.append(System.lineSeparator());
}
}
@@ -485,23 +530,23 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
Log.w(TAG, "Unknown session ID in onEndpointSessionOpenComplete: id=" + sessionId);
return;
}
- mSessionInfoMap.get(sessionId).setSessionState(SessionInfo.SessionState.ACTIVE);
+ mSessionMap.get(sessionId).setSessionState(Session.SessionState.ACTIVE);
}
invokeCallback((consumer) -> consumer.onSessionOpenComplete(sessionId));
}
/* package */ void onMessageReceived(int sessionId, HubMessage message) {
- byte code = onMessageReceivedInternal(sessionId, message);
- if (code != ErrorCode.OK && message.isResponseRequired()) {
- sendMessageDeliveryStatus(sessionId, message.getMessageSequenceNumber(), code);
+ byte errorCode = onMessageReceivedInternal(sessionId, message);
+ if (errorCode != ErrorCode.OK && message.isResponseRequired()) {
+ sendMessageDeliveryStatus(sessionId, message.getMessageSequenceNumber(), errorCode);
}
}
/* package */ void onMessageDeliveryStatusReceived(
int sessionId, int sequenceNumber, byte errorCode) {
synchronized (mOpenSessionLock) {
- SessionInfo info = mSessionInfoMap.get(sessionId);
+ Session info = mSessionMap.get(sessionId);
if (info == null || !info.isActive()) {
Log.w(TAG, "Received delivery status for invalid session: id=" + sessionId);
return;
@@ -517,7 +562,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
/* package */ boolean hasSessionId(int sessionId) {
synchronized (mOpenSessionLock) {
- return mSessionInfoMap.contains(sessionId);
+ return mSessionMap.contains(sessionId);
}
}
@@ -531,8 +576,8 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
}
synchronized (mOpenSessionLock) {
- for (int i = mSessionInfoMap.size() - 1; i >= 0; i--) {
- int id = mSessionInfoMap.keyAt(i);
+ for (int i = mSessionMap.size() - 1; i >= 0; i--) {
+ int id = mSessionMap.keyAt(i);
onCloseEndpointSession(id, Reason.HUB_RESET);
}
}
@@ -555,7 +600,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
return Optional.of(Reason.UNSPECIFIED);
}
- mSessionInfoMap.put(sessionId, new SessionInfo(initiator, true));
+ mSessionMap.put(sessionId, new Session(initiator, true));
}
boolean success =
@@ -567,7 +612,6 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
private byte onMessageReceivedInternal(int sessionId, HubMessage message) {
- HubEndpointInfo remote;
synchronized (mOpenSessionLock) {
if (!isSessionActive(sessionId)) {
Log.e(
@@ -578,29 +622,36 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
+ message);
return ErrorCode.PERMANENT_ERROR;
}
- remote = mSessionInfoMap.get(sessionId).getRemoteEndpointInfo();
- }
+ HubEndpointInfo remote = mSessionMap.get(sessionId).getRemoteEndpointInfo();
+ if (mSessionMap.get(sessionId).isInMessageHistory(message)) {
+ Log.e(TAG, "Dropping duplicate message: " + message);
+ return ErrorCode.TRANSIENT_ERROR;
+ }
- try {
- Binder.withCleanCallingIdentity(
- () -> {
- if (!notePermissions(remote)) {
- throw new RuntimeException(
- "Dropping message from "
- + remote
- + ". "
- + mPackageName
- + " doesn't have permission");
- }
- });
- } catch (RuntimeException e) {
- Log.e(TAG, e.getMessage());
- return ErrorCode.PERMISSION_DENIED;
- }
+ try {
+ Binder.withCleanCallingIdentity(
+ () -> {
+ if (!notePermissions(remote)) {
+ throw new RuntimeException(
+ "Dropping message from "
+ + remote
+ + ". "
+ + mPackageName
+ + " doesn't have permission");
+ }
+ });
+ } catch (RuntimeException e) {
+ Log.e(TAG, e.getMessage());
+ return ErrorCode.PERMISSION_DENIED;
+ }
- boolean success =
- invokeCallback((consumer) -> consumer.onMessageReceived(sessionId, message));
- return success ? ErrorCode.OK : ErrorCode.TRANSIENT_ERROR;
+ boolean success =
+ invokeCallback((consumer) -> consumer.onMessageReceived(sessionId, message));
+ if (success) {
+ mSessionMap.get(sessionId).addMessageToHistory(message);
+ }
+ return success ? ErrorCode.OK : ErrorCode.TRANSIENT_ERROR;
+ }
}
/**
@@ -634,7 +685,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
*/
private boolean cleanupSessionResources(int sessionId) {
synchronized (mOpenSessionLock) {
- SessionInfo info = mSessionInfoMap.get(sessionId);
+ Session info = mSessionMap.get(sessionId);
if (info != null) {
if (!info.isRemoteInitiated()) {
mEndpointManager.returnSessionId(sessionId);
@@ -644,7 +695,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
mTransactionManager.onMessageDeliveryResponse(
sequenceNumber, /* success= */ false);
});
- mSessionInfoMap.remove(sessionId);
+ mSessionMap.remove(sessionId);
}
return info != null;
}
@@ -656,7 +707,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
*/
private boolean isSessionActive(int sessionId) {
synchronized (mOpenSessionLock) {
- return hasSessionId(sessionId) && mSessionInfoMap.get(sessionId).isActive();
+ return hasSessionId(sessionId) && mSessionMap.get(sessionId).isActive();
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 398894bf5273..cec5a93a2a15 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -817,7 +817,13 @@ public final class NotificationRecord {
}
if ((android.app.Flags.nmSummarizationUi() || android.app.Flags.nmSummarization())
&& signals.containsKey(KEY_SUMMARIZATION)) {
- mSummarization = signals.getString(KEY_SUMMARIZATION);
+ CharSequence summary = signals.getCharSequence(KEY_SUMMARIZATION,
+ signals.getString(KEY_SUMMARIZATION));
+ if (summary != null) {
+ mSummarization = summary.toString();
+ } else {
+ mSummarization = null;
+ }
EventLogTags.writeNotificationAdjusted(getKey(),
KEY_SUMMARIZATION, Boolean.toString(mSummarization != null));
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d3513053caf3..66e9e772e063 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1792,7 +1792,7 @@ public class ShortcutService extends IShortcutService.Stub {
void injectPostToHandlerDebounced(@NonNull final Object token, @NonNull final Runnable r) {
Objects.requireNonNull(token);
Objects.requireNonNull(r);
- synchronized (mServiceLock) {
+ synchronized (mHandler) {
mHandler.removeCallbacksAndMessages(token);
mHandler.postDelayed(r, token, CALLBACK_DELAY);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 22f20028eb9c..46dc75817a36 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3801,7 +3801,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return true;
}
}
- // fall through
+ break;
case KeyEvent.KEYCODE_ESCAPE:
if (firstDown && event.isMetaPressed()) {
notifyKeyGestureCompleted(event,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 340115a7d465..5d8f57866f7d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -43,6 +43,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ACTIVITY;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -107,6 +108,7 @@ import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -698,7 +700,7 @@ public class MockingOomAdjusterTests {
@SuppressWarnings("GuardedBy")
@Test
- @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
+ @EnableFlags({Flags.FLAG_USE_CPU_TIME_CAPABILITY, Flags.FLAG_PROTOTYPE_AGGRESSIVE_FREEZING})
public void testUpdateOomAdjFreezeState_bindingFromShortFgs() {
// Setting up a started short FGS within app1.
final ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
@@ -744,6 +746,44 @@ public class MockingOomAdjusterTests {
@SuppressWarnings("GuardedBy")
@Test
@EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
+ @DisableFlags(Flags.FLAG_PROTOTYPE_AGGRESSIVE_FREEZING)
+ public void testUpdateOomAdjFreezeState_bindingFromFgs() {
+ final ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ mProcessStateController.setHasForegroundServices(app.mServices, true,
+ FOREGROUND_SERVICE_TYPE_SPECIAL_USE, false);
+
+ final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ // App with a foreground service binds to app2
+ bindService(app2, app, null, null, 0, mock(IBinder.class));
+
+ setProcessesToLru(app, app2);
+ updateOomAdj(app);
+
+ assertCpuTime(app);
+ assertCpuTime(app2);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
+ @DisableFlags(Flags.FLAG_PROTOTYPE_AGGRESSIVE_FREEZING)
+ public void testUpdateOomAdjFreezeState_soloFgs() {
+ final ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ mProcessStateController.setHasForegroundServices(app.mServices, true,
+ FOREGROUND_SERVICE_TYPE_SPECIAL_USE, false);
+
+ setProcessesToLru(app);
+ updateOomAdj(app);
+
+ assertCpuTime(app);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
public void testUpdateOomAdjFreezeState_receivers() {
final ProcessRecord app = makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
index f7b16c808c50..dd089fcb1d70 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
@@ -201,11 +201,11 @@ public class AutoclickTypePanelTest {
public void moveToNextCorner_positionButton_rotatesThroughAllPositions() {
// Define all positions in sequence
int[][] expectedPositions = {
- {0, Gravity.END | Gravity.BOTTOM, /*x=*/ 15, /*y=*/ 90},
- {1, Gravity.START | Gravity.BOTTOM, /*x=*/ 15, /*y=*/ 90},
- {2, Gravity.START | Gravity.TOP, /*x=*/ 15, /*y=*/ 30},
- {3, Gravity.END | Gravity.TOP, /*x=*/ 15, /*y=*/ 30},
- {0, Gravity.END | Gravity.BOTTOM, /*x=*/ 15, /*y=*/ 90}
+ {CORNER_BOTTOM_RIGHT, Gravity.END | Gravity.BOTTOM, /*x=*/ 15, /*y=*/ 90},
+ {CORNER_BOTTOM_LEFT, Gravity.START | Gravity.BOTTOM, /*x=*/ 15, /*y=*/ 90},
+ {CORNER_TOP_LEFT, Gravity.START | Gravity.TOP, /*x=*/ 15, /*y=*/ 30},
+ {CORNER_TOP_RIGHT, Gravity.END | Gravity.TOP, /*x=*/ 15, /*y=*/ 30},
+ {CORNER_BOTTOM_RIGHT, Gravity.END | Gravity.BOTTOM, /*x=*/ 15, /*y=*/ 90}
};
// Check initial position
@@ -270,7 +270,7 @@ public class AutoclickTypePanelTest {
int screenWidth = mTestableContext.getResources().getDisplayMetrics().widthPixels;
// Verify initial corner is bottom-right.
- assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ assertThat(mAutoclickTypePanel.getCurrentCornerForTesting())
.isEqualTo(CORNER_BOTTOM_RIGHT);
dispatchDragSequence(contentView,
@@ -279,7 +279,7 @@ public class AutoclickTypePanelTest {
// Verify snapping to the right.
assertThat(params.gravity).isEqualTo(Gravity.END | Gravity.TOP);
- assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ assertThat(mAutoclickTypePanel.getCurrentCornerForTesting())
.isEqualTo(CORNER_TOP_RIGHT);
}
@@ -293,7 +293,7 @@ public class AutoclickTypePanelTest {
int screenWidth = mTestableContext.getResources().getDisplayMetrics().widthPixels;
// Verify initial corner is bottom-right.
- assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ assertThat(mAutoclickTypePanel.getCurrentCornerForTesting())
.isEqualTo(CORNER_BOTTOM_RIGHT);
dispatchDragSequence(contentView,
@@ -302,7 +302,7 @@ public class AutoclickTypePanelTest {
// Verify snapping to the left.
assertThat(params.gravity).isEqualTo(Gravity.START | Gravity.TOP);
- assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ assertThat(mAutoclickTypePanel.getCurrentCornerForTesting())
.isEqualTo(CORNER_BOTTOM_LEFT);
}
@@ -319,7 +319,7 @@ public class AutoclickTypePanelTest {
// Verify panel is positioned at default bottom-right corner.
WindowManager.LayoutParams params = panel.getLayoutParamsForTesting();
- assertThat(panel.getCurrentCornerIndexForTesting()).isEqualTo(CORNER_BOTTOM_RIGHT);
+ assertThat(panel.getCurrentCornerForTesting()).isEqualTo(CORNER_BOTTOM_RIGHT);
assertThat(params.gravity).isEqualTo(Gravity.END | Gravity.BOTTOM);
assertThat(params.x).isEqualTo(15); // Default edge margin.
assertThat(params.y).isEqualTo(90); // Default bottom offset.
@@ -353,7 +353,7 @@ public class AutoclickTypePanelTest {
assertThat(params.gravity).isEqualTo(Gravity.START | Gravity.TOP);
assertThat(params.x).isEqualTo(15);
assertThat(params.y).isEqualTo(30);
- assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting()).isEqualTo(
+ assertThat(mAutoclickTypePanel.getCurrentCornerForTesting()).isEqualTo(
CORNER_TOP_LEFT);
}
@@ -392,7 +392,7 @@ public class AutoclickTypePanelTest {
assertThat(params.gravity).isEqualTo(Gravity.START | Gravity.TOP);
assertThat(params.x).isEqualTo(15); // PANEL_EDGE_MARGIN
assertThat(params.y).isEqualTo(panelLocation[1] + 10);
- assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting()).isEqualTo(
+ assertThat(mAutoclickTypePanel.getCurrentCornerForTesting()).isEqualTo(
CORNER_BOTTOM_LEFT);
}
@@ -453,7 +453,7 @@ public class AutoclickTypePanelTest {
private void verifyPanelPosition(int[] expectedPosition) {
WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();
- assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting()).isEqualTo(
+ assertThat(mAutoclickTypePanel.getCurrentCornerForTesting()).isEqualTo(
expectedPosition[0]);
assertThat(params.gravity).isEqualTo(expectedPosition[1]);
assertThat(params.x).isEqualTo(expectedPosition[2]);
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
index 1de864cb4eb0..565a9b6c1c44 100644
--- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
@@ -17,6 +17,7 @@
package com.android.server.location.contexthub;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.timeout;
@@ -42,12 +43,10 @@ import android.os.Binder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
-import java.util.Collections;
-import java.util.List;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -57,6 +56,9 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Collections;
+import java.util.List;
+
@RunWith(AndroidJUnit4.class)
@Presubmit
public class ContextHubEndpointTest {
@@ -73,6 +75,12 @@ public class ContextHubEndpointTest {
private static final String TARGET_ENDPOINT_NAME = "Example target endpoint";
private static final int TARGET_ENDPOINT_ID = 1;
+ private static final int SAMPLE_MESSAGE_TYPE = 1234;
+ private static final HubMessage SAMPLE_MESSAGE =
+ new HubMessage.Builder(SAMPLE_MESSAGE_TYPE, new byte[] {1, 2, 3, 4, 5})
+ .setResponseRequired(true)
+ .build();
+
private ContextHubClientManager mClientManager;
private ContextHubEndpointManager mEndpointManager;
private HubInfoRegistry mHubInfoRegistry;
@@ -229,23 +237,34 @@ public class ContextHubEndpointTest {
assertThat(mTransactionManager.numReliableMessageTransactionPending()).isEqualTo(0);
}
+ @Test
+ public void testDuplicateMessageRejected() throws RemoteException {
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+ int sessionId = openTestSession(endpoint);
+
+ mEndpointManager.onMessageReceived(sessionId, SAMPLE_MESSAGE);
+ ArgumentCaptor<HubMessage> messageCaptor = ArgumentCaptor.forClass(HubMessage.class);
+ verify(mMockCallback).onMessageReceived(eq(sessionId), messageCaptor.capture());
+ assertThat(messageCaptor.getValue()).isEqualTo(SAMPLE_MESSAGE);
+
+ // Send a duplicate message and confirm it can be rejected
+ mEndpointManager.onMessageReceived(sessionId, SAMPLE_MESSAGE);
+ ArgumentCaptor<MessageDeliveryStatus> statusCaptor =
+ ArgumentCaptor.forClass(MessageDeliveryStatus.class);
+ verify(mMockEndpointCommunications)
+ .sendMessageDeliveryStatusToEndpoint(eq(sessionId), statusCaptor.capture());
+ assertThat(statusCaptor.getValue().messageSequenceNumber)
+ .isEqualTo(SAMPLE_MESSAGE.getMessageSequenceNumber());
+ assertThat(statusCaptor.getValue().errorCode).isEqualTo(ErrorCode.TRANSIENT_ERROR);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
/** A helper method to create a session and validates reliable message sending. */
private void testMessageTransactionInternal(
IContextHubEndpoint endpoint, boolean deliverMessageStatus) throws RemoteException {
- HubEndpointInfo targetInfo =
- new HubEndpointInfo(
- TARGET_ENDPOINT_NAME,
- TARGET_ENDPOINT_ID,
- ENDPOINT_PACKAGE_NAME,
- Collections.emptyList());
- int sessionId = endpoint.openSession(targetInfo, /* serviceDescriptor= */ null);
- mEndpointManager.onEndpointSessionOpenComplete(sessionId);
+ int sessionId = openTestSession(endpoint);
- final int messageType = 1234;
- HubMessage message =
- new HubMessage.Builder(messageType, new byte[] {1, 2, 3, 4, 5})
- .setResponseRequired(true)
- .build();
IContextHubTransactionCallback callback =
new IContextHubTransactionCallback.Stub() {
@Override
@@ -258,13 +277,13 @@ public class ContextHubEndpointTest {
Log.i(TAG, "Received onTransactionComplete callback, result=" + result);
}
};
- endpoint.sendMessage(sessionId, message, callback);
+ endpoint.sendMessage(sessionId, SAMPLE_MESSAGE, callback);
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(mMockEndpointCommunications, timeout(1000))
.sendMessageToEndpoint(eq(sessionId), messageCaptor.capture());
Message halMessage = messageCaptor.getValue();
- assertThat(halMessage.type).isEqualTo(message.getMessageType());
- assertThat(halMessage.content).isEqualTo(message.getMessageBody());
+ assertThat(halMessage.type).isEqualTo(SAMPLE_MESSAGE.getMessageType());
+ assertThat(halMessage.content).isEqualTo(SAMPLE_MESSAGE.getMessageBody());
assertThat(mTransactionManager.numReliableMessageTransactionPending()).isEqualTo(1);
if (deliverMessageStatus) {
@@ -308,4 +327,16 @@ public class ContextHubEndpointTest {
.isEqualTo(expectedInfo.getIdentifier().getHub());
assertThat(mEndpointManager.getNumRegisteredClients()).isEqualTo(0);
}
+
+ private int openTestSession(IContextHubEndpoint endpoint) throws RemoteException {
+ HubEndpointInfo targetInfo =
+ new HubEndpointInfo(
+ TARGET_ENDPOINT_NAME,
+ TARGET_ENDPOINT_ID,
+ ENDPOINT_PACKAGE_NAME,
+ Collections.emptyList());
+ int sessionId = endpoint.openSession(targetInfo, /* serviceDescriptor= */ null);
+ mEndpointManager.onEndpointSessionOpenComplete(sessionId);
+ return sessionId;
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 4391152220c0..e9cf036d0fb3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -1009,6 +1009,65 @@ public class NotificationRecordTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(Flags.FLAG_NM_SUMMARIZATION)
+ public void testSummarization_null() {
+ StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ assertThat(record.getSummarization()).isNull();
+
+ Bundle signals = new Bundle();
+ signals.putCharSequence(Adjustment.KEY_SUMMARIZATION, null);
+ record.addAdjustment(new Adjustment(mPkg, record.getKey(), signals, null, sbn.getUserId()));
+
+ record.applyAdjustments();
+
+ assertThat(record.getSummarization()).isNull();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_SUMMARIZATION)
+ public void testSummarization_charSequence() {
+ CharSequence summary = "hello";
+ StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ assertThat(record.getSummarization()).isNull();
+
+ Bundle signals = new Bundle();
+ signals.putCharSequence(Adjustment.KEY_SUMMARIZATION, summary);
+ record.addAdjustment(new Adjustment(mPkg, record.getKey(), signals, null, sbn.getUserId()));
+
+ record.applyAdjustments();
+
+ assertThat(record.getSummarization()).isEqualTo(summary.toString());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_SUMMARIZATION)
+ public void testSummarization_string() {
+ String summary = "hello";
+ StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ assertThat(record.getSummarization()).isNull();
+
+ Bundle signals = new Bundle();
+ signals.putCharSequence(Adjustment.KEY_SUMMARIZATION, summary);
+ record.addAdjustment(new Adjustment(mPkg, record.getKey(), signals, null, sbn.getUserId()));
+
+ record.applyAdjustments();
+
+ assertThat(record.getSummarization()).isEqualTo(summary);
+ }
+
+ @Test
public void testSensitiveContent() {
StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index 6d8a48799112..3ca019728c2b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -150,8 +150,6 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
{"Meta + Left arrow -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_DPAD_LEFT,
META_ON},
- {"Meta + Del -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DEL},
- KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_DEL, META_ON},
{"APP_SWITCH key -> Open App switcher", new int[]{KeyEvent.KEYCODE_APP_SWITCH},
KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
KeyEvent.KEYCODE_APP_SWITCH, 0},
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 531f51604507..9e57fd3c1a7b 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -30,6 +30,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
import com.android.internal.telecom.IVideoProvider;
import com.android.server.telecom.flags.Flags;
@@ -680,6 +681,7 @@ public final class Call {
private final @CallDirection int mCallDirection;
private final @Connection.VerificationStatus int mCallerNumberVerificationStatus;
private final Uri mContactPhotoUri;
+ private final UserHandle mAssociatedUser;
/**
* Whether the supplied capabilities supports the specified capability.
@@ -1081,6 +1083,16 @@ public final class Call {
return mCallerNumberVerificationStatus;
}
+ /**
+ * Gets the user that originated the call
+ * @return The user
+ *
+ * @hide
+ */
+ public UserHandle getAssociatedUser() {
+ return mAssociatedUser;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof Details) {
@@ -1107,7 +1119,8 @@ public final class Call {
Objects.equals(mCallDirection, d.mCallDirection) &&
Objects.equals(mCallerNumberVerificationStatus,
d.mCallerNumberVerificationStatus) &&
- Objects.equals(mContactPhotoUri, d.mContactPhotoUri);
+ Objects.equals(mContactPhotoUri, d.mContactPhotoUri) &&
+ Objects.equals(mAssociatedUser, d.mAssociatedUser);
}
return false;
}
@@ -1133,7 +1146,8 @@ public final class Call {
mContactDisplayName,
mCallDirection,
mCallerNumberVerificationStatus,
- mContactPhotoUri);
+ mContactPhotoUri,
+ mAssociatedUser);
}
/** {@hide} */
@@ -1158,7 +1172,8 @@ public final class Call {
String contactDisplayName,
int callDirection,
int callerNumberVerificationStatus,
- Uri contactPhotoUri) {
+ Uri contactPhotoUri,
+ UserHandle originatingUser) {
mState = state;
mTelecomCallId = telecomCallId;
mHandle = handle;
@@ -1180,6 +1195,7 @@ public final class Call {
mCallDirection = callDirection;
mCallerNumberVerificationStatus = callerNumberVerificationStatus;
mContactPhotoUri = contactPhotoUri;
+ mAssociatedUser = originatingUser;
}
/** {@hide} */
@@ -1205,7 +1221,8 @@ public final class Call {
parcelableCall.getContactDisplayName(),
parcelableCall.getCallDirection(),
parcelableCall.getCallerNumberVerificationStatus(),
- parcelableCall.getContactPhotoUri()
+ parcelableCall.getContactPhotoUri(),
+ parcelableCall.getAssociatedUser()
);
}
@@ -2631,7 +2648,8 @@ public final class Call {
mDetails.getContactDisplayName(),
mDetails.getCallDirection(),
mDetails.getCallerNumberVerificationStatus(),
- mDetails.getContactPhotoUri()
+ mDetails.getContactPhotoUri(),
+ mDetails.getAssociatedUser()
);
fireDetailsChanged(mDetails);
}
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 6a1318982e77..bd004e5e6231 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -16,14 +16,17 @@
package android.telecom;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.telecom.Call.Details.CallDirection;
import com.android.internal.telecom.IVideoProvider;
@@ -70,6 +73,7 @@ public final class ParcelableCall implements Parcelable {
private String mContactDisplayName;
private String mActiveChildCallId;
private Uri mContactPhotoUri;
+ private UserHandle mAssociatedUser;
public ParcelableCallBuilder setId(String id) {
mId = id;
@@ -230,6 +234,11 @@ public final class ParcelableCall implements Parcelable {
return this;
}
+ public ParcelableCallBuilder setAssociatedUser(UserHandle user) {
+ mAssociatedUser = user;
+ return this;
+ }
+
public ParcelableCall createParcelableCall() {
return new ParcelableCall(
mId,
@@ -262,7 +271,8 @@ public final class ParcelableCall implements Parcelable {
mCallerNumberVerificationStatus,
mContactDisplayName,
mActiveChildCallId,
- mContactPhotoUri);
+ mContactPhotoUri,
+ mAssociatedUser);
}
public static ParcelableCallBuilder fromParcelableCall(ParcelableCall parcelableCall) {
@@ -300,6 +310,7 @@ public final class ParcelableCall implements Parcelable {
newBuilder.mContactDisplayName = parcelableCall.mContactDisplayName;
newBuilder.mActiveChildCallId = parcelableCall.mActiveChildCallId;
newBuilder.mContactPhotoUri = parcelableCall.mContactPhotoUri;
+ newBuilder.mAssociatedUser = parcelableCall.mAssociatedUser;
return newBuilder;
}
}
@@ -336,6 +347,7 @@ public final class ParcelableCall implements Parcelable {
private final String mContactDisplayName;
private final String mActiveChildCallId; // Only valid for CDMA conferences
private final Uri mContactPhotoUri;
+ private final UserHandle mAssociatedUser;
public ParcelableCall(
String id,
@@ -368,7 +380,8 @@ public final class ParcelableCall implements Parcelable {
int callerNumberVerificationStatus,
String contactDisplayName,
String activeChildCallId,
- Uri contactPhotoUri
+ Uri contactPhotoUri,
+ UserHandle associatedUser
) {
mId = id;
mState = state;
@@ -401,6 +414,7 @@ public final class ParcelableCall implements Parcelable {
mContactDisplayName = contactDisplayName;
mActiveChildCallId = activeChildCallId;
mContactPhotoUri = contactPhotoUri;
+ mAssociatedUser = associatedUser;
}
/** The unique ID of the call. */
@@ -624,6 +638,13 @@ public final class ParcelableCall implements Parcelable {
return mContactPhotoUri;
}
+ /**
+ * @return the originating user
+ */
+ public @NonNull UserHandle getAssociatedUser() {
+ return mAssociatedUser;
+ }
+
/**
* @return On a CDMA conference with two participants, returns the ID of the child call that's
@@ -666,6 +687,9 @@ public final class ParcelableCall implements Parcelable {
source.readList(conferenceableCallIds, classLoader, java.lang.String.class);
Bundle intentExtras = source.readBundle(classLoader);
Bundle extras = source.readBundle(classLoader);
+ if (extras == null) {
+ extras = new Bundle();
+ }
int supportedAudioRoutes = source.readInt();
boolean isRttCallChanged = source.readByte() == 1;
ParcelableRttCall rttCall = source.readParcelable(classLoader, android.telecom.ParcelableRttCall.class);
@@ -675,6 +699,8 @@ public final class ParcelableCall implements Parcelable {
String contactDisplayName = source.readString();
String activeChildCallId = source.readString();
Uri contactPhotoUri = source.readParcelable(classLoader, Uri.class);
+ UserHandle associatedUser = source.readParcelable(classLoader, UserHandle.class);
+ extras.putParcelable(Intent.EXTRA_USER_HANDLE, associatedUser);
return new ParcelableCallBuilder()
.setId(id)
.setState(state)
@@ -707,6 +733,7 @@ public final class ParcelableCall implements Parcelable {
.setContactDisplayName(contactDisplayName)
.setActiveChildCallId(activeChildCallId)
.setContactPhotoUri(contactPhotoUri)
+ .setAssociatedUser(associatedUser)
.createParcelableCall();
}
@@ -757,6 +784,7 @@ public final class ParcelableCall implements Parcelable {
destination.writeString(mContactDisplayName);
destination.writeString(mActiveChildCallId);
destination.writeParcelable(mContactPhotoUri, 0);
+ destination.writeParcelable(mAssociatedUser, 0);
}
@Override
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index c666fb7e05f1..2799f6c79215 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -382,14 +382,6 @@ class KeyGestureControllerTests {
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "META + DEL -> Back",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_DEL),
- KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
- intArrayOf(KeyEvent.KEYCODE_DEL),
- KeyEvent.META_META_ON,
- intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
- ),
- TestData(
"META + ESC -> Back",
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ESCAPE),
KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
diff --git a/tools/fonts/Android.bp b/tools/fonts/Android.bp
index f8629f9bd0b8..07caa9a979d9 100644
--- a/tools/fonts/Android.bp
+++ b/tools/fonts/Android.bp
@@ -23,11 +23,6 @@ package {
python_defaults {
name: "fonts_python_defaults",
- version: {
- py3: {
- embedded_launcher: true,
- },
- },
}
python_binary_host {
diff --git a/tools/lint/fix/Android.bp b/tools/lint/fix/Android.bp
index ddacf57c3a3e..43f21221ae5a 100644
--- a/tools/lint/fix/Android.bp
+++ b/tools/lint/fix/Android.bp
@@ -25,11 +25,6 @@ python_binary_host {
name: "lint_fix",
main: "soong_lint_fix.py",
srcs: ["soong_lint_fix.py"],
- version: {
- py3: {
- embedded_launcher: true,
- },
- },
}
python_library_host {
diff --git a/tools/lint/global/integration_tests/Android.bp b/tools/lint/global/integration_tests/Android.bp
index 05ba405d2c52..f88709375c98 100644
--- a/tools/lint/global/integration_tests/Android.bp
+++ b/tools/lint/global/integration_tests/Android.bp
@@ -65,9 +65,4 @@ python_test_host {
"AndroidGlobalLintTestNoAidl_py",
"AndroidGlobalLintTestMissingAnnotation_py",
],
- version: {
- py3: {
- embedded_launcher: true,
- },
- },
}