Merge "Fix large clock flickers when animated in Felix" into udc-qpr-dev
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
index f92c297..dca818e 100644
--- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
@@ -17,10 +17,14 @@
package android.surfaceflinger;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.LargeTest;
@@ -194,4 +198,16 @@
mTransaction.apply(true);
}
}
+
+ @Test
+ public void bufferQueue() throws Exception {
+ SurfaceView testSV = mActivity.mTestSurfaceView;
+ SurfaceHolder holder = testSV.getHolder();
+ holder.getSurface();
+ for (int i = 0; i < sProfilingIterations; i++) {
+ Canvas canvas = holder.lockCanvas();
+ holder.unlockCanvasAndPost(canvas);
+ mTransaction.apply(true);
+ }
+ }
}
diff --git a/core/java/android/animation/AnimationHandler.java b/core/java/android/animation/AnimationHandler.java
index df8a50a..4fc90ae 100644
--- a/core/java/android/animation/AnimationHandler.java
+++ b/core/java/android/animation/AnimationHandler.java
@@ -16,6 +16,7 @@
package android.animation;
+import android.annotation.Nullable;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.ArrayMap;
@@ -91,9 +92,13 @@
};
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
+ private static AnimationHandler sTestHandler = null;
private boolean mListDirty = false;
public static AnimationHandler getInstance() {
+ if (sTestHandler != null) {
+ return sTestHandler;
+ }
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
@@ -101,6 +106,17 @@
}
/**
+ * Sets an instance that will be returned by {@link #getInstance()} on every thread.
+ * @return the previously active test handler, if any.
+ * @hide
+ */
+ public static @Nullable AnimationHandler setTestHandler(@Nullable AnimationHandler handler) {
+ AnimationHandler oldHandler = sTestHandler;
+ sTestHandler = handler;
+ return oldHandler;
+ }
+
+ /**
* System property that controls the behavior of pausing infinite animators when an app
* is moved to the background.
*
@@ -369,7 +385,10 @@
* Return the number of callbacks that have registered for frame callbacks.
*/
public static int getAnimationCount() {
- AnimationHandler handler = sAnimatorHandler.get();
+ AnimationHandler handler = sTestHandler;
+ if (handler == null) {
+ handler = sAnimatorHandler.get();
+ }
if (handler == null) {
return 0;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 88c7250..e8366b0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2107,6 +2107,21 @@
"android.settings.MANAGE_MORE_DEFAULT_APPS_SETTINGS";
/**
+ * Activity Action: Show app screen size list settings for user to override app aspect
+ * ratio.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Can include the following extra {@link android.content.Intent#EXTRA_PACKAGE_NAME} specifying
+ * the name of the package to scroll to in the page.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_USER_ASPECT_RATIO_SETTINGS =
+ "android.settings.MANAGE_USER_ASPECT_RATIO_SETTINGS";
+
+ /**
* Activity Action: Show notification settings.
*
* @hide
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 0c6d6f9..965277c 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -196,7 +196,8 @@
public static final int PROFILEABLE = 1 << 24;
/**
- * Enable ptrace. This is enabled on eng or userdebug builds, or if the app is debuggable.
+ * Enable ptrace. This is enabled on eng, if the app is debuggable, or if
+ * the persist.debug.ptrace.enabled property is set.
*/
public static final int DEBUG_ENABLE_PTRACE = 1 << 25;
@@ -1020,20 +1021,35 @@
"persist.debug.dalvik.vm.jdwp.enabled").equals("1");
/**
+ * This will enable ptrace by default for all apps. It is OK to cache this property
+ * because we expect to reboot the system whenever this property changes
+ */
+ private static final boolean ENABLE_PTRACE = SystemProperties.get(
+ "persist.debug.ptrace.enabled").equals("1");
+
+ /**
* Applies debugger system properties to the zygote arguments.
*
- * For eng builds all apps are debuggable. On userdebug and user builds
- * if persist.debug.dalvik.vm.jdwp.enabled is 1 all apps are
- * debuggable. Otherwise, the debugger state is specified via the
- * "--enable-jdwp" flag in the spawn request.
+ * For eng builds all apps are debuggable with JDWP and ptrace.
+ *
+ * On userdebug builds if persist.debug.dalvik.vm.jdwp.enabled
+ * is 1 all apps are debuggable with JDWP and ptrace. Otherwise, the
+ * debugger state is specified via the "--enable-jdwp" flag in the
+ * spawn request.
+ *
+ * On userdebug builds if persist.debug.ptrace.enabled is 1 all
+ * apps are debuggable with ptrace.
*
* @param args non-null; zygote spawner args
*/
static void applyDebuggerSystemProperty(ZygoteArguments args) {
- if (Build.IS_ENG || ENABLE_JDWP) {
+ if (Build.IS_ENG || (Build.IS_USERDEBUG && ENABLE_JDWP)) {
args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
+ // Also enable ptrace when JDWP is enabled for consistency with
+ // before persist.debug.ptrace.enabled existed.
+ args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_PTRACE;
}
- if (RoSystemProperties.DEBUGGABLE) {
+ if (Build.IS_ENG || (Build.IS_USERDEBUG && ENABLE_PTRACE)) {
args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_PTRACE;
}
}
@@ -1057,7 +1073,8 @@
int peerUid = peer.getUid();
if (args.mInvokeWith != null && peerUid != 0
- && (args.mRuntimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
+ && (args.mRuntimeFlags
+ & (Zygote.DEBUG_ENABLE_JDWP | Zygote.DEBUG_ENABLE_PTRACE)) == 0) {
throw new ZygoteSecurityException("Peer is permitted to specify an "
+ "explicit invoke-with wrapper command only for debuggable "
+ "applications.");
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index a6e74d0..68c0693 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -23,12 +23,12 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -254,18 +254,15 @@
callbackInfo1.getCallback().onBackStarted(mBackEvent);
waitForIdle();
- verify(mCallback1).onBackStarted(any(BackEvent.class));
- verifyZeroInteractions(mCallback2);
+ verify(mCallback1, times(1)).onBackStarted(any(BackEvent.class));
+ verify(mCallback2, never()).onBackStarted(any(BackEvent.class));
+ clearInvocations(mCallback1);
callbackInfo2.getCallback().onBackStarted(mBackEvent);
waitForIdle();
- verify(mCallback2).onBackStarted(any(BackEvent.class));
-
- // Calls sequence: BackProgressAnimator.onBackStarted() -> BackProgressAnimator.reset() ->
- // Spring.animateToFinalPosition(0). This causes a progress event to be fired.
- verify(mCallback1, atMost(1)).onBackProgressed(any(BackEvent.class));
- verifyNoMoreInteractions(mCallback1);
+ verify(mCallback1, never()).onBackStarted(any(BackEvent.class));
+ verify(mCallback2, times(1)).onBackStarted(any(BackEvent.class));
}
@Test
diff --git a/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml
new file mode 100644
index 0000000..02b7075
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M180,840Q156,840 138,822Q120,804 120,780L120,180Q120,156 138,138Q156,120 180,120L780,120Q804,120 822,138Q840,156 840,180L840,780Q840,804 822,822Q804,840 780,840L180,840ZM180,780L780,780Q780,780 780,780Q780,780 780,780L780,277L180,277L180,780Q180,780 180,780Q180,780 180,780Z" />
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index fb1980a..7e0c207 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -78,6 +78,19 @@
android:layout_weight="1"/>
<ImageButton
+ android:id="@+id/maximize_window"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:padding="9dp"
+ android:layout_marginEnd="8dp"
+ android:contentDescription="@string/maximize_button_text"
+ android:src="@drawable/decor_desktop_mode_maximize_button_dark"
+ android:scaleType="fitCenter"
+ android:gravity="end"
+ android:background="@null"
+ android:tint="@color/desktop_mode_caption_maximize_button_dark"/>
+
+ <ImageButton
android:id="@+id/close_window"
android:layout_width="40dp"
android:layout_height="40dp"
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index f2a0785..b2ec98b 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -64,6 +64,8 @@
<color name="desktop_mode_caption_expand_button_dark">#48473A</color>
<color name="desktop_mode_caption_close_button_light">#EFF1F2</color>
<color name="desktop_mode_caption_close_button_dark">#1C1C17</color>
+ <color name="desktop_mode_caption_maximize_button_light">#EFF1F2</color>
+ <color name="desktop_mode_caption_maximize_button_dark">#1C1C17</color>
<color name="desktop_mode_caption_app_name_light">#EFF1F2</color>
<color name="desktop_mode_caption_app_name_dark">#1C1C17</color>
<color name="desktop_mode_caption_menu_text_color">#191C1D</color>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index d972f48..16c3960 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.dagger.pip;
-import android.annotation.Nullable;
import android.content.Context;
import android.os.Handler;
@@ -229,7 +228,6 @@
@WMSingleton
@Provides
- @Nullable
static PipTransition providePipTransition(Context context,
ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
index 2ded4a3..04032bb1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
@@ -36,10 +36,13 @@
public abstract class PipModule {
@WMSingleton
@Provides
- @Nullable
static PipTransitionController providePipTransitionController(
com.android.wm.shell.pip.PipTransition legacyPipTransition,
- com.android.wm.shell.pip2.PipTransition newPipTransition) {
- return PipUtils.isPip2ExperimentEnabled() ? newPipTransition : legacyPipTransition;
+ @Nullable com.android.wm.shell.pip2.PipTransition newPipTransition) {
+ if (PipUtils.isPip2ExperimentEnabled() && newPipTransition != null) {
+ return newPipTransition;
+ } else {
+ return legacyPipTransition;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 39b6675..88a81fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -604,7 +604,8 @@
} else if (change.getMode() == TRANSIT_CHANGE) {
// Finish recents animation if the display is changed, so the default
// transition handler can play the animation such as rotation effect.
- if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) {
+ if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)
+ && info.getType() == TRANSIT_CHANGE) {
// This call to cancel will use the screenshots taken preemptively in
// handleMidTransitionRequest() prior to the display changing
cancel(mWillFinishToHome, true /* withScreenshots */, "display change");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 4cc755b..2b19da2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -374,6 +374,11 @@
mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId));
decoration.closeHandleMenu();
}
+ } else if (id == R.id.maximize_window) {
+ final RunningTaskInfo taskInfo = decoration.mTaskInfo;
+ mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(
+ taskInfo, decoration));
+ decoration.closeHandleMenu();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index b67acd5..672e57a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -28,6 +28,7 @@
private val openMenuButton: View = rootView.findViewById(R.id.open_menu_button)
private val closeWindowButton: ImageButton = rootView.findViewById(R.id.close_window)
private val expandMenuButton: ImageButton = rootView.findViewById(R.id.expand_menu_button)
+ private val maximizeWindowButton: ImageButton = rootView.findViewById(R.id.maximize_window)
private val appNameTextView: TextView = rootView.findViewById(R.id.application_name)
private val appIconImageView: ImageView = rootView.findViewById(R.id.application_icon)
@@ -37,6 +38,7 @@
openMenuButton.setOnClickListener(onCaptionButtonClickListener)
openMenuButton.setOnTouchListener(onCaptionTouchListener)
closeWindowButton.setOnClickListener(onCaptionButtonClickListener)
+ maximizeWindowButton.setOnClickListener(onCaptionButtonClickListener)
closeWindowButton.setOnTouchListener(onCaptionTouchListener)
appNameTextView.text = appName
appIconImageView.setImageDrawable(appIcon)
@@ -49,6 +51,8 @@
closeWindowButton.imageTintList = ColorStateList.valueOf(
getCaptionCloseButtonColor(taskInfo))
+ maximizeWindowButton.imageTintList = ColorStateList.valueOf(
+ getCaptionMaximizeButtonColor(taskInfo))
expandMenuButton.imageTintList = ColorStateList.valueOf(
getCaptionExpandButtonColor(taskInfo))
appNameTextView.setTextColor(getCaptionAppNameTextColor(taskInfo))
@@ -70,6 +74,14 @@
}
}
+ private fun getCaptionMaximizeButtonColor(taskInfo: RunningTaskInfo): Int {
+ return if (shouldUseLightCaptionColors(taskInfo)) {
+ context.getColor(R.color.desktop_mode_caption_maximize_button_light)
+ } else {
+ context.getColor(R.color.desktop_mode_caption_maximize_button_dark)
+ }
+ }
+
private fun getCaptionExpandButtonColor(taskInfo: RunningTaskInfo): Int {
return if (shouldUseLightCaptionColors(taskInfo)) {
context.getColor(R.color.desktop_mode_caption_expand_button_light)
diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java
index 4c0850b..4ad3cd1 100644
--- a/media/java/android/media/AudioFocusRequest.java
+++ b/media/java/android/media/AudioFocusRequest.java
@@ -39,8 +39,8 @@
* but there is only one the user would really listen to (focus on), while the other plays in
* the background. An example of this is driving directions being spoken while music plays at
* a reduced volume (a.k.a. ducking).
- * <p>When an application requests audio focus, it expresses its intention to “own” audio focus to
- * play audio. Let’s review the different types of focus requests, the return value after a request,
+ * <p>When an application requests audio focus, it expresses its intention to "own" audio focus to
+ * play audio. Let's review the different types of focus requests, the return value after a request,
* and the responses to a loss.
* <p class="note">Note: applications should not play anything until granted focus.</p>
*
@@ -51,7 +51,7 @@
* <li>{@link AudioManager#AUDIOFOCUS_GAIN} expresses the fact that your application is now the
* sole source of audio that the user is listening to. The duration of the audio playback is
* unknown, and is possibly very long: after the user finishes interacting with your application,
- * (s)he doesn’t expect another audio stream to resume. Examples of uses of this focus gain are
+ * (s)he doesn't expect another audio stream to resume. Examples of uses of this focus gain are
* for music playback, for a game or a video player.</li>
*
* <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT} is for a situation when you know your
@@ -60,20 +60,20 @@
* for playing an alarm, or during a VoIP call. The playback is known to be finite: the alarm will
* time-out or be dismissed, the VoIP call has a beginning and an end. When any of those events
* ends, and if the user was listening to music when it started, the user expects music to resume,
- * but didn’t wish to listen to both at the same time.</li>
+ * but didn't wish to listen to both at the same time.</li>
*
* <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}: this focus request type is similar
* to {@code AUDIOFOCUS_GAIN_TRANSIENT} for the temporary aspect of the focus request, but it also
* expresses the fact during the time you own focus, you allow another application to keep playing
- * at a reduced volume, “ducked”. Examples are when playing driving directions or notifications,
- * it’s ok for music to keep playing, but not loud enough that it would prevent the directions to
- * be hard to understand. A typical attenuation by the “ducked” application is a factor of 0.2f
+ * at a reduced volume, "ducked". Examples are when playing driving directions or notifications,
+ * it's ok for music to keep playing, but not loud enough that it would prevent the directions to
+ * be hard to understand. A typical attenuation by the "ducked" application is a factor of 0.2f
* (or -14dB), that can for instance be applied with {@code MediaPlayer.setVolume(0.2f)} when
* using this class for playback.</li>
*
* <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} is also for a temporary request,
* but also expresses that your application expects the device to not play anything else. This is
- * typically used if you are doing audio recording or speech recognition, and don’t want for
+ * typically used if you are doing audio recording or speech recognition, and don't want for
* examples notifications to be played by the system during that time.</li>
* </ul>
*
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 4323c73..1ee5aa3 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1057,7 +1057,7 @@
* this API to pass the cookies as a list of HttpCookie. If the app has not installed
* a CookieHandler already, this API creates a CookieManager and populates its CookieStore with
* the provided cookies. If the app has installed its own handler already, this API requires the
- * handler to be of CookieManager type such that the API can update the manager’s CookieStore.
+ * handler to be of CookieManager type such that the API can update the manager's CookieStore.
*
* <p><strong>Note</strong> that the cross domain redirection is allowed by default,
* but that can be changed with key/value pairs through the headers parameter with
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 229b7a7..15446b6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -279,9 +279,16 @@
}
private boolean isCallerSessionOwner(int originatingUid, int sessionId) {
+ if (originatingUid == Process.ROOT_UID) {
+ return true;
+ }
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
- int installerUid = packageInstaller.getSessionInfo(sessionId).getInstallerUid();
- return (originatingUid == Process.ROOT_UID) || (originatingUid == installerUid);
+ PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
+ if (sessionInfo == null) {
+ return false;
+ }
+ int installerUid = sessionInfo.getInstallerUid();
+ return originatingUid == installerUid;
}
private void checkDevicePolicyRestriction() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index a05a6e9..69b61c7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -25,10 +25,13 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudioContentMetadata;
import android.bluetooth.BluetoothLeBroadcast;
+import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothLeBroadcastSubgroup;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.bluetooth.BluetoothStatusCodes;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -39,6 +42,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import androidx.annotation.RequiresApi;
@@ -76,14 +80,16 @@
Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME),
};
- private BluetoothLeBroadcast mService;
+ private BluetoothLeBroadcast mServiceBroadcast;
+ private BluetoothLeBroadcastAssistant mServiceBroadcastAssistant;
private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata;
private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
private BluetoothLeAudioContentMetadata.Builder mBuilder;
private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
private String mAppSourceName = "";
private String mNewAppSourceName = "";
- private boolean mIsProfileReady;
+ private boolean mIsBroadcastProfileReady = false;
+ private boolean mIsBroadcastAssistantProfileReady = false;
private String mProgramInfo;
private byte[] mBroadcastCode;
private Executor mExecutor;
@@ -94,17 +100,22 @@
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (DEBUG) {
- Log.d(TAG, "Bluetooth service connected");
+ Log.d(TAG, "Bluetooth service connected: " + profile);
}
- if(!mIsProfileReady) {
- mService = (BluetoothLeBroadcast) proxy;
- mIsProfileReady = true;
+ if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && !mIsBroadcastProfileReady) {
+ mServiceBroadcast = (BluetoothLeBroadcast) proxy;
+ mIsBroadcastProfileReady = true;
registerServiceCallBack(mExecutor, mBroadcastCallback);
List<BluetoothLeBroadcastMetadata> metadata = getAllBroadcastMetadata();
if (!metadata.isEmpty()) {
updateBroadcastInfoFromBroadcastMetadata(metadata.get(0));
}
registerContentObserver();
+ } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
+ && !mIsBroadcastAssistantProfileReady) {
+ mIsBroadcastAssistantProfileReady = true;
+ mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy;
+ registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback);
}
}
@@ -113,9 +124,17 @@
if (DEBUG) {
Log.d(TAG, "Bluetooth service disconnected");
}
- if(mIsProfileReady) {
- mIsProfileReady = false;
+ if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && mIsBroadcastProfileReady) {
+ mIsBroadcastProfileReady = false;
unregisterServiceCallBack(mBroadcastCallback);
+ }
+ if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
+ && mIsBroadcastAssistantProfileReady) {
+ mIsBroadcastAssistantProfileReady = false;
+ unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback);
+ }
+
+ if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) {
unregisterContentObserver();
}
}
@@ -157,6 +176,8 @@
"onBroadcastStopped(), reason = " + reason + ", broadcastId = "
+ broadcastId);
}
+
+ stopLocalSourceReceivers();
resetCacheInfo();
}
@@ -196,6 +217,61 @@
}
};
+ private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
+ new BluetoothLeBroadcastAssistant.Callback() {
+ @Override
+ public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId,
+ int reason) {}
+ @Override
+ public void onSearchStarted(int reason) {}
+
+ @Override
+ public void onSearchStartFailed(int reason) {}
+
+ @Override
+ public void onSearchStopped(int reason) {}
+
+ @Override
+ public void onSearchStopFailed(int reason) {}
+
+ @Override
+ public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {}
+
+ @Override
+ public void onSourceAddFailed(@NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastMetadata source, int reason) {}
+
+ @Override
+ public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId,
+ int reason) {}
+
+ @Override
+ public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId,
+ int reason) {}
+
+ @Override
+ public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId,
+ int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onSourceRemoved(), sink = " + sink + ", reason = "
+ + reason + ", sourceId = " + sourceId);
+ }
+ }
+
+ @Override
+ public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId,
+ int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onSourceRemoveFailed(), sink = " + sink + ", reason = "
+ + reason + ", sourceId = " + sourceId);
+ }
+ }
+
+ @Override
+ public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId,
+ @NonNull BluetoothLeBroadcastReceiveState state) {}
+ };
+
private class BroadcastSettingsObserver extends ContentObserver {
BroadcastSettingsObserver(Handler h) {
super(h);
@@ -219,6 +295,9 @@
// Before registering callback, the constructor should finish creating the all of variables.
BluetoothAdapter.getDefaultAdapter()
.getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST);
+ BluetoothAdapter.getDefaultAdapter()
+ .getProfileProxy(context, mServiceListener,
+ BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
}
/**
@@ -227,7 +306,7 @@
*/
public void startBroadcast(String appSourceName, String language) {
mNewAppSourceName = appSourceName;
- if (mService == null) {
+ if (mServiceBroadcast == null) {
Log.d(TAG, "The BluetoothLeBroadcast is null when starting the broadcast.");
return;
}
@@ -237,7 +316,7 @@
"startBroadcast: language = " + language + " ,programInfo = " + programInfo);
}
buildContentMetadata(language, programInfo);
- mService.startBroadcast(mBluetoothLeAudioContentMetadata,
+ mServiceBroadcast.startBroadcast(mBluetoothLeAudioContentMetadata,
(mBroadcastCode != null && mBroadcastCode.length > 0) ? mBroadcastCode : null);
}
@@ -341,13 +420,13 @@
}
public BluetoothLeBroadcastMetadata getLatestBluetoothLeBroadcastMetadata() {
- if (mService == null) {
+ if (mServiceBroadcast == null) {
Log.d(TAG, "The BluetoothLeBroadcast is null");
return null;
}
if (mBluetoothLeBroadcastMetadata == null) {
final List<BluetoothLeBroadcastMetadata> metadataList =
- mService.getAllBroadcastMetadata();
+ mServiceBroadcast.getAllBroadcastMetadata();
mBluetoothLeBroadcastMetadata = metadataList.stream()
.filter(i -> i.getBroadcastId() == mBroadcastId)
.findFirst()
@@ -411,14 +490,14 @@
* corresponding callback {@link BluetoothLeBroadcast.Callback}.
*/
public void stopBroadcast(int broadcastId) {
- if (mService == null) {
+ if (mServiceBroadcast == null) {
Log.d(TAG, "The BluetoothLeBroadcast is null when stopping the broadcast.");
return;
}
if (DEBUG) {
Log.d(TAG, "stopBroadcast()");
}
- mService.stopBroadcast(broadcastId);
+ mServiceBroadcast.stopBroadcast(broadcastId);
}
/**
@@ -426,7 +505,7 @@
* corresponding callback {@link BluetoothLeBroadcast.Callback}.
*/
public void updateBroadcast(String appSourceName, String language) {
- if (mService == null) {
+ if (mServiceBroadcast == null) {
Log.d(TAG, "The BluetoothLeBroadcast is null when updating the broadcast.");
return;
}
@@ -437,26 +516,57 @@
}
mNewAppSourceName = appSourceName;
mBluetoothLeAudioContentMetadata = mBuilder.setProgramInfo(programInfo).build();
- mService.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata);
+ mServiceBroadcast.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata);
}
public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor,
@NonNull BluetoothLeBroadcast.Callback callback) {
- if (mService == null) {
+ if (mServiceBroadcast == null) {
Log.d(TAG, "The BluetoothLeBroadcast is null.");
return;
}
- mService.registerCallback(executor, callback);
+ mServiceBroadcast.registerCallback(executor, callback);
+ }
+
+ /**
+ * Register Broadcast Assistant Callbacks to track it's state and receivers
+ *
+ * @param executor Executor object for callback
+ * @param callback Callback object to be registered
+ */
+ public void registerBroadcastAssistantCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
+ if (mServiceBroadcastAssistant == null) {
+ Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null.");
+ return;
+ }
+
+ mServiceBroadcastAssistant.registerCallback(executor, callback);
}
public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) {
- if (mService == null) {
+ if (mServiceBroadcast == null) {
Log.d(TAG, "The BluetoothLeBroadcast is null.");
return;
}
- mService.unregisterCallback(callback);
+ mServiceBroadcast.unregisterCallback(callback);
+ }
+
+ /**
+ * Unregister previousely registered Broadcast Assistant Callbacks
+ *
+ * @param callback Callback object to be unregistered
+ */
+ public void unregisterBroadcastAssistantCallback(
+ @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
+ if (mServiceBroadcastAssistant == null) {
+ Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null.");
+ return;
+ }
+
+ mServiceBroadcastAssistant.unregisterCallback(callback);
}
private void buildContentMetadata(String language, String programInfo) {
@@ -474,7 +584,7 @@
}
public boolean isProfileReady() {
- return mIsProfileReady;
+ return mIsBroadcastProfileReady;
}
@Override
@@ -494,40 +604,40 @@
* Not supported since LE Audio Broadcasts do not establish a connection.
*/
public int getConnectionStatus(BluetoothDevice device) {
- if (mService == null) {
+ if (mServiceBroadcast == null) {
return BluetoothProfile.STATE_DISCONNECTED;
}
// LE Audio Broadcasts are not connection-oriented.
- return mService.getConnectionState(device);
+ return mServiceBroadcast.getConnectionState(device);
}
/**
* Not supported since LE Audio Broadcasts do not establish a connection.
*/
public List<BluetoothDevice> getConnectedDevices() {
- if (mService == null) {
+ if (mServiceBroadcast == null) {
return new ArrayList<BluetoothDevice>(0);
}
// LE Audio Broadcasts are not connection-oriented.
- return mService.getConnectedDevices();
+ return mServiceBroadcast.getConnectedDevices();
}
public @NonNull
List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
- if (mService == null) {
+ if (mServiceBroadcast == null) {
Log.d(TAG, "The BluetoothLeBroadcast is null.");
return Collections.emptyList();
}
- return mService.getAllBroadcastMetadata();
+ return mServiceBroadcast.getAllBroadcastMetadata();
}
public boolean isEnabled(BluetoothDevice device) {
- if (mService == null) {
+ if (mServiceBroadcast == null) {
return false;
}
- return !mService.getAllBroadcastMetadata().isEmpty();
+ return !mServiceBroadcast.getAllBroadcastMetadata().isEmpty();
}
/**
@@ -571,12 +681,12 @@
if (DEBUG) {
Log.d(TAG, "finalize()");
}
- if (mService != null) {
+ if (mServiceBroadcast != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
BluetoothProfile.LE_AUDIO_BROADCAST,
- mService);
- mService = null;
+ mServiceBroadcast);
+ mServiceBroadcast = null;
} catch (Throwable t) {
Log.w(TAG, "Error cleaning up LeAudio proxy", t);
}
@@ -626,4 +736,21 @@
}
mContentResolver.unregisterContentObserver(mSettingsObserver);
}
+
+ private void stopLocalSourceReceivers() {
+ if (DEBUG) {
+ Log.d(TAG, "stopLocalSourceReceivers()");
+ }
+ for (BluetoothDevice device : mServiceBroadcastAssistant.getConnectedDevices()) {
+ for (BluetoothLeBroadcastReceiveState receiveState :
+ mServiceBroadcastAssistant.getAllSources(device)) {
+ /* Check if local/last broadcast is the synced one */
+ int localBroadcastId = getLatestBroadcastId();
+ if (receiveState.getBroadcastId() != localBroadcastId) continue;
+
+ mServiceBroadcastAssistant.removeSource(device, receiveState.getSourceId());
+ }
+ }
+ }
+
}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index dca9e21..01b47ea 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -407,6 +407,7 @@
static_libs: [
"SystemUI-tests-base",
"androidx.test.uiautomator_uiautomator",
+ "androidx.core_core-animation-testing",
"mockito-target-extended-minus-junit4",
"androidx.test.ext.junit",
"androidx.test.ext.truth",
@@ -476,6 +477,7 @@
],
static_libs: [
"androidx.test.uiautomator_uiautomator",
+ "androidx.core_core-animation-testing",
"androidx.test.ext.junit",
"inline-mockito-robolectric-prebuilt",
],
diff --git a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
index 8f1323d..a1e2dc3 100644
--- a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
+++ b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
@@ -54,7 +54,7 @@
</FrameLayout>
<ImageView
android:id="@+id/mobile_type"
- android:layout_height="@dimen/status_bar_mobile_signal_size"
+ android:layout_height="@dimen/status_bar_mobile_type_size"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:adjustViewBounds="true"
@@ -74,13 +74,15 @@
<com.android.systemui.statusbar.AnimatedImageView
android:id="@+id/mobile_signal"
android:layout_height="@dimen/status_bar_mobile_signal_size"
- android:layout_width="@dimen/status_bar_mobile_signal_size"
+ android:layout_width="wrap_content"
+ android:adjustViewBounds="true"
systemui:hasOverlappingRendering="false"
/>
<ImageView
android:id="@+id/mobile_roaming"
- android:layout_width="@dimen/status_bar_mobile_signal_size"
- android:layout_height="@dimen/status_bar_mobile_signal_size"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/status_bar_mobile_roam_size"
+ android:adjustViewBounds="true"
android:layout_gravity="top|start"
android:src="@drawable/stat_sys_roaming"
android:contentDescription="@string/data_connection_roaming"
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index badad58..28b5870 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -192,7 +192,7 @@
<string name="kg_prompt_after_user_lockdown_pattern">Pattern is required after lockdown</string>
<!-- Message shown to prepare for an unattended update (OTA). Also known as an over-the-air (OTA) update. [CHAR LIMIT=70] -->
- <string name="kg_prompt_unattended_update">Update will install during inactive hours</string>
+ <string name="kg_prompt_unattended_update">Update will install when device not in use</string>
<!-- Message shown when primary authentication hasn't been used for some time. [CHAR LIMIT=70] -->
<string name="kg_prompt_pin_auth_timeout">Added security required. PIN not used for a while.</string>
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index f3a6bbe..12f13e9 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -133,7 +133,8 @@
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
android:layout_height="wrap_content"
android:paddingEnd="@dimen/signal_cluster_battery_padding" />
diff --git a/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
index 65ee8b3..636f479 100644
--- a/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
+++ b/packages/SystemUI/res/layout/keyguard_settings_popup_menu.xml
@@ -25,13 +25,13 @@
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@drawable/keyguard_settings_popup_menu_background"
- android:padding="12dp">
+ android:padding="@dimen/keyguard_settings_popup_menu_padding">
<ImageView
android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginEnd="8dp"
+ android:layout_width="@dimen/keyguard_settings_popup_menu_icon_height"
+ android:layout_height="@dimen/keyguard_settings_popup_menu_icon_width"
+ android:layout_marginEnd="@dimen/keyguard_settings_popup_menu_icon_end_margin"
android:tint="?androidprv:attr/materialColorOnSecondaryFixed"
android:importantForAccessibility="no"
tools:ignore="UseAppTint" />
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 0ab921f..5132e57 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -67,6 +67,7 @@
android:id="@+id/status_bar_start_side_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|start"
android:clipChildren="false">
<include layout="@layout/heads_up_status_bar_layout" />
@@ -88,7 +89,8 @@
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/status_bar_system_icons_height"
+ android:layout_gravity="center_vertical"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:singleLine="true"
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
@@ -146,7 +148,10 @@
android:layout_marginEnd="@dimen/status_bar_user_chip_end_margin"
layout="@layout/status_bar_user_chip_container" />
- <include layout="@layout/system_icons" />
+ <include layout="@layout/system_icons"
+ android:layout_gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/status_bar_system_icons_height" />
</com.android.keyguard.AlphaOptimizedLinearLayout>
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9c864ab..b6ef594 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -681,7 +681,6 @@
<item>6</item> <!-- WAKE_REASON_WAKE_KEY -->
<item>7</item> <!-- WAKE_REASON_WAKE_MOTION -->
<item>9</item> <!-- WAKE_REASON_LID -->
- <item>10</item> <!-- WAKE_REASON_DISPLAY_GROUP_ADDED -->
<item>12</item> <!-- WAKE_REASON_UNFOLD_DEVICE -->
<item>15</item> <!-- WAKE_REASON_TAP -->
<item>16</item> <!-- WAKE_REASON_LIFT -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9bee972..a056445 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -121,6 +121,9 @@
<dimen name="navigation_edge_cancelled_arrow_height">0dp</dimen>
<dimen name="navigation_edge_cancelled_edge_corners">6dp</dimen>
+ <!-- Height of the system icons container view in the status bar -->
+ <dimen name="status_bar_system_icons_height">@dimen/status_bar_icon_size_sp</dimen>
+
<!-- New sp height of notification icons in the status bar -->
<dimen name="status_bar_icon_size_sp">@*android:dimen/status_bar_icon_size_sp</dimen>
<!-- Original dp height of notification icons in the status bar -->
@@ -162,13 +165,21 @@
<!-- Size of the view displaying the wifi inout icon in the status bar. -->
<dimen name="status_bar_wifi_inout_container_size">17sp</dimen>
- <!-- Size of the view displaying the wifi signal icon in the status bar. -->
- <dimen name="status_bar_wifi_signal_size">13sp</dimen>
+ <!-- Size of the view displaying the wifi signal icon in the status bar. This value should
+ match the core/status_bar_system_icon_size and change to sp unit -->
+ <dimen name="status_bar_wifi_signal_size">15sp</dimen>
<!-- Size of the view displaying the mobile inout icon in the status bar. -->
<dimen name="status_bar_mobile_inout_container_size">17sp</dimen>
- <!-- Size of the view displaying the mobile signal icon in the status bar. -->
- <dimen name="status_bar_mobile_signal_size">13sp</dimen>
+ <!-- Size of the view displaying the mobile signal icon in the status bar. This value should
+ match the core/status_bar_system_icon_size and change to sp unit -->
+ <dimen name="status_bar_mobile_signal_size">15sp</dimen>
+ <!-- Size of the view displaying the mobile signal icon in the status bar. This value should
+ match the viewport height of mobile signal drawables such as ic_lte_mobiledata -->
+ <dimen name="status_bar_mobile_type_size">16sp</dimen>
+ <!-- Size of the view displaying the mobile roam icon in the status bar. This value should
+ match the viewport size of drawable stat_sys_roaming -->
+ <dimen name="status_bar_mobile_roam_size">8sp</dimen>
<!-- Spacing before the airplane mode icon if there are any icons preceding it. -->
<dimen name="status_bar_airplane_spacer_width">4sp</dimen>
@@ -343,8 +354,8 @@
<dimen name="status_bar_icons_padding_start">11dp</dimen>
<dimen name="status_bar_icons_padding_end">0dp</dimen>
- <dimen name="status_bar_icons_padding_bottom">8dp</dimen>
- <dimen name="status_bar_icons_padding_top">8dp</dimen>
+ <dimen name="status_bar_icons_padding_bottom">0dp</dimen>
+ <dimen name="status_bar_icons_padding_top">0dp</dimen>
<!-- gap on either side of status bar notification icons -->
<dimen name="status_bar_icon_horizontal_margin">0sp</dimen>
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index 7b4282f..427fd87 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -83,6 +83,7 @@
android:layout_width="0dp"
android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
app:layout_constraintWidth_default="wrap"
+ app:layout_constraintStart_toEndOf="@id/date"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/date"
app:layout_constraintBottom_toBottomOf="@id/date"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index bb11217..b81e081 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -178,6 +178,7 @@
getKeyguardSecurityCallback().dismiss(true, userId, getSecurityMode());
}
} else {
+ mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
if (isValidPassword) {
getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
if (timeoutMs > 0) {
@@ -186,7 +187,6 @@
handleAttemptLockout(deadline);
}
}
- mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
if (timeoutMs == 0) {
mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 3b09910f..aff2591 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -27,6 +27,7 @@
import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER;
import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
@@ -1081,8 +1082,10 @@
mLockPatternUtils.reportFailedPasswordAttempt(userId);
if (timeoutMs > 0) {
mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
- mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils,
- mSecurityModel.getSecurityMode(userId));
+ if (!mFeatureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) {
+ mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils,
+ mSecurityModel.getSecurityMode(userId));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 53229b9..d950c917 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2696,14 +2696,6 @@
}
/**
- * @return true if the FP sensor is non-UDFPS and the device can be unlocked using fingerprint
- * at this moment.
- */
- public boolean isFingerprintAllowedInBouncer() {
- return !isUdfpsSupported() && isUnlockingWithFingerprintAllowed();
- }
-
- /**
* @return true if there's at least one sfps enrollment for the current user.
*/
public boolean isSfpsEnrolled() {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 76b073e..1a0c7f9 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -181,6 +181,10 @@
mLockIcon.setImageState(getLockIconState(mIconType, mAod), true);
}
+ public ImageView getLockIcon() {
+ return mLockIcon;
+ }
+
private void addLockIconImageView(Context context, AttributeSet attrs) {
mLockIcon = new ImageView(context, attrs);
mLockIcon.setId(R.id.lock_icon);
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 5459718..4845a61 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -417,7 +417,10 @@
private void updateLockIconLocation() {
final float scaleFactor = mAuthController.getScaleFactor();
final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding,
+ scaledPadding);
+ } else {
if (mUdfpsSupported) {
mView.setCenterLocation(mAuthController.getUdfpsLocation(),
mAuthController.getUdfpsRadius(), scaledPadding);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
index 9d8dcc1..c2b9102 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
@@ -177,6 +177,9 @@
if (mFaceManager != null) {
mFaceManager.registerBiometricStateListener(mFaceStateListener);
}
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_RE_ENROLL, REENROLL_NOT_REQUIRED,
+ UserHandle.USER_CURRENT);
}
private void queueFaceReenrollNotification() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
index 86940ca..863ba8d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt
@@ -82,7 +82,9 @@
) : LogContextInteractor {
init {
- foldProvider.start()
+ applicationScope.launch {
+ foldProvider.start()
+ }
}
override val displayState =
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt
index 3206c00..1817ea9 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt
@@ -32,6 +32,7 @@
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
@@ -53,6 +54,9 @@
import com.android.systemui.R.string.kg_primary_auth_locked_out_pattern
import com.android.systemui.R.string.kg_primary_auth_locked_out_pin
import com.android.systemui.R.string.kg_prompt_after_dpm_lock
+import com.android.systemui.R.string.kg_prompt_after_update_password
+import com.android.systemui.R.string.kg_prompt_after_update_pattern
+import com.android.systemui.R.string.kg_prompt_after_update_pin
import com.android.systemui.R.string.kg_prompt_after_user_lockdown_password
import com.android.systemui.R.string.kg_prompt_after_user_lockdown_pattern
import com.android.systemui.R.string.kg_prompt_after_user_lockdown_pin
@@ -89,69 +93,76 @@
fun createFromPromptReason(
@BouncerPromptReason reason: Int,
userId: Int,
+ secondaryMsgOverride: String? = null
): BouncerMessageModel? {
val pair =
getBouncerMessage(
reason,
securityModel.getSecurityMode(userId),
- updateMonitor.isFingerprintAllowedInBouncer
+ updateMonitor.isUnlockingWithFingerprintAllowed
)
return pair?.let {
BouncerMessageModel(
- message = Message(messageResId = pair.first),
- secondaryMessage = Message(messageResId = pair.second)
+ message = Message(messageResId = pair.first, animate = false),
+ secondaryMessage =
+ secondaryMsgOverride?.let {
+ Message(message = secondaryMsgOverride, animate = false)
+ }
+ ?: Message(messageResId = pair.second, animate = false)
)
}
}
- fun createFromString(
- primaryMsg: String? = null,
- secondaryMsg: String? = null
- ): BouncerMessageModel =
- BouncerMessageModel(
- message = primaryMsg?.let { Message(message = it) },
- secondaryMessage = secondaryMsg?.let { Message(message = it) },
- )
-
/**
* Helper method that provides the relevant bouncer message that should be shown for different
- * scenarios indicated by [reason]. [securityMode] & [fpAllowedInBouncer] parameters are used to
+ * scenarios indicated by [reason]. [securityMode] & [fpAuthIsAllowed] parameters are used to
* provide a more specific message.
*/
private fun getBouncerMessage(
@BouncerPromptReason reason: Int,
securityMode: SecurityMode,
- fpAllowedInBouncer: Boolean = false
+ fpAuthIsAllowed: Boolean = false
): Pair<Int, Int>? {
return when (reason) {
+ // Primary auth locked out
+ PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode)
+ // Primary auth required reasons
PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode)
PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode)
PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode)
PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode)
- PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode)
PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode)
+ PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE -> authRequiredForMainlineUpdate(securityMode)
PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode)
- PROMPT_REASON_FACE_LOCKED_OUT -> faceUnlockUnavailable(securityMode)
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT ->
- if (fpAllowedInBouncer) incorrectSecurityInputWithFingerprint(securityMode)
- else incorrectSecurityInput(securityMode)
+ PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode)
+ // Non strong auth not available reasons
+ PROMPT_REASON_FACE_LOCKED_OUT ->
+ if (fpAuthIsAllowed) faceLockedOutButFingerprintAvailable(securityMode)
+ else faceLockedOut(securityMode)
PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT ->
- if (fpAllowedInBouncer) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
+ if (fpAuthIsAllowed) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
else nonStrongAuthTimeout(securityMode)
PROMPT_REASON_TRUSTAGENT_EXPIRED ->
- if (fpAllowedInBouncer) trustAgentDisabledWithFingerprintAllowed(securityMode)
+ if (fpAuthIsAllowed) trustAgentDisabledWithFingerprintAllowed(securityMode)
else trustAgentDisabled(securityMode)
+ // Auth incorrect input reasons.
+ PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT ->
+ if (fpAuthIsAllowed) incorrectSecurityInputWithFingerprint(securityMode)
+ else incorrectSecurityInput(securityMode)
PROMPT_REASON_INCORRECT_FACE_INPUT ->
- if (fpAllowedInBouncer) incorrectFaceInputWithFingerprintAllowed(securityMode)
+ if (fpAuthIsAllowed) incorrectFaceInputWithFingerprintAllowed(securityMode)
else incorrectFaceInput(securityMode)
PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode)
+ // Default message
PROMPT_REASON_DEFAULT ->
- if (fpAllowedInBouncer) defaultMessageWithFingerprint(securityMode)
+ if (fpAuthIsAllowed) defaultMessageWithFingerprint(securityMode)
else defaultMessage(securityMode)
- PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode)
else -> null
}
}
+
+ fun emptyMessage(): BouncerMessageModel =
+ BouncerMessageModel(Message(message = ""), Message(message = ""))
}
@Retention(AnnotationRetention.SOURCE)
@@ -172,6 +183,7 @@
PROMPT_REASON_NONE,
PROMPT_REASON_RESTART,
PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT,
+ PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE,
)
annotation class BouncerPromptReason
@@ -284,6 +296,15 @@
}
}
+private fun authRequiredForMainlineUpdate(securityMode: SecurityMode): Pair<Int, Int> {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_update_pattern)
+ SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_update_password)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_update_pin)
+ else -> Pair(0, 0)
+ }
+}
+
private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> {
return when (securityMode) {
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout)
@@ -311,7 +332,7 @@
}
}
-private fun faceUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> {
+private fun faceLockedOut(securityMode: SecurityMode): Pair<Int, Int> {
return when (securityMode) {
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out)
SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out)
@@ -320,6 +341,15 @@
}
}
+private fun faceLockedOutButFingerprintAvailable(securityMode: SecurityMode): Pair<Int, Int> {
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_face_locked_out)
+ SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_face_locked_out)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_face_locked_out)
+ else -> Pair(0, 0)
+ }
+}
+
private fun fingerprintUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> {
return when (securityMode) {
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_fp_locked_out)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
index 7e420cf..6fb0d4c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
@@ -28,6 +28,7 @@
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED
import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST
@@ -38,6 +39,7 @@
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
@@ -105,6 +107,9 @@
fun clearMessage()
}
+private const val SYS_BOOT_REASON_PROP = "sys.boot.reason.last"
+private const val REBOOT_MAINLINE_UPDATE = "reboot,mainline_update"
+
@SysUISingleton
class BouncerMessageRepositoryImpl
@Inject
@@ -114,6 +119,7 @@
updateMonitor: KeyguardUpdateMonitor,
private val bouncerMessageFactory: BouncerMessageFactory,
private val userRepository: UserRepository,
+ private val systemPropertiesHelper: SystemPropertiesHelper,
fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
) : BouncerMessageRepository {
@@ -132,6 +138,9 @@
private val isAnyBiometricsEnabledAndEnrolled =
or(isFaceEnrolledAndEnabled, isFingerprintEnrolledAndEnabled)
+ private val wasRebootedForMainlineUpdate
+ get() = systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE
+
private val authFlagsBasedPromptReason: Flow<Int> =
combine(
biometricSettingsRepository.authenticationFlags,
@@ -144,7 +153,8 @@
return@map if (
trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterReboot
) {
- PROMPT_REASON_RESTART
+ if (wasRebootedForMainlineUpdate) PROMPT_REASON_RESTART_FOR_MAINLINE_UPDATE
+ else PROMPT_REASON_RESTART
} else if (trustOrBiometricsAvailable && flags.isPrimaryAuthRequiredAfterTimeout) {
PROMPT_REASON_TIMEOUT
} else if (flags.isPrimaryAuthRequiredAfterDpmLockdown) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index d8cf398..8ed964d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -127,10 +127,12 @@
repository.setMessage(message ?: promptMessage(getAuthenticationMethod()))
sceneInteractor.setCurrentScene(
scene = SceneModel(SceneKey.Bouncer),
+ loggingReason = "request to unlock device while authentication required",
)
} else {
sceneInteractor.setCurrentScene(
scene = SceneModel(SceneKey.Gone),
+ loggingReason = "request to unlock device while authentication isn't required",
)
}
}
@@ -176,6 +178,7 @@
if (isAuthenticated) {
sceneInteractor.setCurrentScene(
scene = SceneModel(SceneKey.Gone),
+ loggingReason = "successful authentication",
)
} else {
repository.setMessage(errorMessage(getAuthenticationMethod()))
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index d06dd8e..fe01d08 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -86,7 +86,11 @@
repository.setFingerprintAcquisitionMessage(
if (value != null) {
- factory.createFromString(secondaryMsg = value)
+ factory.createFromPromptReason(
+ PROMPT_REASON_DEFAULT,
+ userRepository.getSelectedUserInfo().id,
+ secondaryMsgOverride = value
+ )
} else {
null
}
@@ -98,7 +102,11 @@
repository.setFaceAcquisitionMessage(
if (value != null) {
- factory.createFromString(secondaryMsg = value)
+ factory.createFromPromptReason(
+ PROMPT_REASON_DEFAULT,
+ userRepository.getSelectedUserInfo().id,
+ secondaryMsgOverride = value
+ )
} else {
null
}
@@ -110,7 +118,11 @@
repository.setCustomMessage(
if (value != null) {
- factory.createFromString(secondaryMsg = value)
+ factory.createFromPromptReason(
+ PROMPT_REASON_DEFAULT,
+ userRepository.getSelectedUserInfo().id,
+ secondaryMsgOverride = value
+ )
} else {
null
}
@@ -140,8 +152,7 @@
// always maps to an empty string.
private fun nullOrEmptyMessage() =
flowOf(
- if (featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) null
- else factory.createFromString("", "")
+ if (featureFlags.isEnabled(REVAMPED_BOUNCER_MESSAGES)) null else factory.emptyMessage()
)
val bouncerMessage =
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt
index 47fac2b..6486802 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt
@@ -41,8 +41,6 @@
super.onFinishInflate()
primaryMessageView = findViewById(R.id.bouncer_primary_message_area)
secondaryMessageView = findViewById(R.id.bouncer_secondary_message_area)
- primaryMessageView?.disable()
- secondaryMessageView?.disable()
}
fun init(factory: KeyguardMessageAreaController.Factory) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 3c42a29..f73a602 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -54,7 +54,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.keyboard.KeyboardModule;
-import com.android.systemui.keyguard.ui.view.layout.LockscreenLayoutModule;
+import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
@@ -179,7 +179,7 @@
GarbageMonitorModule.class,
KeyboardModule.class,
LetterboxModule.class,
- LockscreenLayoutModule.class,
+ KeyguardBlueprintModule.class,
LogModule.class,
MediaProjectionModule.class,
MediaProjectionTaskSwitcherModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 5a49475..5e14d7b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -195,10 +195,10 @@
@JvmField val FALSING_OFF_FOR_UNFOLDED = releasedFlag(225, "falsing_off_for_unfolded")
/** Enables code to show contextual loyalty cards in wallet entrypoints */
- // TODO(b/247587924): Tracking Bug
+ // TODO(b/294110497): Tracking Bug
@JvmField
val ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS =
- unreleasedFlag(226, "enable_wallet_contextual_loyalty_cards", teamfood = false)
+ unreleasedFlag(226, "enable_wallet_contextual_loyalty_cards", teamfood = true)
// TODO(b/242908637): Tracking Bug
@JvmField val WALLPAPER_FULLSCREEN_PREVIEW = releasedFlag(227, "wallpaper_fullscreen_preview")
@@ -266,11 +266,11 @@
@JvmField val KEYGUARD_TALKBACK_FIX = releasedFlag(238, "keyguard_talkback_fix")
// TODO(b/287268101): Tracking bug.
- @JvmField val TRANSIT_CLOCK = unreleasedFlag(239, "lockscreen_custom_transit_clock")
+ @JvmField val TRANSIT_CLOCK = releasedFlag(239, "lockscreen_custom_transit_clock")
/** Migrate the lock icon view to the new keyguard root view. */
// TODO(b/286552209): Tracking bug.
- @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon", teamfood = true)
+ @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon")
// TODO(b/288276738): Tracking bug.
@JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag(241, "widget_on_keyguard")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 4495943..6213265c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -26,27 +26,28 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
+import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
-import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager
-import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManagerCommandListener
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
-import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import javax.inject.Inject
@@ -68,9 +69,8 @@
private val notificationShadeWindowView: NotificationShadeWindowView,
private val featureFlags: FeatureFlags,
private val indicationController: KeyguardIndicationController,
- private val keyguardLayoutManager: KeyguardLayoutManager,
- private val keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener,
- private val keyguardQuickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
+ private val keyguardQuickAffordancesCombinedViewModel:
+ KeyguardQuickAffordancesCombinedViewModel,
private val falsingManager: FalsingManager,
private val vibratorHelper: VibratorHelper,
private val keyguardStateController: KeyguardStateController,
@@ -78,6 +78,8 @@
private val activityStarter: ActivityStarter,
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
+ private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
+ private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -100,8 +102,8 @@
bindAmbientIndicationArea()
bindSettingsPopupMenu()
- keyguardLayoutManager.layoutViews()
- keyguardLayoutManagerCommandListener.start()
+ KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
+ keyguardBlueprintCommandListener.start()
}
fun setupNotificationStackScrollLayout(legacyParent: ViewGroup) {
@@ -125,8 +127,6 @@
leftShortcutHandle?.onConfigurationChanged()
rightShortcutHandle?.onConfigurationChanged()
ambientIndicationAreaHandle?.onConfigurationChanged()
-
- keyguardLayoutManager.layoutViews()
}
fun bindIndicationArea() {
@@ -152,14 +152,15 @@
private fun bindKeyguardRootView() {
rootViewHandle?.dispose()
- rootViewHandle = KeyguardRootViewBinder.bind(
- keyguardRootView,
- keyguardRootViewModel,
- featureFlags,
- occludingAppDeviceEntryMessageViewModel,
- chipbarCoordinator,
- keyguardStateController,
- )
+ rootViewHandle =
+ KeyguardRootViewBinder.bind(
+ keyguardRootView,
+ keyguardRootViewModel,
+ featureFlags,
+ occludingAppDeviceEntryMessageViewModel,
+ chipbarCoordinator,
+ keyguardStateController,
+ )
}
private fun bindLockIconView(legacyParent: ViewGroup) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
new file mode 100644
index 0000000..059f72b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
+import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Manages blueprint changes for the lockscreen.
+ *
+ * To add a blueprint, create a class that implements LockscreenBlueprint and bind it to the map in
+ * the dagger module:
+ *
+ * A Blueprint determines how the layout should be constrained on a high level.
+ *
+ * A Section is a modular piece of code that implements the constraints. The blueprint uses the
+ * sections to define the constraints.
+ *
+ * @see KeyguardBlueprintModule
+ */
+@SysUISingleton
+class KeyguardBlueprintRepository
+@Inject
+constructor(
+ configurationRepository: ConfigurationRepository,
+ blueprints: Set<@JvmSuppressWildcards KeyguardBlueprint>,
+ @Application private val applicationScope: CoroutineScope,
+) {
+ private val blueprintIdMap: Map<String, KeyguardBlueprint> = blueprints.associateBy { it.id }
+ private val _blueprint: MutableSharedFlow<KeyguardBlueprint> = MutableSharedFlow(replay = 1)
+ val blueprint: Flow<KeyguardBlueprint> = _blueprint.asSharedFlow()
+
+ init {
+ applyBlueprint(blueprintIdMap[DEFAULT]!!)
+ applicationScope.launch {
+ configurationRepository.onAnyConfigurationChange.collect { refreshBlueprint() }
+ }
+ }
+
+ /**
+ * Emits the blueprint value to the collectors.
+ *
+ * @param blueprintId
+ * @return whether the transition has succeeded.
+ */
+ fun applyBlueprint(blueprintId: String?): Boolean {
+ val blueprint = blueprintIdMap[blueprintId] ?: return false
+ applyBlueprint(blueprint)
+ return true
+ }
+
+ /** Emits the blueprint value to the collectors. */
+ fun applyBlueprint(blueprint: KeyguardBlueprint?) {
+ blueprint?.let { _blueprint.tryEmit(it) }
+ }
+
+ /** Re-emits the last emitted blueprint value if possible. */
+ fun refreshBlueprint() {
+ if (_blueprint.replayCache.isNotEmpty()) {
+ _blueprint.tryEmit(_blueprint.replayCache.last())
+ }
+ }
+
+ /** Prints all available blueprints to the PrintWriter. */
+ fun printBlueprints(pw: PrintWriter) {
+ blueprintIdMap.forEach { entry -> pw.println("${entry.key}") }
+ }
+}
+
+/** Determines the constraints for the ConstraintSet in the lockscreen root view. */
+interface KeyguardBlueprint {
+ val id: String
+
+ fun apply(constraintSet: ConstraintSet)
+}
+
+/**
+ * Lower level modules that determine constraints for a particular section in the lockscreen root
+ * view.
+ */
+interface KeyguardSection {
+ fun apply(constraintSet: ConstraintSet)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
new file mode 100644
index 0000000..390ad7e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
+import javax.inject.Inject
+
+@SysUISingleton
+class KeyguardBlueprintInteractor
+@Inject
+constructor(private val keyguardBlueprintRepository: KeyguardBlueprintRepository) {
+ val blueprint = keyguardBlueprintRepository.blueprint
+
+ /**
+ * Transitions to a blueprint.
+ *
+ * @param blueprintId
+ * @return whether the transition has succeeded.
+ */
+ fun transitionToBlueprint(blueprintId: String): Boolean {
+ return keyguardBlueprintRepository.applyBlueprint(blueprintId)
+ }
+
+ /** Re-emits the blueprint value to the collectors. */
+ fun refreshBlueprint() {
+ keyguardBlueprintRepository.refreshBlueprint()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
new file mode 100644
index 0000000..23b80b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.os.Trace
+import android.util.Log
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.launch
+
+class KeyguardBlueprintViewBinder {
+ companion object {
+ private const val TAG = "KeyguardBlueprintViewBinder"
+
+ fun bind(keyguardRootView: KeyguardRootView, viewModel: KeyguardBlueprintViewModel) {
+ keyguardRootView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ viewModel.blueprint.collect { blueprint ->
+ Trace.beginSection("KeyguardBlueprintController#applyBlueprint")
+ Log.d(TAG, "applying blueprint: $blueprint")
+ ConstraintSet().apply {
+ clone(keyguardRootView)
+ val emptyLayout = ConstraintSet.Layout()
+ knownIds.forEach { getConstraint(it).layout.copyFrom(emptyLayout) }
+ blueprint?.apply(this)
+ applyTo(keyguardRootView)
+ }
+ Trace.endSection()
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 3e6e158..58bc552 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -44,11 +44,13 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
@@ -90,12 +92,13 @@
private val lockscreenSmartspaceController: LockscreenSmartspaceController,
private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
private val featureFlags: FeatureFlags,
- private val keyguardLayoutManager: KeyguardLayoutManager,
private val falsingManager: FalsingManager,
private val vibratorHelper: VibratorHelper,
private val indicationController: KeyguardIndicationController,
private val keyguardRootViewModel: KeyguardRootViewModel,
@Assisted bundle: Bundle,
+ private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
+ private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -129,17 +132,17 @@
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
keyguardRootViewModel.enablePreviewMode(
initiallySelectedSlotId =
- bundle.getString(
- KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
- ),
+ bundle.getString(
+ KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
+ ),
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
} else {
bottomAreaViewModel.enablePreviewMode(
initiallySelectedSlotId =
- bundle.getString(
- KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
- ),
+ bundle.getString(
+ KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
+ ),
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
}
@@ -169,7 +172,8 @@
),
)
setupShortcuts(keyguardRootView)
- keyguardLayoutManager.layoutViews(keyguardRootView)
+ KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
+ keyguardBlueprintInteractor.refreshBlueprint()
} else {
setUpBottomArea(rootView)
}
@@ -309,7 +313,7 @@
false,
) as KeyguardBottomAreaView
bottomAreaView.init(
- viewModel = bottomAreaViewModel,
+ viewModel = bottomAreaViewModel,
)
parentView.addView(
bottomAreaView,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
index 65fe990..e60901f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -19,10 +19,10 @@
import android.content.Context
import android.util.AttributeSet
-import androidx.constraintlayout.widget.ConstraintLayout
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.res.ResourcesCompat
import com.android.keyguard.LockIconView
import com.android.systemui.R
@@ -62,8 +62,9 @@
}
private fun addLeftShortcut() {
- val view = LaunchableImageView(context, attrs)
- .apply {
+ val padding = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
+ val view =
+ LaunchableImageView(context, attrs).apply {
id = R.id.start_button
scaleType = ImageView.ScaleType.FIT_CENTER
background =
@@ -79,13 +80,15 @@
context.theme
)
visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
}
addView(view)
}
private fun addRightShortcut() {
- val view = LaunchableImageView(context, attrs)
- .apply {
+ val padding = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
+ val view =
+ LaunchableImageView(context, attrs).apply {
id = R.id.end_button
scaleType = ImageView.ScaleType.FIT_CENTER
background =
@@ -101,20 +104,19 @@
context.theme
)
visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
}
addView(view)
}
private fun addSettingsPopupMenu() {
- val view = LayoutInflater.from(context).inflate(
- R.layout.keyguard_settings_popup_menu,
- this,
- false
- )
- .apply {
- id = R.id.keyguard_settings_button
- visibility = GONE
- }
+ val view =
+ LayoutInflater.from(context)
+ .inflate(R.layout.keyguard_settings_popup_menu, this, false)
+ .apply {
+ id = R.id.keyguard_settings_button
+ visibility = GONE
+ }
addView(view)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt
deleted file mode 100644
index 6be45c7..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * 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.
- *
- */
-
-package com.android.systemui.keyguard.ui.view.layout
-
-import android.content.Context
-import android.graphics.Point
-import android.graphics.Rect
-import android.util.DisplayMetrics
-import android.view.View
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.view.ViewGroup.MarginLayoutParams
-import android.view.WindowManager
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.TextView
-import androidx.annotation.VisibleForTesting
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.LEFT
-import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.RIGHT
-import androidx.constraintlayout.widget.ConstraintSet.START
-import androidx.constraintlayout.widget.ConstraintSet.TOP
-import androidx.core.view.setPadding
-import androidx.core.view.updateLayoutParams
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
-import com.android.systemui.animation.view.LaunchableLinearLayout
-import com.android.systemui.biometrics.AuthController
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import javax.inject.Inject
-
-/**
- * Positions elements of the lockscreen to the default position.
- *
- * This will be the most common use case for phones in portrait mode.
- */
-@SysUISingleton
-class DefaultLockscreenLayout
-@Inject
-constructor(
- private val authController: AuthController,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val windowManager: WindowManager,
- private val context: Context,
-) : LockscreenLayout {
- override val id: String = DEFAULT
-
- override fun layoutIndicationArea(rootView: KeyguardRootView) {
- val indicationArea = rootView.findViewById<View>(R.id.keyguard_indication_area) ?: return
-
- rootView.getConstraintSet().apply {
- constrainWidth(indicationArea.id, MATCH_PARENT)
- constrainHeight(indicationArea.id, WRAP_CONTENT)
- connect(
- indicationArea.id,
- BOTTOM,
- PARENT_ID,
- BOTTOM,
- R.dimen.keyguard_indication_margin_bottom.dp()
- )
- connect(indicationArea.id, START, PARENT_ID, START)
- connect(indicationArea.id, END, PARENT_ID, END)
- applyTo(rootView)
- }
- }
-
- override fun layoutLockIcon(rootView: KeyguardRootView) {
- val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
- val scaleFactor: Float = authController.scaleFactor
- val mBottomPaddingPx = R.dimen.lock_icon_margin_bottom.dp()
- val mDefaultPaddingPx = R.dimen.lock_icon_padding.dp()
- val scaledPadding: Int = (mDefaultPaddingPx * scaleFactor).toInt()
- val bounds = windowManager.currentWindowMetrics.bounds
- val widthPixels = bounds.right.toFloat()
- val heightPixels = bounds.bottom.toFloat()
- val defaultDensity =
- DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
- DisplayMetrics.DENSITY_DEFAULT.toFloat()
- val lockIconRadiusPx = (defaultDensity * 36).toInt()
-
- if (isUdfpsSupported) {
- authController.udfpsLocation?.let { udfpsLocation ->
- centerLockIcon(udfpsLocation, authController.udfpsRadius, scaledPadding, rootView)
- }
- } else {
- centerLockIcon(
- Point(
- (widthPixels / 2).toInt(),
- (heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
- ),
- lockIconRadiusPx * scaleFactor,
- scaledPadding,
- rootView
- )
- }
- }
-
- @VisibleForTesting
- internal fun centerLockIcon(
- center: Point,
- radius: Float,
- drawablePadding: Int,
- rootView: KeyguardRootView,
- ) {
- val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
- val lockIcon = lockIconView.findViewById<View>(R.id.lock_icon) ?: return
- lockIcon.setPadding(drawablePadding, drawablePadding, drawablePadding, drawablePadding)
-
- val sensorRect =
- Rect().apply {
- set(
- center.x - radius.toInt(),
- center.y - radius.toInt(),
- center.x + radius.toInt(),
- center.y + radius.toInt(),
- )
- }
-
- rootView.getConstraintSet().apply {
- constrainWidth(lockIconView.id, sensorRect.right - sensorRect.left)
- constrainHeight(lockIconView.id, sensorRect.bottom - sensorRect.top)
- connect(lockIconView.id, TOP, PARENT_ID, TOP, sensorRect.top)
- connect(lockIconView.id, START, PARENT_ID, START, sensorRect.left)
- applyTo(rootView)
- }
- }
-
- override fun layoutShortcuts(rootView: KeyguardRootView) {
- val leftShortcut = rootView.findViewById<View>(R.id.start_button) ?: return
- val rightShortcut = rootView.findViewById<View>(R.id.end_button) ?: return
- val width =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
- val height =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
- val horizontalOffsetMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
- val verticalOffsetMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
- val padding =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
-
- leftShortcut.apply {
- updateLayoutParams<MarginLayoutParams> {
- marginStart = horizontalOffsetMargin
- bottomMargin = verticalOffsetMargin
- }
- setPadding(padding)
- }
-
- rightShortcut.apply {
- updateLayoutParams<MarginLayoutParams> {
- marginEnd = horizontalOffsetMargin
- bottomMargin = verticalOffsetMargin
- }
- setPadding(padding)
- }
-
- rootView.getConstraintSet().apply {
- constrainWidth(leftShortcut.id, width)
- constrainHeight(leftShortcut.id, height)
- connect(leftShortcut.id, LEFT, PARENT_ID, LEFT)
- connect(leftShortcut.id, BOTTOM, PARENT_ID, BOTTOM)
-
- constrainWidth(rightShortcut.id, width)
- constrainHeight(rightShortcut.id, height)
- connect(rightShortcut.id, RIGHT, PARENT_ID, RIGHT)
- connect(rightShortcut.id, BOTTOM, PARENT_ID, BOTTOM)
- applyTo(rootView)
- }
- }
-
- override fun layoutAmbientIndicationArea(rootView: KeyguardRootView) {
- val ambientIndicationContainer =
- rootView.findViewById<View>(R.id.ambient_indication_container) ?: return
- val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
- val indicationArea = rootView.findViewById<View>(R.id.keyguard_indication_area) ?: return
-
- rootView.getConstraintSet().apply {
- constrainWidth(ambientIndicationContainer.id, MATCH_PARENT)
-
- if (keyguardUpdateMonitor.isUdfpsSupported) {
- //constrain below udfps and above indication area
- constrainHeight(ambientIndicationContainer.id, MATCH_CONSTRAINT)
- connect(ambientIndicationContainer.id, TOP, lockIconView.id, BOTTOM)
- connect(ambientIndicationContainer.id, BOTTOM, indicationArea.id, TOP)
- connect(ambientIndicationContainer.id, LEFT, PARENT_ID, LEFT)
- connect(ambientIndicationContainer.id, RIGHT, PARENT_ID, RIGHT)
- } else {
- //constrain above lock icon
- constrainHeight(ambientIndicationContainer.id, WRAP_CONTENT)
- connect(ambientIndicationContainer.id, BOTTOM, lockIconView.id, TOP)
- connect(ambientIndicationContainer.id, LEFT, PARENT_ID, LEFT)
- connect(ambientIndicationContainer.id, RIGHT, PARENT_ID, RIGHT)
- }
-
- applyTo(rootView)
- }
- }
-
- override fun layoutSettingsPopupMenu(rootView: KeyguardRootView) {
- val popupMenu =
- rootView.findViewById<LaunchableLinearLayout>(R.id.keyguard_settings_button) ?: return
- val icon = popupMenu.findViewById<ImageView>(R.id.icon) ?: return
- val textView = popupMenu.findViewById<TextView>(R.id.text) ?: return
- val horizontalOffsetMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
-
- icon.updateLayoutParams<LinearLayout.LayoutParams> {
- height =
- context
- .resources
- .getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_height)
- width =
- context
- .resources
- .getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_width)
- marginEnd =
- context
- .resources
- .getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_end_margin)
- }
-
- textView.updateLayoutParams<LinearLayout.LayoutParams> {
- height = WRAP_CONTENT
- width = WRAP_CONTENT
- }
-
- popupMenu.updateLayoutParams<MarginLayoutParams> {
- bottomMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
- marginStart = horizontalOffsetMargin
- marginEnd = horizontalOffsetMargin
- }
- popupMenu.setPadding(
- context.resources.getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_padding)
- )
-
- rootView.getConstraintSet().apply {
- constrainWidth(popupMenu.id, WRAP_CONTENT)
- constrainHeight(popupMenu.id, WRAP_CONTENT)
- constrainMinHeight(
- popupMenu.id,
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
- )
- connect(popupMenu.id, LEFT, PARENT_ID, LEFT)
- connect(popupMenu.id, RIGHT, PARENT_ID, RIGHT)
- connect(popupMenu.id, BOTTOM, PARENT_ID, BOTTOM)
-
- applyTo(rootView)
- }
- }
-
- private fun Int.dp(): Int {
- return context.resources.getDimensionPixelSize(this)
- }
-
- private fun ConstraintLayout.getConstraintSet(): ConstraintSet =
- ConstraintSet().also {
- it.clone(this)
- }
-
- companion object {
- const val DEFAULT = "default"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
index b351ea8..36d21f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
@@ -16,17 +16,20 @@
package com.android.systemui.keyguard.ui.view.layout
+import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import java.io.PrintWriter
import javax.inject.Inject
-/** Uses $ adb shell cmd statusbar layout <LayoutId> */
-class KeyguardLayoutManagerCommandListener
+/** Uses $ adb shell cmd statusbar blueprint <BlueprintId> */
+class KeyguardBlueprintCommandListener
@Inject
constructor(
private val commandRegistry: CommandRegistry,
- private val keyguardLayoutManager: KeyguardLayoutManager
+ private val keyguardBlueprintRepository: KeyguardBlueprintRepository,
+ private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
) {
private val layoutCommand = KeyguardLayoutManagerCommand()
@@ -42,22 +45,22 @@
return
}
- if (keyguardLayoutManager.transitionToLayout(arg)) {
+ if (keyguardBlueprintInteractor.transitionToBlueprint(arg)) {
pw.println("Transition succeeded!")
} else {
- pw.println("Invalid argument! To see available layout ids, run:")
- pw.println("$ adb shell cmd statusbar layout help")
+ pw.println("Invalid argument! To see available blueprint ids, run:")
+ pw.println("$ adb shell cmd statusbar blueprint help")
}
}
override fun help(pw: PrintWriter) {
- pw.println("Usage: $ adb shell cmd statusbar layout <layoutId>")
- pw.println("Existing Layout Ids: ")
- keyguardLayoutManager.layoutIdMap.forEach { entry -> pw.println("${entry.key}") }
+ pw.println("Usage: $ adb shell cmd statusbar blueprint <blueprintId>")
+ pw.println("Existing Blueprint Ids: ")
+ keyguardBlueprintRepository.printBlueprints(pw)
}
}
companion object {
- internal const val COMMAND = "layout"
+ internal const val COMMAND = "blueprint"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt
deleted file mode 100644
index 6973cd9..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.
- *
- */
-
-package com.android.systemui.keyguard.ui.view.layout
-
-import android.content.res.Configuration
-import androidx.constraintlayout.widget.ConstraintSet
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.keyguard.ui.view.layout.DefaultLockscreenLayout.Companion.DEFAULT
-import com.android.systemui.statusbar.policy.ConfigurationController
-import javax.inject.Inject
-
-/**
- * Manages layout changes for the lockscreen.
- *
- * To add a layout, add an entry to the map with a unique id and call #transitionToLayout(string).
- */
-@SysUISingleton
-class KeyguardLayoutManager
-@Inject
-constructor(
- configurationController: ConfigurationController,
- layouts: Set<@JvmSuppressWildcards LockscreenLayout>,
- private val keyguardRootView: KeyguardRootView,
-) {
- internal val layoutIdMap: Map<String, LockscreenLayout> = layouts.associateBy { it.id }
- private var layout: LockscreenLayout? = layoutIdMap[DEFAULT]
-
- init {
- configurationController.addCallback(
- object : ConfigurationController.ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration?) {
- layoutViews()
- }
- }
- )
- }
-
- /**
- * Transitions to a layout.
- *
- * @param layoutId
- * @return whether the transition has succeeded.
- */
- fun transitionToLayout(layoutId: String): Boolean {
- layout = layoutIdMap[layoutId] ?: return false
- layoutViews()
- return true
- }
-
- fun layoutViews(rootView: KeyguardRootView = keyguardRootView) {
- layout?.layoutViews(rootView)
- }
-
- companion object {
- const val TAG = "KeyguardLayoutManager"
- }
-}
-
-interface LockscreenLayout {
- val id: String
-
- fun layoutViews(rootView: KeyguardRootView) {
- // Clear constraints.
- ConstraintSet()
- .apply {
- clone(rootView)
- knownIds.forEach { getConstraint(it).layout.copyFrom(ConstraintSet.Layout()) }
- }
- .applyTo(rootView)
- layoutIndicationArea(rootView)
- layoutLockIcon(rootView)
- layoutShortcuts(rootView)
- layoutAmbientIndicationArea(rootView)
- layoutSettingsPopupMenu(rootView)
- }
- fun layoutIndicationArea(rootView: KeyguardRootView)
- fun layoutLockIcon(rootView: KeyguardRootView)
- fun layoutShortcuts(rootView: KeyguardRootView)
- fun layoutAmbientIndicationArea(rootView: KeyguardRootView)
- fun layoutSettingsPopupMenu(rootView: KeyguardRootView)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/ShortcutsBesideUdfpsLockscreenLayout.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/ShortcutsBesideUdfpsLockscreenLayout.kt
deleted file mode 100644
index 569762d..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/ShortcutsBesideUdfpsLockscreenLayout.kt
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * 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.
- *
- */
-
-package com.android.systemui.keyguard.ui.view.layout
-
-import android.content.Context
-import android.graphics.Point
-import android.graphics.Rect
-import android.util.DisplayMetrics
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.view.WindowManager
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.TextView
-import androidx.annotation.VisibleForTesting
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.LEFT
-import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.RIGHT
-import androidx.constraintlayout.widget.ConstraintSet.START
-import androidx.constraintlayout.widget.ConstraintSet.TOP
-import androidx.core.view.setPadding
-import androidx.core.view.updateLayoutParams
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
-import com.android.systemui.animation.view.LaunchableLinearLayout
-import com.android.systemui.biometrics.AuthController
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import javax.inject.Inject
-
-/**
- * Positions elements of the lockscreen to the default position.
- *
- * This will be the most common use case for phones in portrait mode.
- */
-@SysUISingleton
-class ShortcutsBesideUdfpsLockscreenLayout
-@Inject
-constructor(
- private val authController: AuthController,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val windowManager: WindowManager,
- private val context: Context,
-) : LockscreenLayout {
- override val id: String = SHORTCUTS_BESIDE_UDFPS
-
- override fun layoutIndicationArea(rootView: KeyguardRootView) {
- val indicationArea = rootView.findViewById<View>(R.id.keyguard_indication_area) ?: return
-
- rootView.getConstraintSet().apply {
- constrainWidth(indicationArea.id, MATCH_PARENT)
- constrainHeight(indicationArea.id, WRAP_CONTENT)
- connect(
- indicationArea.id,
- BOTTOM,
- PARENT_ID,
- BOTTOM,
- R.dimen.keyguard_indication_margin_bottom.dp()
- )
- connect(indicationArea.id, START, PARENT_ID, START)
- connect(indicationArea.id, END, PARENT_ID, END)
- applyTo(rootView)
- }
- }
-
- override fun layoutLockIcon(rootView: KeyguardRootView) {
- val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
- val scaleFactor: Float = authController.scaleFactor
- val mBottomPaddingPx = R.dimen.lock_icon_margin_bottom.dp()
- val mDefaultPaddingPx = R.dimen.lock_icon_padding.dp()
- val scaledPadding: Int = (mDefaultPaddingPx * scaleFactor).toInt()
- val bounds = windowManager.currentWindowMetrics.bounds
- val widthPixels = bounds.right.toFloat()
- val heightPixels = bounds.bottom.toFloat()
- val defaultDensity =
- DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
- DisplayMetrics.DENSITY_DEFAULT.toFloat()
- val lockIconRadiusPx = (defaultDensity * 36).toInt()
-
- if (isUdfpsSupported) {
- authController.udfpsLocation?.let { udfpsLocation ->
- centerLockIcon(udfpsLocation, authController.udfpsRadius, scaledPadding, rootView)
- }
- } else {
- centerLockIcon(
- Point(
- (widthPixels / 2).toInt(),
- (heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
- ),
- lockIconRadiusPx * scaleFactor,
- scaledPadding,
- rootView
- )
- }
- }
-
- @VisibleForTesting
- internal fun centerLockIcon(
- center: Point,
- radius: Float,
- drawablePadding: Int,
- rootView: KeyguardRootView,
- ) {
- val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
- val lockIcon = lockIconView.findViewById<View>(R.id.lock_icon) ?: return
- lockIcon.setPadding(drawablePadding, drawablePadding, drawablePadding, drawablePadding)
-
- val sensorRect =
- Rect().apply {
- set(
- center.x - radius.toInt(),
- center.y - radius.toInt(),
- center.x + radius.toInt(),
- center.y + radius.toInt(),
- )
- }
-
- rootView.getConstraintSet().apply {
- constrainWidth(lockIconView.id, sensorRect.right - sensorRect.left)
- constrainHeight(lockIconView.id, sensorRect.bottom - sensorRect.top)
- connect(lockIconView.id, TOP, PARENT_ID, TOP, sensorRect.top)
- connect(lockIconView.id, START, PARENT_ID, START, sensorRect.left)
- applyTo(rootView)
- }
- }
-
- override fun layoutShortcuts(rootView: KeyguardRootView) {
- val leftShortcut = rootView.findViewById<View>(R.id.start_button) ?: return
- val rightShortcut = rootView.findViewById<View>(R.id.end_button) ?: return
- val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
- val udfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
- val width =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
- val height =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
- val horizontalOffsetMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
- val verticalOffsetMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
- val padding =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
-
- if (!udfpsSupported) {
- leftShortcut.apply {
- updateLayoutParams<ViewGroup.MarginLayoutParams> {
- marginStart = horizontalOffsetMargin
- bottomMargin = verticalOffsetMargin
- }
- setPadding(padding)
- }
-
- rightShortcut.apply {
- updateLayoutParams<ViewGroup.MarginLayoutParams> {
- marginEnd = horizontalOffsetMargin
- bottomMargin = verticalOffsetMargin
- }
- setPadding(padding)
- }
- }
-
- rootView.getConstraintSet().apply {
- if (udfpsSupported) {
- constrainWidth(leftShortcut.id, width)
- constrainHeight(leftShortcut.id, height)
- connect(leftShortcut.id, LEFT, PARENT_ID, LEFT)
- connect(leftShortcut.id, RIGHT, lockIconView.id, LEFT)
- connect(leftShortcut.id, TOP, lockIconView.id, TOP)
- connect(leftShortcut.id, BOTTOM, lockIconView.id, BOTTOM)
-
- constrainWidth(rightShortcut.id, width)
- constrainHeight(rightShortcut.id, height)
- connect(rightShortcut.id, RIGHT, PARENT_ID, RIGHT)
- connect(rightShortcut.id, LEFT, lockIconView.id, RIGHT)
- connect(rightShortcut.id, TOP, lockIconView.id, TOP)
- connect(rightShortcut.id, BOTTOM, lockIconView.id, BOTTOM)
- } else {
- constrainWidth(leftShortcut.id, width)
- constrainHeight(leftShortcut.id, height)
- connect(leftShortcut.id, LEFT, PARENT_ID, LEFT)
- connect(leftShortcut.id, BOTTOM, PARENT_ID, BOTTOM)
-
- constrainWidth(rightShortcut.id, width)
- constrainHeight(rightShortcut.id, height)
- connect(rightShortcut.id, RIGHT, PARENT_ID, RIGHT)
- connect(rightShortcut.id, BOTTOM, PARENT_ID, BOTTOM)
- }
- applyTo(rootView)
- }
- }
-
- override fun layoutAmbientIndicationArea(rootView: KeyguardRootView) {
- val ambientIndicationContainer =
- rootView.findViewById<View>(R.id.ambient_indication_container) ?: return
- val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
- val indicationArea = rootView.findViewById<View>(R.id.keyguard_indication_area) ?: return
-
- rootView.getConstraintSet().apply {
- constrainWidth(ambientIndicationContainer.id, MATCH_PARENT)
-
- if (keyguardUpdateMonitor.isUdfpsSupported) {
- //constrain below udfps and above indication area
- constrainHeight(ambientIndicationContainer.id, MATCH_CONSTRAINT)
- connect(ambientIndicationContainer.id, TOP, lockIconView.id, BOTTOM)
- connect(ambientIndicationContainer.id, BOTTOM, indicationArea.id, TOP)
- connect(ambientIndicationContainer.id, LEFT, PARENT_ID, LEFT)
- connect(ambientIndicationContainer.id, RIGHT, PARENT_ID, RIGHT)
- } else {
- //constrain above lock icon
- constrainHeight(ambientIndicationContainer.id, WRAP_CONTENT)
- connect(ambientIndicationContainer.id, BOTTOM, lockIconView.id, TOP)
- connect(ambientIndicationContainer.id, LEFT, PARENT_ID, LEFT)
- connect(ambientIndicationContainer.id, RIGHT, PARENT_ID, RIGHT)
- }
- applyTo(rootView)
- }
- }
-
- override fun layoutSettingsPopupMenu(rootView: KeyguardRootView) {
- val popupMenu =
- rootView.findViewById<LaunchableLinearLayout>(R.id.keyguard_settings_button) ?: return
- val icon = popupMenu.findViewById<ImageView>(R.id.icon) ?: return
- val textView = popupMenu.findViewById<TextView>(R.id.text) ?: return
- val horizontalOffsetMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
-
- icon.updateLayoutParams<LinearLayout.LayoutParams> {
- height =
- context
- .resources
- .getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_height)
- width =
- context
- .resources
- .getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_width)
- marginEnd =
- context
- .resources
- .getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_end_margin)
- }
-
- textView.updateLayoutParams<LinearLayout.LayoutParams> {
- height = WRAP_CONTENT
- width = WRAP_CONTENT
- }
-
- popupMenu.updateLayoutParams<ViewGroup.MarginLayoutParams> {
- bottomMargin =
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
- marginStart = horizontalOffsetMargin
- marginEnd = horizontalOffsetMargin
- }
- popupMenu.setPadding(
- context.resources.getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_padding)
- )
-
- rootView.getConstraintSet().apply {
- constrainWidth(popupMenu.id, WRAP_CONTENT)
- constrainHeight(popupMenu.id, WRAP_CONTENT)
- constrainMinHeight(
- popupMenu.id,
- context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
- )
- connect(popupMenu.id, LEFT, PARENT_ID, LEFT)
- connect(popupMenu.id, RIGHT, PARENT_ID, RIGHT)
- connect(popupMenu.id, BOTTOM, PARENT_ID, BOTTOM)
-
- applyTo(rootView)
- }
- }
-
- private fun Int.dp(): Int {
- return context.resources.getDimensionPixelSize(this)
- }
-
- private fun ConstraintLayout.getConstraintSet(): ConstraintSet =
- ConstraintSet().also {
- it.clone(this)
- }
-
- companion object {
- const val SHORTCUTS_BESIDE_UDFPS = "shortcutsBesideUdfps"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
new file mode 100644
index 0000000..5538fe7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.blueprints
+
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import javax.inject.Inject
+
+/**
+ * Positions elements of the lockscreen to the default position.
+ *
+ * This will be the most common use case for phones in portrait mode.
+ */
+@SysUISingleton
+@JvmSuppressWildcards
+class DefaultKeyguardBlueprint
+@Inject
+constructor(
+ private val defaultIndicationAreaSection: DefaultIndicationAreaSection,
+ private val defaultLockIconSection: DefaultLockIconSection,
+ private val defaultShortcutsSection: DefaultShortcutsSection,
+ private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
+) : KeyguardBlueprint {
+ override val id: String = DEFAULT
+
+ override fun apply(constraintSet: ConstraintSet) {
+ defaultIndicationAreaSection.apply(constraintSet)
+ defaultLockIconSection.apply(constraintSet)
+ defaultShortcutsSection.apply(constraintSet)
+ defaultAmbientIndicationAreaSection.apply(constraintSet)
+ defaultSettingsPopupMenuSection.apply(constraintSet)
+ }
+
+ companion object {
+ const val DEFAULT = "default"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
similarity index 61%
rename from packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index c0447af..fefe679 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -15,23 +15,24 @@
*
*/
-package com.android.systemui.keyguard.ui.view.layout
+package com.android.systemui.keyguard.ui.view.layout.blueprints
+import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
@Module
-abstract class LockscreenLayoutModule {
+abstract class KeyguardBlueprintModule {
@Binds
@IntoSet
- abstract fun bindDefaultLayout(
- defaultLockscreenLayout: DefaultLockscreenLayout
- ): LockscreenLayout
+ abstract fun bindDefaultBlueprint(
+ defaultLockscreenBlueprint: DefaultKeyguardBlueprint
+ ): KeyguardBlueprint
@Binds
@IntoSet
- abstract fun bindShortcutsBesideUdfpsLockscreenLayout(
- shortcutsBesideUdfpsLockscreenLayout: ShortcutsBesideUdfpsLockscreenLayout
- ): LockscreenLayout
+ abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
+ shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
+ ): KeyguardBlueprint
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
new file mode 100644
index 0000000..19410e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.blueprints
+
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdfpsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import javax.inject.Inject
+
+/** Vertically aligns the shortcuts with the udfps. */
+@SysUISingleton
+class ShortcutsBesideUdfpsKeyguardBlueprint
+@Inject
+constructor(
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val defaultIndicationAreaSection: DefaultIndicationAreaSection,
+ private val defaultLockIconSection: DefaultLockIconSection,
+ private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
+ private val alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
+ private val defaultShortcutsSection: DefaultShortcutsSection,
+) : KeyguardBlueprint {
+ override val id: String = SHORTCUTS_BESIDE_UDFPS
+
+ override fun apply(constraintSet: ConstraintSet) {
+ defaultIndicationAreaSection.apply(constraintSet)
+ defaultLockIconSection.apply(constraintSet)
+ defaultAmbientIndicationAreaSection.apply(constraintSet)
+ defaultSettingsPopupMenuSection.apply(constraintSet)
+ if (keyguardUpdateMonitor.isUdfpsSupported) {
+ alignShortcutsToUdfpsSection.apply(constraintSet)
+ } else {
+ defaultShortcutsSection.apply(constraintSet)
+ }
+ }
+
+ companion object {
+ const val SHORTCUTS_BESIDE_UDFPS = "shortcutsBesideUdfps"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
new file mode 100644
index 0000000..156b9f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.res.Resources
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.LEFT
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.RIGHT
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.data.repository.KeyguardSection
+import javax.inject.Inject
+
+class AlignShortcutsToUdfpsSection @Inject constructor(@Main private val resources: Resources) :
+ KeyguardSection {
+ override fun apply(constraintSet: ConstraintSet) {
+ val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
+ val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
+
+ constraintSet.apply {
+ constrainWidth(R.id.start_button, width)
+ constrainHeight(R.id.start_button, height)
+ connect(R.id.start_button, LEFT, PARENT_ID, LEFT)
+ connect(R.id.start_button, RIGHT, R.id.lock_icon_view, LEFT)
+ connect(R.id.start_button, TOP, R.id.lock_icon_view, TOP)
+ connect(R.id.start_button, BOTTOM, R.id.lock_icon_view, BOTTOM)
+
+ constrainWidth(R.id.end_button, width)
+ constrainHeight(R.id.end_button, height)
+ connect(R.id.end_button, RIGHT, PARENT_ID, RIGHT)
+ connect(R.id.end_button, LEFT, R.id.lock_icon_view, RIGHT)
+ connect(R.id.end_button, TOP, R.id.lock_icon_view, TOP)
+ connect(R.id.end_button, BOTTOM, R.id.lock_icon_view, BOTTOM)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
new file mode 100644
index 0000000..abf25a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.keyguard.data.repository.KeyguardSection
+import javax.inject.Inject
+
+class DefaultAmbientIndicationAreaSection
+@Inject
+constructor(private val keyguardUpdateMonitor: KeyguardUpdateMonitor) : KeyguardSection {
+ override fun apply(constraintSet: ConstraintSet) {
+ constraintSet.apply {
+ constrainWidth(R.id.ambient_indication_container, MATCH_PARENT)
+
+ if (keyguardUpdateMonitor.isUdfpsSupported) {
+ // constrain below udfps and above indication area
+ constrainHeight(R.id.ambient_indication_container, MATCH_CONSTRAINT)
+ connect(R.id.ambient_indication_container, TOP, R.id.lock_icon_view, BOTTOM)
+ connect(
+ R.id.ambient_indication_container,
+ BOTTOM,
+ R.id.keyguard_indication_area,
+ TOP
+ )
+ connect(R.id.ambient_indication_container, START, PARENT_ID, START)
+ connect(R.id.ambient_indication_container, END, PARENT_ID, END)
+ } else {
+ // constrain above lock icon
+ constrainHeight(R.id.ambient_indication_container, WRAP_CONTENT)
+ connect(R.id.ambient_indication_container, BOTTOM, R.id.lock_icon_view, TOP)
+ connect(R.id.ambient_indication_container, START, PARENT_ID, START)
+ connect(R.id.ambient_indication_container, END, PARENT_ID, END)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
new file mode 100644
index 0000000..dee7ed5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.R
+import com.android.systemui.keyguard.data.repository.KeyguardSection
+import javax.inject.Inject
+
+class DefaultIndicationAreaSection @Inject constructor(private val context: Context) :
+ KeyguardSection {
+ private val indicationAreaViewId = R.id.keyguard_indication_area
+
+ override fun apply(constraintSet: ConstraintSet) {
+ constraintSet.apply {
+ constrainWidth(indicationAreaViewId, ViewGroup.LayoutParams.MATCH_PARENT)
+ constrainHeight(indicationAreaViewId, ViewGroup.LayoutParams.WRAP_CONTENT)
+ connect(
+ indicationAreaViewId,
+ ConstraintSet.BOTTOM,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.BOTTOM,
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_indication_margin_bottom)
+ )
+ connect(
+ indicationAreaViewId,
+ ConstraintSet.START,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.START
+ )
+ connect(
+ indicationAreaViewId,
+ ConstraintSet.END,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.END
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
new file mode 100644
index 0000000..461faec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.graphics.Point
+import android.graphics.Rect
+import android.util.DisplayMetrics
+import android.view.WindowManager
+import androidx.annotation.VisibleForTesting
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.keyguard.data.repository.KeyguardSection
+import javax.inject.Inject
+
+class DefaultLockIconSection
+@Inject
+constructor(
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val authController: AuthController,
+ private val windowManager: WindowManager,
+ private val context: Context,
+) : KeyguardSection {
+ private val lockIconViewId = R.id.lock_icon_view
+
+ override fun apply(constraintSet: ConstraintSet) {
+ val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
+ val scaleFactor: Float = authController.scaleFactor
+ val mBottomPaddingPx =
+ context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
+ val mDefaultPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding)
+ val scaledPadding: Int = (mDefaultPaddingPx * scaleFactor).toInt()
+ val bounds = windowManager.currentWindowMetrics.bounds
+ val widthPixels = bounds.right.toFloat()
+ val heightPixels = bounds.bottom.toFloat()
+ val defaultDensity =
+ DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
+ DisplayMetrics.DENSITY_DEFAULT.toFloat()
+ val lockIconRadiusPx = (defaultDensity * 36).toInt()
+
+ if (isUdfpsSupported) {
+ authController.udfpsLocation?.let { udfpsLocation ->
+ centerLockIcon(
+ udfpsLocation,
+ authController.udfpsRadius,
+ scaledPadding,
+ constraintSet
+ )
+ }
+ } else {
+ centerLockIcon(
+ Point(
+ (widthPixels / 2).toInt(),
+ (heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
+ ),
+ lockIconRadiusPx * scaleFactor,
+ scaledPadding,
+ constraintSet,
+ )
+ }
+ }
+
+ @VisibleForTesting
+ internal fun centerLockIcon(
+ center: Point,
+ radius: Float,
+ drawablePadding: Int,
+ constraintSet: ConstraintSet
+ ) {
+ val sensorRect =
+ Rect().apply {
+ set(
+ center.x - radius.toInt(),
+ center.y - radius.toInt(),
+ center.x + radius.toInt(),
+ center.y + radius.toInt(),
+ )
+ }
+
+ constraintSet.apply {
+ constrainWidth(lockIconViewId, sensorRect.right - sensorRect.left)
+ constrainHeight(lockIconViewId, sensorRect.bottom - sensorRect.top)
+ connect(
+ lockIconViewId,
+ ConstraintSet.TOP,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.TOP,
+ sensorRect.top
+ )
+ connect(
+ lockIconViewId,
+ ConstraintSet.START,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.START,
+ sensorRect.left
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
new file mode 100644
index 0000000..ad1e4f8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.res.Resources
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.data.repository.KeyguardSection
+import javax.inject.Inject
+
+class DefaultSettingsPopupMenuSection @Inject constructor(@Main private val resources: Resources) :
+ KeyguardSection {
+ override fun apply(constraintSet: ConstraintSet) {
+ val horizontalOffsetMargin =
+ resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
+
+ constraintSet.apply {
+ constrainWidth(R.id.keyguard_settings_button, WRAP_CONTENT)
+ constrainHeight(R.id.keyguard_settings_button, WRAP_CONTENT)
+ constrainMinHeight(
+ R.id.keyguard_settings_button,
+ resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
+ )
+ connect(R.id.keyguard_settings_button, START, PARENT_ID, START, horizontalOffsetMargin)
+ connect(R.id.keyguard_settings_button, END, PARENT_ID, END, horizontalOffsetMargin)
+ connect(
+ R.id.keyguard_settings_button,
+ BOTTOM,
+ PARENT_ID,
+ BOTTOM,
+ resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
new file mode 100644
index 0000000..db4653d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.res.Resources
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.LEFT
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.RIGHT
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.data.repository.KeyguardSection
+import javax.inject.Inject
+
+class DefaultShortcutsSection @Inject constructor(@Main private val resources: Resources) :
+ KeyguardSection {
+ override fun apply(constraintSet: ConstraintSet) {
+ val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
+ val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
+ val horizontalOffsetMargin =
+ resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
+ val verticalOffsetMargin =
+ resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
+
+ constraintSet.apply {
+ constrainWidth(R.id.start_button, width)
+ constrainHeight(R.id.start_button, height)
+ connect(R.id.start_button, LEFT, PARENT_ID, LEFT, horizontalOffsetMargin)
+ connect(R.id.start_button, BOTTOM, PARENT_ID, BOTTOM, verticalOffsetMargin)
+
+ constrainWidth(R.id.end_button, width)
+ constrainHeight(R.id.end_button, height)
+ connect(R.id.end_button, RIGHT, PARENT_ID, RIGHT, horizontalOffsetMargin)
+ connect(R.id.end_button, BOTTOM, PARENT_ID, BOTTOM, verticalOffsetMargin)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
new file mode 100644
index 0000000..5e9e553
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import javax.inject.Inject
+
+@SysUISingleton
+class KeyguardBlueprintViewModel
+@Inject
+constructor(keyguardBlueprintInteractor: KeyguardBlueprintInteractor) {
+ val blueprint = keyguardBlueprintInteractor.blueprint
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index b5759e3..cc1504a 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -496,4 +496,12 @@
public static LogBuffer provideDisplayMetricsRepoLogBuffer(LogBufferFactory factory) {
return factory.create("DisplayMetricsRepo", 50);
}
+
+ /** Provides a {@link LogBuffer} for the scene framework. */
+ @Provides
+ @SysUISingleton
+ @SceneFrameworkLog
+ public static LogBuffer provideSceneFrameworkLogBuffer(LogBufferFactory factory) {
+ return factory.create("SceneFramework", 50);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/SceneFrameworkLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/SceneFrameworkLog.kt
new file mode 100644
index 0000000..ef5f4e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/SceneFrameworkLog.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for the Scene Framework. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class SceneFrameworkLog
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index fdb3ddd..20a75f9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -73,7 +73,7 @@
override fun getLayoutResource() = R.layout.media_projection_app_selector
public override fun onCreate(bundle: Bundle?) {
- component = componentFactory.create(view = this, resultHandler = this)
+ component = componentFactory.create(activity = this, view = this, resultHandler = this)
component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
// Create a separate configuration controller for this activity as the configuration
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index b4578e9..11a16a4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -261,7 +261,10 @@
mMediaOutputController.registerLeBroadcastAssistantServiceCallback(mExecutor,
mBroadcastAssistantCallback);
}
- connectBroadcastWithActiveDevice();
+ /* Add local source broadcast to connected capable devices that may be possible receivers
+ * of stream.
+ */
+ startBroadcastWithConnectedDevices();
}
@Override
@@ -394,30 +397,26 @@
}
}
- void connectBroadcastWithActiveDevice() {
+ void startBroadcastWithConnectedDevices() {
//get the Metadata, and convert to BT QR code format.
BluetoothLeBroadcastMetadata broadcastMetadata = getBroadcastMetadata();
if (broadcastMetadata == null) {
Log.e(TAG, "Error: There is no broadcastMetadata.");
return;
}
- MediaDevice mediaDevice = mMediaOutputController.getCurrentConnectedMediaDevice();
- if (mediaDevice == null || !(mediaDevice instanceof BluetoothMediaDevice)
- || !mediaDevice.isBLEDevice()) {
- Log.e(TAG, "Error: There is no active BT LE device.");
- return;
- }
- BluetoothDevice sink = ((BluetoothMediaDevice) mediaDevice).getCachedDevice().getDevice();
- Log.d(TAG, "The broadcastMetadata broadcastId: " + broadcastMetadata.getBroadcastId()
- + ", the device: " + sink.getAnonymizedAddress());
- if (mMediaOutputController.isThereAnyBroadcastSourceIntoSinkDevice(sink)) {
- Log.d(TAG, "The sink device has the broadcast source now.");
- return;
- }
- if (!mMediaOutputController.addSourceIntoSinkDeviceWithBluetoothLeAssistant(sink,
- broadcastMetadata, /*isGroupOp=*/ true)) {
- Log.e(TAG, "Error: Source add failed");
+ for (BluetoothDevice sink : mMediaOutputController.getConnectedBroadcastSinkDevices()) {
+ Log.d(TAG, "The broadcastMetadata broadcastId: " + broadcastMetadata.getBroadcastId()
+ + ", the device: " + sink.getAnonymizedAddress());
+
+ if (mMediaOutputController.isThereAnyBroadcastSourceIntoSinkDevice(sink)) {
+ Log.d(TAG, "The sink device has the broadcast source now.");
+ return;
+ }
+ if (!mMediaOutputController.addSourceIntoSinkDeviceWithBluetoothLeAssistant(sink,
+ broadcastMetadata, /*isGroupOp=*/ false)) {
+ Log.e(TAG, "Error: Source add failed");
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index b6ca0b0..b431bab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -989,7 +989,7 @@
LocalBluetoothLeBroadcast broadcast =
mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
if (broadcast == null) {
- Log.d(TAG, "getBroadcastMetadata: LE Audio Broadcast is null");
+ Log.d(TAG, "getLocalBroadcastMetadataQrCodeString: LE Audio Broadcast is null");
return "";
}
final LocalBluetoothLeBroadcastMetadata metadata =
@@ -1093,11 +1093,23 @@
broadcast.unregisterServiceCallBack(callback);
}
+ List<BluetoothDevice> getConnectedBroadcastSinkDevices() {
+ LocalBluetoothLeBroadcastAssistant assistant =
+ mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+ if (assistant == null) {
+ Log.d(TAG, "getConnectedBroadcastSinkDevices: The broadcast assistant profile is null");
+ return null;
+ }
+
+ return assistant.getConnectedDevices();
+ }
+
boolean isThereAnyBroadcastSourceIntoSinkDevice(BluetoothDevice sink) {
LocalBluetoothLeBroadcastAssistant assistant =
mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
if (assistant == null) {
- Log.d(TAG, "The broadcast assistant profile is null");
+ Log.d(TAG, "isThereAnyBroadcastSourceIntoSinkDevice: The broadcast assistant profile "
+ + "is null");
return false;
}
List<BluetoothLeBroadcastReceiveState> sourceList = assistant.getAllSources(sink);
@@ -1110,7 +1122,8 @@
LocalBluetoothLeBroadcastAssistant assistant =
mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
if (assistant == null) {
- Log.d(TAG, "The broadcast assistant profile is null");
+ Log.d(TAG, "addSourceIntoSinkDeviceWithBluetoothLeAssistant: The broadcast assistant "
+ + "profile is null");
return false;
}
assistant.addSource(sink, metadata, isGroupOp);
@@ -1123,7 +1136,8 @@
LocalBluetoothLeBroadcastAssistant assistant =
mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
if (assistant == null) {
- Log.d(TAG, "The broadcast assistant profile is null");
+ Log.d(TAG, "registerLeBroadcastAssistantServiceCallback: The broadcast assistant "
+ + "profile is null");
return;
}
Log.d(TAG, "Register LE broadcast assistant callback");
@@ -1135,7 +1149,8 @@
LocalBluetoothLeBroadcastAssistant assistant =
mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
if (assistant == null) {
- Log.d(TAG, "The broadcast assistant profile is null");
+ Log.d(TAG, "unregisterLeBroadcastAssistantServiceCallback: The broadcast assistant "
+ + "profile is null");
return;
}
Log.d(TAG, "Unregister LE broadcast assistant callback");
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index e0869ac6..11538fa 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -161,6 +161,7 @@
interface Factory {
/** Create a factory to inject the activity into the graph */
fun create(
+ @BindsInstance activity: MediaProjectionAppSelectorActivity,
@BindsInstance view: MediaProjectionAppSelectorView,
@BindsInstance resultHandler: MediaProjectionAppSelectorResultHandler,
): MediaProjectionAppSelectorComponent
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 03bd11b..9f45f66 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -220,7 +220,7 @@
// If scene framework is enabled, set the scene container window to
// visible and let the touch "slip" into that window.
if (mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
- mSceneInteractor.get().setVisible(true);
+ mSceneInteractor.get().setVisible(true, "swipe down on launcher");
} else {
centralSurfaces.onInputFocusTransfer(
mInputFocusTransferStarted, false /* cancel */,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index b09a5cf..64715bc 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -18,6 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.data.repository.SceneContainerRepository
+import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.RemoteUserInput
import com.android.systemui.scene.shared.model.SceneKey
@@ -41,6 +42,7 @@
@Inject
constructor(
private val repository: SceneContainerRepository,
+ private val logger: SceneLogger,
) {
/**
@@ -54,8 +56,17 @@
}
/** Sets the scene in the container with the given name. */
- fun setCurrentScene(scene: SceneModel) {
+ fun setCurrentScene(scene: SceneModel, loggingReason: String) {
val currentSceneKey = repository.currentScene.value.key
+ if (currentSceneKey == scene.key) {
+ return
+ }
+
+ logger.logSceneChange(
+ from = currentSceneKey,
+ to = scene.key,
+ reason = loggingReason,
+ )
repository.setCurrentScene(scene)
repository.setSceneTransition(from = currentSceneKey, to = scene.key)
}
@@ -64,7 +75,17 @@
val currentScene: StateFlow<SceneModel> = repository.currentScene
/** Sets the visibility of the container with the given name. */
- fun setVisible(isVisible: Boolean) {
+ fun setVisible(isVisible: Boolean, loggingReason: String) {
+ val wasVisible = repository.isVisible.value
+ if (wasVisible == isVisible) {
+ return
+ }
+
+ logger.logVisibilityChange(
+ from = wasVisible,
+ to = isVisible,
+ reason = loggingReason,
+ )
return repository.setVisible(isVisible)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 1c87eb2..20ee393 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -28,6 +28,7 @@
import com.android.systemui.model.SysUiState
import com.android.systemui.model.updateFlags
import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING
@@ -57,13 +58,17 @@
private val featureFlags: FeatureFlags,
private val sysUiState: SysUiState,
@DisplayId private val displayId: Int,
+ private val sceneLogger: SceneLogger,
) : CoreStartable {
override fun start() {
if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
+ sceneLogger.logFrameworkEnabled(isEnabled = true)
hydrateVisibility()
automaticallySwitchScenes()
hydrateSystemUiState()
+ } else {
+ sceneLogger.logFrameworkEnabled(isEnabled = false)
}
}
@@ -73,7 +78,9 @@
sceneInteractor.currentScene
.map { it.key }
.distinctUntilChanged()
- .collect { sceneKey -> sceneInteractor.setVisible(sceneKey != SceneKey.Gone) }
+ .collect { sceneKey ->
+ sceneInteractor.setVisible(sceneKey != SceneKey.Gone, "scene is $sceneKey")
+ }
}
}
@@ -88,10 +95,17 @@
isUnlocked ->
when (currentSceneKey) {
// When the device becomes unlocked in Bouncer, go to Gone.
- is SceneKey.Bouncer -> SceneKey.Gone
+ is SceneKey.Bouncer ->
+ SceneKey.Gone to "device unlocked in Bouncer scene"
// When the device becomes unlocked in Lockscreen, go to Gone if
// bypass is enabled.
- is SceneKey.Lockscreen -> SceneKey.Gone.takeIf { isBypassEnabled }
+ is SceneKey.Lockscreen ->
+ if (isBypassEnabled) {
+ SceneKey.Gone to
+ "device unlocked in Lockscreen scene with bypass"
+ } else {
+ null
+ }
// We got unlocked while on a scene that's not Lockscreen or
// Bouncer, no need to change scenes.
else -> null
@@ -104,13 +118,19 @@
is SceneKey.Bouncer -> null
// We got locked while on a scene that's not Lockscreen or Bouncer,
// go to Lockscreen.
- else -> SceneKey.Lockscreen
+ else ->
+ SceneKey.Lockscreen to "device locked in $currentSceneKey scene"
}
else -> null
}
}
.filterNotNull()
- .collect { targetSceneKey -> switchToScene(targetSceneKey) }
+ .collect { (targetSceneKey, loggingReason) ->
+ switchToScene(
+ targetSceneKey = targetSceneKey,
+ loggingReason = loggingReason,
+ )
+ }
}
applicationScope.launch {
@@ -121,7 +141,16 @@
if (isAsleep) {
// When the device goes to sleep, reset the current scene.
val isUnlocked = authenticationInteractor.isUnlocked.value
- switchToScene(if (isUnlocked) SceneKey.Gone else SceneKey.Lockscreen)
+ val (targetSceneKey, loggingReason) =
+ if (isUnlocked) {
+ SceneKey.Gone to "device is asleep while unlocked"
+ } else {
+ SceneKey.Lockscreen to "device is asleep while locked"
+ }
+ switchToScene(
+ targetSceneKey = targetSceneKey,
+ loggingReason = loggingReason,
+ )
}
}
}
@@ -147,9 +176,10 @@
}
}
- private fun switchToScene(targetSceneKey: SceneKey) {
+ private fun switchToScene(targetSceneKey: SceneKey, loggingReason: String) {
sceneInteractor.setCurrentScene(
scene = SceneModel(targetSceneKey),
+ loggingReason = loggingReason,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
new file mode 100644
index 0000000..0adbd5a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 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.
+ */
+
+package com.android.systemui.scene.shared.logger
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.SceneFrameworkLog
+import com.android.systemui.scene.shared.model.SceneKey
+import javax.inject.Inject
+
+class SceneLogger @Inject constructor(@SceneFrameworkLog private val logBuffer: LogBuffer) {
+
+ fun logFrameworkEnabled(isEnabled: Boolean) {
+ fun asWord(isEnabled: Boolean): String {
+ return if (isEnabled) "enabled" else "disabled"
+ }
+
+ logBuffer.log(
+ tag = TAG,
+ level = LogLevel.INFO,
+ messageInitializer = { bool1 = isEnabled },
+ messagePrinter = { "Scene framework is ${asWord(bool1)}" }
+ )
+ }
+
+ fun logSceneChange(
+ from: SceneKey,
+ to: SceneKey,
+ reason: String,
+ ) {
+ logBuffer.log(
+ tag = TAG,
+ level = LogLevel.INFO,
+ messageInitializer = {
+ str1 = from.toString()
+ str2 = to.toString()
+ str3 = reason
+ },
+ messagePrinter = { "$str1 → $str2, reason: $str3" },
+ )
+ }
+
+ fun logVisibilityChange(
+ from: Boolean,
+ to: Boolean,
+ reason: String,
+ ) {
+ fun asWord(isVisible: Boolean): String {
+ return if (isVisible) "visible" else "invisible"
+ }
+
+ logBuffer.log(
+ tag = TAG,
+ level = LogLevel.INFO,
+ messageInitializer = {
+ str1 = asWord(from)
+ str2 = asWord(to)
+ str3 = reason
+ },
+ messagePrinter = { "$str1 → $str2, reason: $str3" },
+ )
+ }
+
+ companion object {
+ private const val TAG = "SceneFramework"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index bd73e36..b4ebaec 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -53,7 +53,10 @@
/** Requests a transition to the scene with the given key. */
fun setCurrentScene(scene: SceneModel) {
- interactor.setCurrentScene(scene)
+ interactor.setCurrentScene(
+ scene = scene,
+ loggingReason = SCENE_TRANSITION_LOGGING_REASON,
+ )
}
/**
@@ -69,4 +72,8 @@
fun onRemoteUserInput(event: MotionEvent) {
interactor.onRemoteUserInput(RemoteUserInput.translateMotionEvent(event))
}
+
+ companion object {
+ private const val SCENE_TRANSITION_LOGGING_REASON = "user input"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 6afed1d..18e9644 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -61,6 +61,7 @@
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -157,6 +158,7 @@
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
KeyguardTransitionInteractor keyguardTransitionInteractor,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
+ NotificationExpansionRepository notificationExpansionRepository,
FeatureFlags featureFlags,
SystemClock clock,
BouncerMessageInteractor bouncerMessageInteractor,
@@ -201,6 +203,10 @@
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
mLockscreenToDreamingTransition);
+ collectFlow(
+ mView,
+ notificationExpansionRepository.isExpandAnimationRunning(),
+ this::setExpandAnimationRunning);
mClock = clock;
if (featureFlags.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) {
@@ -518,7 +524,7 @@
pw.println(mTouchActive);
}
- public void setExpandAnimationRunning(boolean running) {
+ private void setExpandAnimationRunning(boolean running) {
if (mExpandAnimationRunning != running) {
mExpandAnimationRunning = running;
mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 5c1dd56..9412542 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -155,10 +155,8 @@
largeScreenShadeHeaderActive = LargeScreenUtils.shouldUseLargeScreenShadeHeader(resources)
notificationsBottomMargin = resources.getDimensionPixelSize(
R.dimen.notification_panel_margin_bottom)
- largeScreenShadeHeaderHeight =
- resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
- shadeHeaderHeight =
- resources.getDimensionPixelSize(R.dimen.qs_header_height)
+ largeScreenShadeHeaderHeight = calculateLargeShadeHeaderHeight()
+ shadeHeaderHeight = calculateShadeHeaderHeight()
panelMarginHorizontal = resources.getDimensionPixelSize(
R.dimen.notification_panel_margin_horizontal)
topMargin = if (largeScreenShadeHeaderActive) {
@@ -182,6 +180,23 @@
}
}
+ private fun calculateLargeShadeHeaderHeight(): Int {
+ return resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
+ }
+
+ private fun calculateShadeHeaderHeight(): Int {
+ val minHeight = resources.getDimensionPixelSize(R.dimen.qs_header_height)
+
+ // Following the constraints in xml/qs_header, the total needed height would be the sum of
+ // 1. privacy_container height (R.dimen.large_screen_shade_header_min_height)
+ // 2. carrier_group height (R.dimen.large_screen_shade_header_min_height)
+ // 3. date height (R.dimen.new_qs_header_non_clickable_element_height)
+ val estimatedHeight =
+ 2 * resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height) +
+ resources.getDimensionPixelSize(R.dimen.new_qs_header_non_clickable_element_height)
+ return estimatedHeight.coerceAtLeast(minHeight)
+ }
+
override fun setCustomizerAnimating(animating: Boolean) {
if (isQSCustomizerAnimating != animating) {
isQSCustomizerAnimating = animating
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING b/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING
new file mode 100644
index 0000000..8849d6e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TEST_MAPPING
@@ -0,0 +1,29 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsNotificationTestCases",
+ "options": [
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsNotificationTestCases"
+ }
+ ]
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 798bbe8..c1b905a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -1,10 +1,26 @@
+/*
+ * 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.
+ */
+
package com.android.systemui.statusbar.notification
import android.view.ViewGroup
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.LaunchAnimator
-import com.android.systemui.shade.NotificationShadeWindowViewController
+import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
@@ -17,7 +33,7 @@
/** A provider of [NotificationLaunchAnimatorController]. */
@CentralSurfacesComponent.CentralSurfacesScope
class NotificationLaunchAnimatorControllerProvider @Inject constructor(
- private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
+ private val notificationExpansionRepository: NotificationExpansionRepository,
private val notificationListContainer: NotificationListContainer,
private val headsUpManager: HeadsUpManagerPhone,
private val jankMonitor: InteractionJankMonitor
@@ -28,7 +44,7 @@
onFinishAnimationCallback: Runnable? = null
): NotificationLaunchAnimatorController {
return NotificationLaunchAnimatorController(
- notificationShadeWindowViewController,
+ notificationExpansionRepository,
notificationListContainer,
headsUpManager,
notification,
@@ -44,7 +60,7 @@
* notification expanding into an opening window.
*/
class NotificationLaunchAnimatorController(
- private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
+ private val notificationExpansionRepository: NotificationExpansionRepository,
private val notificationListContainer: NotificationListContainer,
private val headsUpManager: HeadsUpManagerPhone,
private val notification: ExpandableNotificationRow,
@@ -119,7 +135,7 @@
}
override fun onIntentStarted(willAnimate: Boolean) {
- notificationShadeWindowViewController.setExpandAnimationRunning(willAnimate)
+ notificationExpansionRepository.setIsExpandAnimationRunning(willAnimate)
notificationEntry.isExpandAnimationRunning = willAnimate
if (!willAnimate) {
@@ -140,7 +156,7 @@
override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
// TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
// here?
- notificationShadeWindowViewController.setExpandAnimationRunning(false)
+ notificationExpansionRepository.setIsExpandAnimationRunning(false)
notificationEntry.isExpandAnimationRunning = false
removeHun(animate = true)
onFinishAnimationCallback?.run()
@@ -158,7 +174,7 @@
jankMonitor.end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
notification.isExpandAnimationRunning = false
- notificationShadeWindowViewController.setExpandAnimationRunning(false)
+ notificationExpansionRepository.setIsExpandAnimationRunning(false)
notificationEntry.isExpandAnimationRunning = false
notificationListContainer.setExpandingNotification(null)
applyParams(null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt
new file mode 100644
index 0000000..f605bdf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** A repository tracking the status of notification expansion animations. */
+@SysUISingleton
+class NotificationExpansionRepository @Inject constructor() {
+ private val _isExpandAnimationRunning = MutableStateFlow(false)
+
+ /**
+ * Emits true if an animation that expands a notification object into an opening window is
+ * running and false otherwise.
+ *
+ * See [com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController].
+ */
+ val isExpandAnimationRunning: Flow<Boolean> = _isExpandAnimationRunning.asStateFlow()
+
+ /** Sets whether the notification expansion animation is currently running. */
+ fun setIsExpandAnimationRunning(running: Boolean) {
+ _isExpandAnimationRunning.value = running
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 80f5d19..a4e8c2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -21,16 +21,12 @@
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
@@ -75,10 +71,6 @@
@NotificationRowScope
public class ExpandableNotificationRowController implements NotifViewController {
private static final String TAG = "NotifRowController";
-
- static final Uri BUBBLES_SETTING_URI =
- Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BUBBLES);
- private static final String BUBBLES_SETTING_ENABLED_VALUE = "1";
private final ExpandableNotificationRow mView;
private final NotificationListContainer mListContainer;
private final RemoteInputViewSubcomponent.Factory mRemoteInputViewSubcomponentFactory;
@@ -112,23 +104,6 @@
private final ExpandableNotificationRowDragController mDragController;
private final NotificationDismissibilityProvider mDismissibilityProvider;
private final IStatusBarService mStatusBarService;
-
- private final NotificationSettingsController mSettingsController;
-
- @VisibleForTesting
- final NotificationSettingsController.Listener mSettingsListener =
- new NotificationSettingsController.Listener() {
- @Override
- public void onSettingChanged(Uri setting, int userId, String value) {
- if (BUBBLES_SETTING_URI.equals(setting)) {
- final int viewUserId = mView.getEntry().getSbn().getUserId();
- if (viewUserId == UserHandle.USER_ALL || viewUserId == userId) {
- mView.getPrivateLayout().setBubblesEnabledForUser(
- BUBBLES_SETTING_ENABLED_VALUE.equals(value));
- }
- }
- }
- };
private final ExpandableNotificationRow.ExpandableNotificationRowLogger mLoggerCallback =
new ExpandableNotificationRow.ExpandableNotificationRowLogger() {
@Override
@@ -226,7 +201,6 @@
FeatureFlags featureFlags,
PeopleNotificationIdentifier peopleNotificationIdentifier,
Optional<BubblesManager> bubblesManagerOptional,
- NotificationSettingsController settingsController,
ExpandableNotificationRowDragController dragController,
NotificationDismissibilityProvider dismissibilityProvider,
IStatusBarService statusBarService) {
@@ -255,7 +229,6 @@
mFeatureFlags = featureFlags;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
mBubblesManagerOptional = bubblesManagerOptional;
- mSettingsController = settingsController;
mDragController = dragController;
mMetricsLogger = metricsLogger;
mChildrenContainerLogger = childrenContainerLogger;
@@ -325,14 +298,12 @@
NotificationMenuRowPlugin.class, false /* Allow multiple */);
mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD);
mStatusBarStateController.addCallback(mStatusBarStateListener);
- mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
}
@Override
public void onViewDetachedFromWindow(View v) {
mPluginManager.removePluginListener(mView);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
- mSettingsController.removeCallback(BUBBLES_SETTING_URI, mSettingsListener);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 7b6802f..20f4429 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -44,7 +44,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
@@ -66,6 +65,7 @@
import com.android.systemui.statusbar.policy.SmartReplyView;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.util.Compile;
+import com.android.systemui.wmshell.BubblesManager;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -134,7 +134,6 @@
private PeopleNotificationIdentifier mPeopleIdentifier;
private RemoteInputViewSubcomponent.Factory mRemoteInputSubcomponentFactory;
private IStatusBarService mStatusBarService;
- private boolean mBubblesEnabledForUser;
/**
* List of listeners for when content views become inactive (i.e. not the showing view).
@@ -1441,17 +1440,12 @@
}
}
- @Background
- public void setBubblesEnabledForUser(boolean enabled) {
- mBubblesEnabledForUser = enabled;
- }
-
@VisibleForTesting
boolean shouldShowBubbleButton(NotificationEntry entry) {
boolean isPersonWithShortcut =
mPeopleIdentifier.getPeopleNotificationType(entry)
>= PeopleNotificationIdentifier.TYPE_FULL_PERSON;
- return mBubblesEnabledForUser
+ return BubblesManager.areBubblesEnabled(mContext, entry.getSbn().getUser())
&& isPersonWithShortcut
&& entry.getBubbleMetadata() != null;
}
@@ -2085,7 +2079,6 @@
pw.print("null");
}
pw.println();
- pw.println("mBubblesEnabledForUser: " + mBubblesEnabledForUser);
pw.print("RemoteInputViews { ");
pw.print(" visibleType: " + mVisibleType);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
deleted file mode 100644
index 585ff52..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.statusbar.notification.row;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-import javax.inject.Inject;
-
-/**
- * Centralized controller for listening to Secure Settings changes and informing in-process
- * listeners, on a background thread.
- */
-@SysUISingleton
-public class NotificationSettingsController implements Dumpable {
-
- private final static String TAG = "NotificationSettingsController";
- private final UserTracker mUserTracker;
- private final UserTracker.Callback mCurrentUserTrackerCallback;
- private final Handler mHandler;
- private final ContentObserver mContentObserver;
- private final SecureSettings mSecureSettings;
- private final HashMap<Uri, ArrayList<Listener>> mListeners = new HashMap<>();
-
- @Inject
- public NotificationSettingsController(UserTracker userTracker,
- @Background Handler handler,
- SecureSettings secureSettings,
- DumpManager dumpManager) {
- mUserTracker = userTracker;
- mHandler = handler;
- mSecureSettings = secureSettings;
- mContentObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- super.onChange(selfChange, uri);
- synchronized (mListeners) {
- if (mListeners.containsKey(uri)) {
- for (Listener listener : mListeners.get(uri)) {
- notifyListener(listener, uri);
- }
- }
- }
- }
- };
-
- mCurrentUserTrackerCallback = new UserTracker.Callback() {
- @Override
- public void onUserChanged(int newUser, Context userContext) {
- synchronized (mListeners) {
- if (mListeners.size() > 0) {
- mSecureSettings.unregisterContentObserver(mContentObserver);
- for (Uri uri : mListeners.keySet()) {
- mSecureSettings.registerContentObserverForUser(
- uri, false, mContentObserver, newUser);
- }
- }
- }
- }
- };
- mUserTracker.addCallback(mCurrentUserTrackerCallback, new HandlerExecutor(handler));
-
- dumpManager.registerNormalDumpable(TAG, this);
- }
-
- /**
- * Register callback whenever the given secure settings changes.
- *
- * On registration, will call back on the provided handler with the current value of
- * the setting.
- */
- public void addCallback(@NonNull Uri uri, @NonNull Listener listener) {
- if (uri == null || listener == null) {
- return;
- }
- synchronized (mListeners) {
- ArrayList<Listener> currentListeners = mListeners.get(uri);
- if (currentListeners == null) {
- currentListeners = new ArrayList<>();
- }
- if (!currentListeners.contains(listener)) {
- currentListeners.add(listener);
- }
- mListeners.put(uri, currentListeners);
- if (currentListeners.size() == 1) {
- mSecureSettings.registerContentObserverForUser(
- uri, false, mContentObserver, mUserTracker.getUserId());
- }
- }
- mHandler.post(() -> notifyListener(listener, uri));
-
- }
-
- public void removeCallback(Uri uri, Listener listener) {
- synchronized (mListeners) {
- ArrayList<Listener> currentListeners = mListeners.get(uri);
-
- if (currentListeners != null) {
- currentListeners.remove(listener);
- }
- if (currentListeners == null || currentListeners.size() == 0) {
- mListeners.remove(uri);
- }
-
- if (mListeners.size() == 0) {
- mSecureSettings.unregisterContentObserver(mContentObserver);
- }
- }
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- synchronized (mListeners) {
- pw.println("Settings Uri Listener List:");
- for (Uri uri : mListeners.keySet()) {
- pw.println(" Uri=" + uri);
- for (Listener listener : mListeners.get(uri)) {
- pw.println(" Listener=" + listener.getClass().getName());
- }
- }
- }
- }
-
- private void notifyListener(Listener listener, Uri uri) {
- final String setting = uri == null ? null : uri.getLastPathSegment();
- int userId = mUserTracker.getUserId();
- listener.onSettingChanged(uri, userId, mSecureSettings.getStringForUser(setting, userId));
- }
-
- /**
- * Listener invoked whenever settings are changed.
- */
- public interface Listener {
- void onSettingChanged(@NonNull Uri setting, int userId, @Nullable String value);
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index f96fb26..632f241 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -220,6 +220,7 @@
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -312,6 +313,7 @@
private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
private float mTransitionToFullShadeProgress = 0f;
private final NotificationListContainer mNotifListContainer;
+ private final NotificationExpansionRepository mNotificationExpansionRepository;
private boolean mIsShortcutListSearchEnabled;
private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
@@ -725,6 +727,7 @@
NotificationShelfController notificationShelfController,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
NotificationPresenter notificationPresenter,
+ NotificationExpansionRepository notificationExpansionRepository,
DozeParameters dozeParameters,
ScrimController scrimController,
Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
@@ -832,6 +835,7 @@
mStackScroller = mStackScrollerController.getView();
mNotifListContainer = mStackScrollerController.getNotificationListContainer();
mPresenter = notificationPresenter;
+ mNotificationExpansionRepository = notificationExpansionRepository;
mDozeServiceHost = dozeServiceHost;
mPowerManager = powerManager;
mDozeParameters = dozeParameters;
@@ -1546,7 +1550,7 @@
mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback);
mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
- getNotificationShadeWindowViewController(),
+ mNotificationExpansionRepository,
mNotifListContainer,
mHeadsUpManager,
mJankMonitor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 1227287..2affb817 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -150,10 +150,10 @@
fun onTouch(event: MotionEvent) {
if (centralSurfaces.statusBarWindowState == WINDOW_STATE_SHOWING) {
val upOrCancel =
- event.action == MotionEvent.ACTION_UP ||
+ event.action == MotionEvent.ACTION_UP ||
event.action == MotionEvent.ACTION_CANCEL
centralSurfaces.setInteracting(WINDOW_STATUS_BAR,
- !upOrCancel || shadeController.isExpandedVisible)
+ !upOrCancel || shadeController.isExpandedVisible)
}
}
@@ -171,7 +171,7 @@
if (!centralSurfaces.commandQueuePanelsEnabled) {
if (event.action == MotionEvent.ACTION_DOWN) {
Log.v(TAG, String.format("onTouchForwardedFromStatusBar: panel disabled, " +
- "ignoring touch at (${event.x.toInt()},${event.y.toInt()})"))
+ "ignoring touch at (${event.x.toInt()},${event.y.toInt()})"))
}
return false
}
@@ -182,7 +182,7 @@
sceneInteractor.get()
.onRemoteUserInput(RemoteUserInput.translateMotionEvent(event))
// TODO(b/291965119): remove once view is expanded to cover the status bar
- sceneInteractor.get().setVisible(true)
+ sceneInteractor.get().setVisible(true, "swipe down from status bar")
return false
}
@@ -191,11 +191,11 @@
// bar eat the gesture.
if (!shadeViewController.isViewEnabled) {
shadeLogger.logMotionEvent(event,
- "onTouchForwardedFromStatusBar: panel view disabled")
+ "onTouchForwardedFromStatusBar: panel view disabled")
return true
}
if (shadeViewController.isFullyCollapsed &&
- event.y < 1f) {
+ event.y < 1f) {
// b/235889526 Eat events on the top edge of the phone when collapsed
shadeLogger.logMotionEvent(event, "top edge touch ignored")
return true
@@ -257,27 +257,27 @@
view: PhoneStatusBarView
): PhoneStatusBarViewController {
val statusBarMoveFromCenterAnimationController =
- if (featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) {
- unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController()
- } else {
- null
- }
+ if (featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) {
+ unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController()
+ } else {
+ null
+ }
return PhoneStatusBarViewController(
- view,
- progressProvider.getOrNull(),
- centralSurfaces,
- shadeController,
- shadeViewController,
- sceneInteractor,
- shadeLogger,
- statusBarMoveFromCenterAnimationController,
- userChipViewModel,
- viewUtil,
- featureFlags,
- configurationController,
- statusOverlayHoverListenerFactory,
+ view,
+ progressProvider.getOrNull(),
+ centralSurfaces,
+ shadeController,
+ shadeViewController,
+ sceneInteractor,
+ shadeLogger,
+ statusBarMoveFromCenterAnimationController,
+ userChipViewModel,
+ viewUtil,
+ featureFlags,
+ configurationController,
+ statusOverlayHoverListenerFactory,
)
}
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
index f4c5723..cffc833 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
@@ -38,6 +38,19 @@
constructor(
@VerboseMobileViewLog private val buffer: LogBuffer,
) {
+ fun logBinderReceivedVisibility(parentView: View, subId: Int, visibility: Boolean) {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = parentView.getIdForLogging()
+ int1 = subId
+ bool1 = visibility
+ },
+ { "Binder[subId=$int1, viewId=$str1] received visibility: $bool1" },
+ )
+ }
+
fun logBinderReceivedSignalIcon(parentView: View, subId: Int, icon: SignalIconModel) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index c221109..55bc8d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -99,7 +99,16 @@
}
}
- launch { viewModel.isVisible.collect { isVisible -> view.isVisible = isVisible } }
+ launch {
+ viewModel.isVisible.collect { isVisible ->
+ viewModel.verboseLogger?.logBinderReceivedVisibility(
+ view,
+ viewModel.subscriptionId,
+ isVisible
+ )
+ view.isVisible = isVisible
+ }
+ }
// Set the icon for the triangle
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 7aeba66..f0aae0f 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -48,6 +48,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -94,6 +96,7 @@
wakeLockBuilder: WakeLock.Builder,
systemClock: SystemClock,
tempViewUiEventLogger: TemporaryViewUiEventLogger,
+ private val featureFlags: FeatureFlags,
) :
TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>(
context,
@@ -231,14 +234,18 @@
maybeGetAccessibilityFocus(newInfo, currentView)
// ---- Haptics ----
- newInfo.vibrationEffect?.let {
- vibratorHelper.vibrate(
- Process.myUid(),
- context.getApplicationContext().getPackageName(),
- it,
- newInfo.windowTitle,
- VIBRATION_ATTRIBUTES,
- )
+ if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+ vibratorHelper.performHapticFeedback(parent, newInfo.vibrationConstant)
+ } else {
+ newInfo.vibrationEffect?.let {
+ vibratorHelper.vibrate(
+ Process.myUid(),
+ context.getApplicationContext().getPackageName(),
+ it,
+ newInfo.windowTitle,
+ VIBRATION_ATTRIBUTES,
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 1d50241..f24d526 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -17,6 +17,7 @@
package com.android.systemui.temporarydisplay.chipbar
import android.os.VibrationEffect
+import android.view.HapticFeedbackConstants
import android.view.View
import androidx.annotation.AttrRes
import com.android.internal.logging.InstanceId
@@ -42,6 +43,7 @@
val text: Text,
val endItem: ChipbarEndItem?,
val vibrationEffect: VibrationEffect? = null,
+ val vibrationConstant: Int = HapticFeedbackConstants.NO_HAPTICS,
val allowSwipeToDismiss: Boolean = false,
override val windowTitle: String,
override val wakeReason: String,
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index 4da5d49..de9b5ee 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -157,9 +157,10 @@
* Query the wallet cards from {@link QuickAccessWalletClient}.
*
* @param cardsRetriever a callback to retrieve wallet cards.
+ * @param maxCards the maximum number of cards requested from the QuickAccessWallet
*/
public void queryWalletCards(
- QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) {
+ QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever, int maxCards) {
if (mClock.elapsedRealtime() - mQawClientCreatedTimeMillis
> RECREATION_TIME_WINDOW) {
Log.i(TAG, "Re-creating the QAW client to avoid stale.");
@@ -175,11 +176,22 @@
mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height);
int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size);
GetWalletCardsRequest request =
- new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1);
+ new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, maxCards);
mQuickAccessWalletClient.getWalletCards(mBgExecutor, request, cardsRetriever);
}
/**
+ * Query the wallet cards from {@link QuickAccessWalletClient}.
+ *
+ * @param cardsRetriever a callback to retrieve wallet cards.
+ */
+ public void queryWalletCards(
+ QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) {
+ queryWalletCards(cardsRetriever, /* maxCards= */ 1);
+ }
+
+
+ /**
* Re-create the {@link QuickAccessWalletClient} of the controller.
*/
public void reCreateWalletClient() {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
index b3ad9b0..75df1bd 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
@@ -39,7 +39,6 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
@@ -88,7 +87,7 @@
QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE
)
walletController.updateWalletPreference()
- walletController.queryWalletCards(callback)
+ walletController.queryWalletCards(callback, MAX_CARDS)
awaitClose {
walletController.unregisterWalletChangeObservers(
@@ -152,5 +151,6 @@
companion object {
private const val TAG = "WalletSuggestions"
+ private const val MAX_CARDS = 50
}
}
diff --git a/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleTest.kt b/packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
similarity index 67%
copy from packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleTest.kt
copy to packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
index e7738af..0fe2283 100644
--- a/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleTest.kt
+++ b/packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
@@ -13,29 +13,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package androidx.core.animation
+package android.animation
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.core.animation.doOnEnd
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.doOnEnd
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+/**
+ * This test class validates that two tests' animators are isolated from each other when using the
+ * same animator test rule. This is a test to prevent future instances of b/275602127.
+ */
@RunWith(AndroidTestingRunner::class)
@SmallTest
-@RunWithLooper(setAsMainLooper = true)
-class AnimatorTestRuleTest : SysuiTestCase() {
+@RunWithLooper
+class AnimatorTestRuleIsolationTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule()
@Test
fun testA() {
+ // GIVEN global state is reset at the start of the test
didTouchA = false
didTouchB = false
+ // WHEN starting 2 animations of different durations, and setting didTouchA at the end
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 100
doOnEnd { didTouchA = true }
@@ -46,15 +52,20 @@
doOnEnd { didTouchA = true }
start()
}
+ // WHEN when you advance time so that only one of the animations has ended
animatorTestRule.advanceTimeBy(100)
+ // VERIFY we did indeed end the current animation
assertThat(didTouchA).isTrue()
+ // VERIFY advancing the animator did NOT cause testB's animator to end
assertThat(didTouchB).isFalse()
}
@Test
fun testB() {
+ // GIVEN global state is reset at the start of the test
didTouchA = false
didTouchB = false
+ // WHEN starting 2 animations of different durations, and setting didTouchB at the end
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 100
doOnEnd { didTouchB = true }
@@ -66,7 +77,9 @@
start()
}
animatorTestRule.advanceTimeBy(100)
+ // VERIFY advancing the animator did NOT cause testA's animator to end
assertThat(didTouchA).isFalse()
+ // VERIFY we did indeed end the current animation
assertThat(didTouchB).isTrue()
}
diff --git a/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt b/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
new file mode 100644
index 0000000..cc7f7e4
--- /dev/null
+++ b/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+package android.animation
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.core.animation.doOnEnd
+import androidx.test.filters.SmallTest
+import com.android.app.animation.Interpolators
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class AnimatorTestRulePrecisionTest : SysuiTestCase() {
+
+ @get:Rule val animatorTestRule = AnimatorTestRule()
+
+ var value1: Float = -1f
+ var value2: Float = -1f
+
+ private inline fun animateThis(
+ propertyName: String,
+ duration: Long,
+ startDelay: Long = 0,
+ crossinline onEndAction: (animator: Animator) -> Unit,
+ ) {
+ ObjectAnimator.ofFloat(this, propertyName, 0f, 1f).also {
+ it.interpolator = Interpolators.LINEAR
+ it.duration = duration
+ it.startDelay = startDelay
+ it.doOnEnd(onEndAction)
+ it.start()
+ }
+ }
+
+ @Test
+ fun testSingleAnimator() {
+ var ended = false
+ animateThis("value1", duration = 100) { ended = true }
+
+ assertThat(value1).isEqualTo(0f)
+ assertThat(ended).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(50)
+ assertThat(value1).isEqualTo(0.5f)
+ assertThat(ended).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(49)
+ assertThat(value1).isEqualTo(0.99f)
+ assertThat(ended).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(1)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(ended).isTrue()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(0)
+ }
+
+ @Test
+ fun testDelayedAnimator() {
+ var ended = false
+ animateThis("value1", duration = 100, startDelay = 50) { ended = true }
+
+ assertThat(value1).isEqualTo(-1f)
+ assertThat(ended).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(49)
+ assertThat(value1).isEqualTo(-1f)
+ assertThat(ended).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(1)
+ assertThat(value1).isEqualTo(0f)
+ assertThat(ended).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(99)
+ assertThat(value1).isEqualTo(0.99f)
+ assertThat(ended).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(1)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(ended).isTrue()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(0)
+ }
+
+ @Test
+ fun testTwoAnimators() {
+ var ended1 = false
+ var ended2 = false
+ animateThis("value1", duration = 100) { ended1 = true }
+ animateThis("value2", duration = 200) { ended2 = true }
+ assertThat(value1).isEqualTo(0f)
+ assertThat(value2).isEqualTo(0f)
+ assertThat(ended1).isFalse()
+ assertThat(ended2).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(2)
+
+ animatorTestRule.advanceTimeBy(99)
+ assertThat(value1).isEqualTo(0.99f)
+ assertThat(value2).isEqualTo(0.495f)
+ assertThat(ended1).isFalse()
+ assertThat(ended2).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(2)
+
+ animatorTestRule.advanceTimeBy(1)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(0.5f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(99)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(0.995f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(1)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(1f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isTrue()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(0)
+ }
+
+ @Test
+ fun testChainedAnimators() {
+ var ended1 = false
+ var ended2 = false
+ animateThis("value1", duration = 100) {
+ ended1 = true
+ animateThis("value2", duration = 100) { ended2 = true }
+ }
+
+ assertThat(value1).isEqualTo(0f)
+ assertThat(value2).isEqualTo(-1f)
+ assertThat(ended1).isFalse()
+ assertThat(ended2).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(99)
+ assertThat(value1).isEqualTo(0.99f)
+ assertThat(value2).isEqualTo(-1f)
+ assertThat(ended1).isFalse()
+ assertThat(ended2).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(1)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(0f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(99)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(0.99f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isFalse()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(1)
+
+ animatorTestRule.advanceTimeBy(1)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(1f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isTrue()
+ assertThat(AnimationHandler.getAnimationCount()).isEqualTo(0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleTest.kt b/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt
similarity index 69%
rename from packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleTest.kt
rename to packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt
index e7738af..2d84fba 100644
--- a/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleTest.kt
+++ b/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt
@@ -25,17 +25,23 @@
import org.junit.Test
import org.junit.runner.RunWith
+/**
+ * This test class validates that two tests' animators are isolated from each other when using the
+ * same animator test rule. This is a test to prevent future instances of b/275602127.
+ */
@RunWith(AndroidTestingRunner::class)
@SmallTest
-@RunWithLooper(setAsMainLooper = true)
-class AnimatorTestRuleTest : SysuiTestCase() {
+@RunWithLooper
+class AnimatorTestRuleIsolationTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule()
@Test
fun testA() {
+ // GIVEN global state is reset at the start of the test
didTouchA = false
didTouchB = false
+ // WHEN starting 2 animations of different durations, and setting didTouchA at the end
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 100
doOnEnd { didTouchA = true }
@@ -46,15 +52,20 @@
doOnEnd { didTouchA = true }
start()
}
+ // WHEN when you advance time so that only one of the animations has ended
animatorTestRule.advanceTimeBy(100)
+ // VERIFY we did indeed end the current animation
assertThat(didTouchA).isTrue()
+ // VERIFY advancing the animator did NOT cause testB's animator to end
assertThat(didTouchB).isFalse()
}
@Test
fun testB() {
+ // GIVEN global state is reset at the start of the test
didTouchA = false
didTouchB = false
+ // WHEN starting 2 animations of different durations, and setting didTouchB at the end
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 100
doOnEnd { didTouchB = true }
@@ -66,7 +77,9 @@
start()
}
animatorTestRule.advanceTimeBy(100)
+ // VERIFY advancing the animator did NOT cause testA's animator to end
assertThat(didTouchA).isFalse()
+ // VERIFY we did indeed end the current animation
assertThat(didTouchB).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index e447c29..efb981e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -733,20 +733,20 @@
// is
// not enough to trigger a dismissal of the keyguard.
underTest.onViewAttached()
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer, null))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer, null), "reason")
runCurrent()
verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt())
// While listening, going from the bouncer scene to the gone scene, does dismiss the
// keyguard.
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone, null))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone, null), "reason")
runCurrent()
verify(viewMediatorCallback).keyguardDone(anyBoolean(), anyInt())
// While listening, moving back to the bouncer scene does not dismiss the keyguard
// again.
clearInvocations(viewMediatorCallback)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer, null))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer, null), "reason")
runCurrent()
verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt())
@@ -754,12 +754,12 @@
// scene
// does not dismiss the keyguard while we're not listening.
underTest.onViewDetached()
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone, null))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone, null), "reason")
runCurrent()
verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt())
// While not listening, moving back to the bouncer does not dismiss the keyguard.
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer, null))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer, null), "reason")
runCurrent()
verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt())
@@ -767,7 +767,7 @@
// gone
// scene now does dismiss the keyguard again.
underTest.onViewAttached()
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone, null))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone, null), "reason")
runCurrent()
verify(viewMediatorCallback).keyguardDone(anyBoolean(), anyInt())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index 44a2b68..ba27fcd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -25,12 +25,12 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import androidx.core.animation.AnimatorTestRule;
import androidx.core.animation.ObjectAnimator;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.NotificationMediaManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
new file mode 100644
index 0000000..a7e7dd0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.animation
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.core.animation.doOnEnd
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.doOnEnd
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class AnimatorTestRuleOrderTest : SysuiTestCase() {
+
+ @get:Rule val animatorTestRule = AnimatorTestRule()
+
+ var value1: Float = -1f
+ var value2: Float = -1f
+
+ private inline fun animateThisX(
+ propertyName: String,
+ duration: Long,
+ startDelay: Long = 0,
+ crossinline onEndAction: () -> Unit,
+ ) {
+ androidx.core.animation.ObjectAnimator.ofFloat(this, propertyName, 0f, 1f).also {
+ it.interpolator = null
+ it.duration = duration
+ it.startDelay = startDelay
+ it.doOnEnd { onEndAction() }
+ it.start()
+ }
+ }
+
+ private inline fun animateThisP(
+ propertyName: String,
+ duration: Long,
+ startDelay: Long = 0,
+ crossinline onEndAction: () -> Unit,
+ ) {
+ android.animation.ObjectAnimator.ofFloat(this, propertyName, 0f, 1f).also {
+ it.interpolator = null
+ it.duration = duration
+ it.startDelay = startDelay
+ it.doOnEnd { onEndAction() }
+ it.start()
+ }
+ }
+
+ @Test
+ fun testTwoAnimators() {
+ var ended1 = false
+ var ended2 = false
+ animateThisP("value1", duration = 100) { ended1 = true }
+ animateThisX("value2", duration = 200) { ended2 = true }
+ assertThat(value1).isEqualTo(0f)
+ assertThat(value2).isEqualTo(0f)
+ assertThat(ended1).isFalse()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(99)
+ assertThat(value1).isEqualTo(0.99f)
+ assertThat(value2).isEqualTo(0.495f)
+ assertThat(ended1).isFalse()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(1)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(0.5f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(99)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(0.995f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(1)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(1f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isTrue()
+ }
+
+ @Test
+ fun testChainedAnimatorsPlatformThenX() {
+ var ended1 = false
+ var ended2 = false
+ animateThisP("value1", duration = 100) {
+ ended1 = true
+ animateThisX("value2", duration = 100) { ended2 = true }
+ }
+
+ assertThat(value1).isEqualTo(0f)
+ assertThat(value2).isEqualTo(-1f)
+ assertThat(ended1).isFalse()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(50)
+ assertThat(value1).isEqualTo(0.5f)
+ assertThat(value2).isEqualTo(-1f)
+ assertThat(ended1).isFalse()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(50)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(0f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(50)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(0.5f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(50)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(1f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isTrue()
+ }
+
+ @Test
+ fun testChainedAnimatorsXThenPlatform() {
+ var ended1 = false
+ var ended2 = false
+ animateThisX("value1", duration = 100) {
+ ended1 = true
+ animateThisP("value2", duration = 100) { ended2 = true }
+ }
+
+ assertThat(value1).isEqualTo(0f)
+ assertThat(value2).isEqualTo(-1f)
+ assertThat(ended1).isFalse()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(50)
+ assertThat(value1).isEqualTo(0.5f)
+ assertThat(value2).isEqualTo(-1f)
+ assertThat(ended1).isFalse()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(50)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(0f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(50)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(0.5f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isFalse()
+
+ animatorTestRule.advanceTimeBy(50)
+ assertThat(value1).isEqualTo(1f)
+ assertThat(value2).isEqualTo(1f)
+ assertThat(ended1).isTrue()
+ assertThat(ended2).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
index 9cb3b1a..60c6e1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
@@ -23,6 +23,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -86,6 +87,7 @@
private final ArgumentCaptor<Notification> mNotificationArgumentCaptor =
ArgumentCaptor.forClass(Notification.class);
+ private BiometricNotificationService mBiometricNotificationService;
private TestableLooper mLooper;
private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
private KeyguardStateController.Callback mKeyguardStateControllerCallback;
@@ -104,7 +106,7 @@
BiometricNotificationDialogFactory dialogFactory = new BiometricNotificationDialogFactory();
BiometricNotificationBroadcastReceiver broadcastReceiver =
new BiometricNotificationBroadcastReceiver(mContext, dialogFactory);
- BiometricNotificationService biometricNotificationService =
+ mBiometricNotificationService =
new BiometricNotificationService(mContext,
mKeyguardUpdateMonitor, mKeyguardStateController, handler,
mNotificationManager,
@@ -112,7 +114,7 @@
mFingerprintReEnrollNotificationOptional,
mFingerprintManager,
mFaceManager);
- biometricNotificationService.start();
+ mBiometricNotificationService.start();
ArgumentCaptor<KeyguardUpdateMonitorCallback> updateMonitorCallbackArgumentCaptor =
ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
@@ -231,4 +233,24 @@
eq(UserHandle.CURRENT));
}
+ @Test
+ public void testResetFaceUnlockReEnroll_onStart() {
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ mKeyguardUpdateMonitorCallback.onBiometricError(
+ BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL,
+ "Testing Face Re-enrollment" /* errString */,
+ BiometricSourceType.FACE
+ );
+
+ mBiometricNotificationService.start();
+ mKeyguardStateControllerCallback.onKeyguardShowingChanged();
+
+ mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS);
+ mLooper.processAllMessages();
+
+ verify(mNotificationManager, never()).notifyAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID),
+ mNotificationArgumentCaptor.capture(), any());
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt
index 992ee1a..efae3fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt
@@ -59,63 +59,79 @@
}
@Test
- fun bouncerMessages_choosesTheRightMessage_basedOnSecurityModeAndFpAllowedInBouncer() =
+ fun bouncerMessages_choosesTheRightMessage_basedOnSecurityModeAndFpAuthIsAllowed() =
testScope.runTest {
- primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAllowedInBouncer = false)
+ primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAuthAllowed = false)
.isEqualTo("Enter PIN")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAllowedInBouncer = true)
+ primaryMessage(PROMPT_REASON_DEFAULT, mode = PIN, fpAuthAllowed = true)
.isEqualTo("Unlock with PIN or fingerprint")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAllowedInBouncer = false)
+ primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAuthAllowed = false)
.isEqualTo("Enter password")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAllowedInBouncer = true)
+ primaryMessage(PROMPT_REASON_DEFAULT, mode = Password, fpAuthAllowed = true)
.isEqualTo("Unlock with password or fingerprint")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAllowedInBouncer = false)
+ primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAuthAllowed = false)
.isEqualTo("Draw pattern")
- primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAllowedInBouncer = true)
+ primaryMessage(PROMPT_REASON_DEFAULT, mode = Pattern, fpAuthAllowed = true)
.isEqualTo("Unlock with pattern or fingerprint")
}
@Test
- fun bouncerMessages_setsPrimaryAndSecondaryMessage_basedOnSecurityModeAndFpAllowedInBouncer() =
+ fun bouncerMessages_overridesSecondaryMessageValue() =
+ testScope.runTest {
+ val bouncerMessageModel =
+ bouncerMessageModel(
+ PIN,
+ true,
+ PROMPT_REASON_DEFAULT,
+ secondaryMessageOverride = "face acquisition message"
+ )!!
+ assertThat(context.resources.getString(bouncerMessageModel.message!!.messageResId!!))
+ .isEqualTo("Unlock with PIN or fingerprint")
+ assertThat(bouncerMessageModel.secondaryMessage!!.message!!)
+ .isEqualTo("face acquisition message")
+ }
+
+ @Test
+ fun bouncerMessages_setsPrimaryAndSecondaryMessage_basedOnSecurityModeAndFpAuthIsAllowed() =
testScope.runTest {
primaryMessage(
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
mode = PIN,
- fpAllowedInBouncer = true
+ fpAuthAllowed = true
)
.isEqualTo("Wrong PIN. Try again.")
secondaryMessage(
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
mode = PIN,
- fpAllowedInBouncer = true
+ fpAuthAllowed = true
)
.isEqualTo("Or unlock with fingerprint")
primaryMessage(
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
mode = Password,
- fpAllowedInBouncer = true
+ fpAuthAllowed = true
)
.isEqualTo("Wrong password. Try again.")
secondaryMessage(
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
mode = Password,
- fpAllowedInBouncer = true
+ fpAuthAllowed = true
)
.isEqualTo("Or unlock with fingerprint")
primaryMessage(
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
mode = Pattern,
- fpAllowedInBouncer = true
+ fpAuthAllowed = true
)
.isEqualTo("Wrong pattern. Try again.")
secondaryMessage(
PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
mode = Pattern,
- fpAllowedInBouncer = true
+ fpAuthAllowed = true
)
.isEqualTo("Or unlock with fingerprint")
}
@@ -123,11 +139,11 @@
private fun primaryMessage(
reason: Int,
mode: KeyguardSecurityModel.SecurityMode,
- fpAllowedInBouncer: Boolean
+ fpAuthAllowed: Boolean
): StringSubject {
return assertThat(
context.resources.getString(
- bouncerMessageModel(mode, fpAllowedInBouncer, reason)!!.message!!.messageResId!!
+ bouncerMessageModel(mode, fpAuthAllowed, reason)!!.message!!.messageResId!!
)
)!!
}
@@ -135,25 +151,28 @@
private fun secondaryMessage(
reason: Int,
mode: KeyguardSecurityModel.SecurityMode,
- fpAllowedInBouncer: Boolean
+ fpAuthAllowed: Boolean
): StringSubject {
return assertThat(
context.resources.getString(
- bouncerMessageModel(mode, fpAllowedInBouncer, reason)!!
- .secondaryMessage!!
- .messageResId!!
+ bouncerMessageModel(mode, fpAuthAllowed, reason)!!.secondaryMessage!!.messageResId!!
)
)!!
}
private fun bouncerMessageModel(
mode: KeyguardSecurityModel.SecurityMode,
- fpAllowedInBouncer: Boolean,
- reason: Int
+ fpAuthAllowed: Boolean,
+ reason: Int,
+ secondaryMessageOverride: String? = null,
): BouncerMessageModel? {
whenever(securityModel.getSecurityMode(0)).thenReturn(mode)
- whenever(updateMonitor.isFingerprintAllowedInBouncer).thenReturn(fpAllowedInBouncer)
+ whenever(updateMonitor.isUnlockingWithFingerprintAllowed).thenReturn(fpAuthAllowed)
- return underTest.createFromPromptReason(reason, 0)
+ return underTest.createFromPromptReason(
+ reason,
+ 0,
+ secondaryMsgOverride = secondaryMessageOverride
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt
index de712da..2be7d8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt
@@ -51,6 +51,7 @@
import com.android.systemui.bouncer.shared.model.BouncerMessageModel
import com.android.systemui.bouncer.shared.model.Message
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
@@ -79,6 +80,7 @@
@Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var securityModel: KeyguardSecurityModel
+ @Mock private lateinit var systemPropertiesHelper: SystemPropertiesHelper
@Captor
private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -99,7 +101,7 @@
fingerprintRepository = FakeDeviceEntryFingerprintAuthRepository()
testScope = TestScope()
- whenever(updateMonitor.isFingerprintAllowedInBouncer).thenReturn(false)
+ whenever(updateMonitor.isUnlockingWithFingerprintAllowed).thenReturn(false)
whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN)
underTest =
BouncerMessageRepositoryImpl(
@@ -108,7 +110,8 @@
updateMonitor = updateMonitor,
bouncerMessageFactory = BouncerMessageFactory(updateMonitor, securityModel),
userRepository = userRepository,
- fingerprintAuthRepository = fingerprintRepository
+ fingerprintAuthRepository = fingerprintRepository,
+ systemPropertiesHelper = systemPropertiesHelper
)
}
@@ -214,6 +217,21 @@
}
@Test
+ fun onRestartForMainlineUpdate_shouldProvideRelevantMessage() =
+ testScope.runTest {
+ whenever(systemPropertiesHelper.get("sys.boot.reason.last"))
+ .thenReturn("reboot,mainline_update")
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ biometricSettingsRepository.setFaceEnrolled(true)
+ biometricSettingsRepository.setIsFaceAuthEnabled(true)
+
+ verifyMessagesForAuthFlag(
+ STRONG_AUTH_REQUIRED_AFTER_BOOT to
+ Pair(keyguard_enter_pin, R.string.kg_prompt_after_update_pin),
+ )
+ }
+
+ @Test
fun onAuthFlagsChanged_withTrustNotManagedAndNoBiometrics_isANoop() =
testScope.runTest {
userRepository.setSelectedUserInfo(PRIMARY_USER)
@@ -345,8 +363,8 @@
private fun message(primaryResId: Int, secondaryResId: Int): BouncerMessageModel {
return BouncerMessageModel(
- message = Message(messageResId = primaryResId),
- secondaryMessage = Message(messageResId = secondaryResId)
+ message = Message(messageResId = primaryResId, animate = false),
+ secondaryMessage = Message(messageResId = secondaryResId, animate = false)
)
}
private fun message(value: String): BouncerMessageModel {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index 8e5256e..3ca94aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -76,7 +76,7 @@
allowTestableLooperAsMainThread()
whenever(securityModel.getSecurityMode(PRIMARY_USER_ID)).thenReturn(PIN)
- whenever(updateMonitor.isFingerprintAllowedInBouncer).thenReturn(false)
+ whenever(updateMonitor.isUnlockingWithFingerprintAllowed).thenReturn(false)
}
suspend fun TestScope.init() {
@@ -150,11 +150,12 @@
underTest.setCustomMessage("not empty")
- assertThat(repository.customMessage.value)
- .isEqualTo(BouncerMessageModel(secondaryMessage = Message(message = "not empty")))
+ val customMessage = repository.customMessage
+ assertThat(customMessage.value!!.message!!.messageResId).isEqualTo(keyguard_enter_pin)
+ assertThat(customMessage.value!!.secondaryMessage!!.message).isEqualTo("not empty")
underTest.setCustomMessage(null)
- assertThat(repository.customMessage.value).isNull()
+ assertThat(customMessage.value).isNull()
}
@Test
@@ -164,11 +165,15 @@
underTest.setFaceAcquisitionMessage("not empty")
- assertThat(repository.faceAcquisitionMessage.value)
- .isEqualTo(BouncerMessageModel(secondaryMessage = Message(message = "not empty")))
+ val faceAcquisitionMessage = repository.faceAcquisitionMessage
+
+ assertThat(faceAcquisitionMessage.value!!.message!!.messageResId)
+ .isEqualTo(keyguard_enter_pin)
+ assertThat(faceAcquisitionMessage.value!!.secondaryMessage!!.message)
+ .isEqualTo("not empty")
underTest.setFaceAcquisitionMessage(null)
- assertThat(repository.faceAcquisitionMessage.value).isNull()
+ assertThat(faceAcquisitionMessage.value).isNull()
}
@Test
@@ -178,11 +183,15 @@
underTest.setFingerprintAcquisitionMessage("not empty")
- assertThat(repository.fingerprintAcquisitionMessage.value)
- .isEqualTo(BouncerMessageModel(secondaryMessage = Message(message = "not empty")))
+ val fingerprintAcquisitionMessage = repository.fingerprintAcquisitionMessage
+
+ assertThat(fingerprintAcquisitionMessage.value!!.message!!.messageResId)
+ .isEqualTo(keyguard_enter_pin)
+ assertThat(fingerprintAcquisitionMessage.value!!.secondaryMessage!!.message)
+ .isEqualTo("not empty")
underTest.setFingerprintAcquisitionMessage(null)
- assertThat(repository.fingerprintAcquisitionMessage.value).isNull()
+ assertThat(fingerprintAcquisitionMessage.value).isNull()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 1f089ca..7f8d54c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -79,7 +79,7 @@
AuthenticationMethodModel.Password
)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
@@ -99,7 +99,7 @@
AuthenticationMethodModel.Password
)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
@@ -119,7 +119,7 @@
AuthenticationMethodModel.Password
)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPasswordInputChanged("password")
@@ -139,7 +139,7 @@
AuthenticationMethodModel.Password
)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPasswordInputChanged("wrong")
@@ -161,7 +161,7 @@
AuthenticationMethodModel.Password
)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPasswordInputChanged("wrong")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index af54989..57fcbe5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -83,7 +83,7 @@
AuthenticationMethodModel.Pattern
)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
@@ -105,7 +105,7 @@
AuthenticationMethodModel.Pattern
)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
@@ -128,7 +128,7 @@
AuthenticationMethodModel.Pattern
)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onDragStart()
@@ -176,7 +176,7 @@
AuthenticationMethodModel.Pattern
)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onDragStart()
@@ -208,7 +208,7 @@
AuthenticationMethodModel.Pattern
)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onDragStart()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index c12ed03..81c68ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -79,7 +79,7 @@
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
@@ -97,7 +97,7 @@
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
@@ -117,7 +117,7 @@
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
@@ -138,7 +138,7 @@
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
@@ -161,7 +161,7 @@
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
@@ -183,7 +183,7 @@
val currentScene by collectLastValue(sceneInteractor.currentScene)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
@@ -203,7 +203,7 @@
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPinButtonClicked(1)
@@ -227,7 +227,7 @@
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
underTest.onPinButtonClicked(1)
@@ -258,7 +258,7 @@
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
utils.authenticationRepository.setAutoConfirmEnabled(true)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
@@ -277,7 +277,7 @@
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
utils.authenticationRepository.setAutoConfirmEnabled(true)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
FakeAuthenticationRepository.DEFAULT_PIN.dropLast(1).forEach { digit ->
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
index a341ca3..8a1b094 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
@@ -23,6 +23,7 @@
/**
* Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests.
*/
+@JvmOverloads
fun logcatLogBuffer(name: String = "EchoToLogcatLogBuffer") =
LogBuffer(name, 50, LogcatEchoTrackerAlways())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
new file mode 100644
index 0000000..66ead14
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class KeyguardBlueprintRepositoryTest : SysuiTestCase() {
+ private lateinit var underTest: KeyguardBlueprintRepository
+ @Mock lateinit var configurationRepository: ConfigurationRepository
+ @Mock lateinit var defaultLockscreenBlueprint: DefaultKeyguardBlueprint
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+ private val configurationFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(defaultLockscreenBlueprint.id).thenReturn(DEFAULT)
+ whenever(configurationRepository.onAnyConfigurationChange).thenReturn(configurationFlow)
+ underTest =
+ KeyguardBlueprintRepository(
+ configurationRepository,
+ setOf(defaultLockscreenBlueprint),
+ testScope.backgroundScope,
+ )
+ }
+
+ @Test
+ fun testApplyBlueprint_DefaultLayout() {
+ testScope.runTest {
+ val blueprint by collectLastValue(underTest.blueprint)
+ runCurrent()
+ underTest.applyBlueprint(defaultLockscreenBlueprint)
+ runCurrent()
+ assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
+ }
+ }
+
+ @Test
+ fun testConfigurationChange() {
+ testScope.runTest {
+ val blueprint by collectLastValue(underTest.blueprint)
+ runCurrent()
+ configurationFlow.tryEmit(Unit)
+ runCurrent()
+ assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
+ }
+ }
+
+ @Test
+ fun testRefreshBlueprint() {
+ testScope.runTest {
+ val blueprint by collectLastValue(underTest.blueprint)
+ runCurrent()
+ underTest.refreshBlueprint()
+ runCurrent()
+ assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
+ }
+ }
+
+ @Test
+ fun testTransitionToLayout_validId() {
+ assertThat(underTest.applyBlueprint(DEFAULT)).isTrue()
+ }
+ @Test
+ fun testTransitionToLayout_invalidId() {
+ assertThat(underTest.applyBlueprint("abc")).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index ec30732..dcaafe8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -19,10 +19,10 @@
import android.graphics.Point
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import androidx.core.animation.AnimatorTestRule
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
new file mode 100644
index 0000000..b8a2e9d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardBlueprintInteractorTest : SysuiTestCase() {
+ private lateinit var underTest: KeyguardBlueprintInteractor
+
+ @Mock private lateinit var keyguardBlueprintRepository: KeyguardBlueprintRepository
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = KeyguardBlueprintInteractor(keyguardBlueprintRepository)
+ }
+
+ @Test
+ fun testRefreshBlueprint() {
+ underTest.refreshBlueprint()
+ verify(keyguardBlueprintRepository).refreshBlueprint()
+ }
+
+ @Test
+ fun testTransitionToBlueprint() {
+ underTest.transitionToBlueprint("abc")
+ verify(keyguardBlueprintRepository).applyBlueprint("abc")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
index d825c2a..86e56bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
@@ -130,11 +130,11 @@
fun switchFromLockScreenToGone_authMethodNotSwipe_doesNotUnlockDevice() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Lockscreen))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Lockscreen), "reason")
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(isUnlocked).isFalse()
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone), "reason")
assertThat(isUnlocked).isFalse()
}
@@ -144,13 +144,13 @@
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
runCurrent()
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Shade))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Shade), "reason")
runCurrent()
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
runCurrent()
assertThat(isUnlocked).isFalse()
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone), "reason")
assertThat(isUnlocked).isFalse()
}
@@ -161,7 +161,7 @@
val currentScene by collectLastValue(sceneInteractor.currentScene)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
runCurrent()
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.QuickSettings))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.QuickSettings), "reason")
runCurrent()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.QuickSettings))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt
deleted file mode 100644
index c771356..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.
- *
- */
-
-package com.android.systemui.keyguard.ui.view.layout
-
-import android.graphics.Point
-import android.view.ViewGroup
-import android.view.WindowManager
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.AuthController
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Answers
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@RunWith(JUnit4::class)
-@SmallTest
-class DefaultLockscreenLayoutTest : SysuiTestCase() {
- private lateinit var defaultLockscreenLayout: DefaultLockscreenLayout
- private lateinit var rootView: KeyguardRootView
- @Mock private lateinit var authController: AuthController
- @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
-
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- rootView = KeyguardRootView(context, null)
- defaultLockscreenLayout =
- DefaultLockscreenLayout(authController, keyguardUpdateMonitor, windowManager, context)
- }
-
- @Test
- fun testLayoutViews_KeyguardIndicationArea() {
- defaultLockscreenLayout.layoutViews(rootView)
- val constraint = getViewConstraint(R.id.keyguard_indication_area)
- assertThat(constraint.layout.bottomToBottom).isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(constraint.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(constraint.layout.mWidth).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT)
- assertThat(constraint.layout.mHeight).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
- }
-
- @Test
- fun testLayoutViews_lockIconView() {
- defaultLockscreenLayout.layoutViews(rootView)
- val constraint = getViewConstraint(R.id.lock_icon_view)
- assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
- }
-
- @Test
- fun testCenterLockIcon() {
- defaultLockscreenLayout.centerLockIcon(Point(5, 6), 1F, 5, rootView)
- val constraint = getViewConstraint(R.id.lock_icon_view)
-
- assertThat(constraint.layout.mWidth).isEqualTo(2)
- assertThat(constraint.layout.mHeight).isEqualTo(2)
- assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
- assertThat(constraint.layout.topMargin).isEqualTo(5)
- assertThat(constraint.layout.startMargin).isEqualTo(4)
- }
-
- /** Get the ConstraintLayout constraint of the view. */
- private fun getViewConstraint(viewId: Int): ConstraintSet.Constraint {
- val constraintSet = ConstraintSet()
- constraintSet.clone(rootView)
- return constraintSet.getConstraint(viewId)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt
similarity index 66%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt
index 145b2fd..bb73dc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt
@@ -18,10 +18,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import java.io.PrintWriter
import org.junit.Before
@@ -31,32 +32,33 @@
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
-import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(JUnit4::class)
@SmallTest
-class KeyguardLayoutManagerCommandListenerTest : SysuiTestCase() {
- private lateinit var keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener
+class KeyguardBlueprintCommandListenerTest : SysuiTestCase() {
+ private lateinit var keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener
@Mock private lateinit var commandRegistry: CommandRegistry
- @Mock private lateinit var keyguardLayoutManager: KeyguardLayoutManager
+ @Mock private lateinit var keyguardBlueprintRepository: KeyguardBlueprintRepository
+ @Mock private lateinit var keyguardBlueprintInteractor: KeyguardBlueprintInteractor
@Mock private lateinit var pw: PrintWriter
private lateinit var command: () -> Command
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- keyguardLayoutManagerCommandListener =
- KeyguardLayoutManagerCommandListener(
+ keyguardBlueprintCommandListener =
+ KeyguardBlueprintCommandListener(
commandRegistry,
- keyguardLayoutManager,
+ keyguardBlueprintRepository,
+ keyguardBlueprintInteractor,
)
- keyguardLayoutManagerCommandListener.start()
+ keyguardBlueprintCommandListener.start()
command =
withArgCaptor<() -> Command> {
- verify(commandRegistry).registerCommand(eq("layout"), capture())
+ verify(commandRegistry).registerCommand(eq("blueprint"), capture())
}
}
@@ -64,25 +66,19 @@
fun testHelp() {
command().execute(pw, listOf("help"))
verify(pw, atLeastOnce()).println(anyString())
- verify(keyguardLayoutManager, never()).transitionToLayout(anyString())
+ verify(keyguardBlueprintInteractor, never()).transitionToBlueprint(anyString())
}
@Test
fun testBlank() {
command().execute(pw, listOf())
verify(pw, atLeastOnce()).println(anyString())
- verify(keyguardLayoutManager, never()).transitionToLayout(anyString())
+ verify(keyguardBlueprintInteractor, never()).transitionToBlueprint(anyString())
}
@Test
fun testValidArg() {
- bindFakeIdMapToLayoutManager()
command().execute(pw, listOf("fake"))
- verify(keyguardLayoutManager).transitionToLayout("fake")
- }
-
- private fun bindFakeIdMapToLayoutManager() {
- val map = mapOf("fake" to mock(LockscreenLayout::class.java))
- whenever(keyguardLayoutManager.layoutIdMap).thenReturn(map)
+ verify(keyguardBlueprintInteractor).transitionToBlueprint("fake")
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt
deleted file mode 100644
index 95b2030..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.
- *
- */
-
-package com.android.systemui.keyguard.ui.view.layout
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.keyguard.ui.view.layout.DefaultLockscreenLayout.Companion.DEFAULT
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@RunWith(JUnit4::class)
-@SmallTest
-class KeyguardLayoutManagerTest : SysuiTestCase() {
- private lateinit var keyguardLayoutManager: KeyguardLayoutManager
- @Mock lateinit var configurationController: ConfigurationController
- @Mock lateinit var defaultLockscreenLayout: DefaultLockscreenLayout
- @Mock lateinit var keyguardRootView: KeyguardRootView
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- whenever(defaultLockscreenLayout.id).thenReturn(DEFAULT)
- keyguardLayoutManager =
- KeyguardLayoutManager(
- configurationController,
- setOf(defaultLockscreenLayout),
- keyguardRootView
- )
- }
-
- @Test
- fun testDefaultLayout() {
- keyguardLayoutManager.transitionToLayout(DEFAULT)
- verify(defaultLockscreenLayout).layoutViews(keyguardRootView)
- }
-
- @Test
- fun testTransitionToLayout_validId() {
- assertThat(keyguardLayoutManager.transitionToLayout(DEFAULT)).isTrue()
- }
- @Test
- fun testTransitionToLayout_invalidId() {
- assertThat(keyguardLayoutManager.transitionToLayout("abc")).isFalse()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
new file mode 100644
index 0000000..66631dc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.blueprints
+
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class DefaultKeyguardBlueprintTest : SysuiTestCase() {
+ private lateinit var underTest: DefaultKeyguardBlueprint
+ private lateinit var rootView: KeyguardRootView
+ @Mock private lateinit var defaultIndicationAreaSection: DefaultIndicationAreaSection
+ @Mock private lateinit var defaultLockIconSection: DefaultLockIconSection
+ @Mock private lateinit var defaultShortcutsSection: DefaultShortcutsSection
+ @Mock
+ private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
+ @Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ rootView = KeyguardRootView(context, null)
+ underTest =
+ DefaultKeyguardBlueprint(
+ defaultIndicationAreaSection,
+ defaultLockIconSection,
+ defaultShortcutsSection,
+ defaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection,
+ )
+ }
+
+ @Test
+ fun apply() {
+ val cs = ConstraintSet()
+ underTest.apply(cs)
+ verify(defaultIndicationAreaSection).apply(cs)
+ verify(defaultLockIconSection).apply(cs)
+ verify(defaultShortcutsSection).apply(cs)
+ verify(defaultAmbientIndicationAreaSection).apply(cs)
+ verify(defaultSettingsPopupMenuSection).apply(cs)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
new file mode 100644
index 0000000..3dcc03d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+@SmallTest
+class DefaultIndicationAreaSectionTest : SysuiTestCase() {
+ private val underTest = DefaultIndicationAreaSection(context)
+
+ @Test
+ fun apply() {
+ val cs = ConstraintSet()
+ underTest.apply(cs)
+
+ val constraint = cs.getConstraint(R.id.keyguard_indication_area)
+
+ assertThat(constraint.layout.bottomToBottom).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.mWidth).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT)
+ assertThat(constraint.layout.mHeight).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
new file mode 100644
index 0000000..1444f8d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.graphics.Point
+import android.view.WindowManager
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class DefaultLockIconSectionTest : SysuiTestCase() {
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var authController: AuthController
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
+ private lateinit var underTest: DefaultLockIconSection
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ DefaultLockIconSection(keyguardUpdateMonitor, authController, windowManager, context)
+ }
+
+ @Test
+ fun apply() {
+ val cs = ConstraintSet()
+ underTest.apply(cs)
+
+ val constraint = cs.getConstraint(R.id.lock_icon_view)
+
+ assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ }
+
+ @Test
+ fun testCenterLockIcon() {
+ val cs = ConstraintSet()
+ underTest.centerLockIcon(Point(5, 6), 1F, 5, cs)
+
+ val constraint = cs.getConstraint(R.id.lock_icon_view)
+
+ assertThat(constraint.layout.mWidth).isEqualTo(2)
+ assertThat(constraint.layout.mHeight).isEqualTo(2)
+ assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.topMargin).isEqualTo(5)
+ assertThat(constraint.layout.startMargin).isEqualTo(4)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
new file mode 100644
index 0000000..a18b033
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class KeyguardBlueprintViewModelTest : SysuiTestCase() {
+ @Mock private lateinit var keyguardBlueprintInteractor: KeyguardBlueprintInteractor
+ private lateinit var undertest: KeyguardBlueprintViewModel
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ undertest =
+ KeyguardBlueprintViewModel(keyguardBlueprintInteractor = keyguardBlueprintInteractor)
+ }
+
+ @Test
+ fun testBlueprintFlow() {
+ verify(keyguardBlueprintInteractor).blueprint
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index 45e8e27..f25454c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -98,7 +98,8 @@
private final BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
private final LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
private final MediaDevice mBluetoothMediaDevice = mock(BluetoothMediaDevice.class);
- private final BluetoothDevice mBluetoothDevice = mock(BluetoothDevice.class);
+ private final BluetoothDevice mBluetoothFirstDevice = mock(BluetoothDevice.class);
+ private final BluetoothDevice mBluetoothSecondDevice = mock(BluetoothDevice.class);
private final CachedBluetoothDevice mCachedBluetoothDevice = mock(CachedBluetoothDevice.class);
private final CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
@@ -142,20 +143,20 @@
}
@Test
- public void connectBroadcastWithActiveDevice_noBroadcastMetadata_failToAddSource() {
+ public void startBroadcastWithConnectedDevices_noBroadcastMetadata_failToAddSource() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(null);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(
mLocalBluetoothLeBroadcastAssistant);
- mMediaOutputBroadcastDialog.connectBroadcastWithActiveDevice();
+ mMediaOutputBroadcastDialog.startBroadcastWithConnectedDevices();
verify(mLocalBluetoothLeBroadcastAssistant, never()).addSource(any(), any(), anyBoolean());
}
@Test
- public void connectBroadcastWithActiveDevice_noConnectedMediaDevice_failToAddSource() {
+ public void startBroadcastWithConnectedDevices_noConnectedMediaDevice_failToAddSource() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(
@@ -164,13 +165,13 @@
mLocalBluetoothLeBroadcastAssistant);
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(null);
- mMediaOutputBroadcastDialog.connectBroadcastWithActiveDevice();
+ mMediaOutputBroadcastDialog.startBroadcastWithConnectedDevices();
verify(mLocalBluetoothLeBroadcastAssistant, never()).addSource(any(), any(), anyBoolean());
}
@Test
- public void connectBroadcastWithActiveDevice_hasBroadcastSource_failToAddSource() {
+ public void startBroadcastWithConnectedDevices_hasBroadcastSource_failToAddSource() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(
@@ -180,19 +181,19 @@
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mBluetoothMediaDevice);
when(((BluetoothMediaDevice) mBluetoothMediaDevice).getCachedDevice())
.thenReturn(mCachedBluetoothDevice);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothFirstDevice);
List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
sourceList.add(mBluetoothLeBroadcastReceiveState);
- when(mLocalBluetoothLeBroadcastAssistant.getAllSources(mBluetoothDevice)).thenReturn(
+ when(mLocalBluetoothLeBroadcastAssistant.getAllSources(mBluetoothFirstDevice)).thenReturn(
sourceList);
- mMediaOutputBroadcastDialog.connectBroadcastWithActiveDevice();
+ mMediaOutputBroadcastDialog.startBroadcastWithConnectedDevices();
verify(mLocalBluetoothLeBroadcastAssistant, never()).addSource(any(), any(), anyBoolean());
}
@Test
- public void connectBroadcastWithActiveDevice_noBroadcastSource_failToAddSource() {
+ public void startBroadcastWithConnectedDevices_noBroadcastSource_failToAddSource() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(
@@ -203,12 +204,16 @@
when(mBluetoothMediaDevice.isBLEDevice()).thenReturn(true);
when(((BluetoothMediaDevice) mBluetoothMediaDevice).getCachedDevice()).thenReturn(
mCachedBluetoothDevice);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothFirstDevice);
List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
- when(mLocalBluetoothLeBroadcastAssistant.getAllSources(mBluetoothDevice)).thenReturn(
+ when(mLocalBluetoothLeBroadcastAssistant.getAllSources(mBluetoothFirstDevice)).thenReturn(
sourceList);
+ List<BluetoothDevice> connectedDevicesList = new ArrayList<>();
+ connectedDevicesList.add(mBluetoothFirstDevice);
+ when(mLocalBluetoothLeBroadcastAssistant.getConnectedDevices()).thenReturn(
+ connectedDevicesList);
- mMediaOutputBroadcastDialog.connectBroadcastWithActiveDevice();
+ mMediaOutputBroadcastDialog.startBroadcastWithConnectedDevices();
verify(mLocalBluetoothLeBroadcastAssistant, times(1)).addSource(any(), any(), anyBoolean());
}
@@ -360,4 +365,32 @@
assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE);
}
+
+ @Test
+ public void addSourceToAllConnectedDevices() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(
+ mLocalBluetoothLeBroadcastAssistant);
+ when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(
+ mBluetoothLeBroadcastMetadata);
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mBluetoothMediaDevice);
+ when(mBluetoothMediaDevice.isBLEDevice()).thenReturn(true);
+ when(((BluetoothMediaDevice) mBluetoothMediaDevice).getCachedDevice())
+ .thenReturn(mCachedBluetoothDevice);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothFirstDevice);
+ List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+ when(mLocalBluetoothLeBroadcastAssistant.getAllSources(mBluetoothFirstDevice)).thenReturn(
+ sourceList);
+ List<BluetoothDevice> connectedDevicesList = new ArrayList<>();
+ connectedDevicesList.add(mBluetoothFirstDevice);
+ connectedDevicesList.add(mBluetoothSecondDevice);
+ when(mLocalBluetoothLeBroadcastAssistant.getConnectedDevices()).thenReturn(
+ connectedDevicesList);
+
+ mMediaOutputBroadcastDialog.startBroadcastWithConnectedDevices();
+
+ verify(mLocalBluetoothLeBroadcastAssistant, times(2)).addSource(any(), any(), anyBoolean());
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index ea25f71..c7a74da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -40,6 +40,8 @@
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.media.taptotransfer.MediaTttFlags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.CommandQueue
@@ -111,6 +113,7 @@
private lateinit var uiEventLogger: MediaTttSenderUiEventLogger
private lateinit var tempViewUiEventLogger: TemporaryViewUiEventLogger
private val defaultTimeout = context.resources.getInteger(R.integer.heads_up_notification_decay)
+ private val featureFlags = FakeFeatureFlags()
@Before
fun setUp() {
@@ -160,7 +163,9 @@
fakeWakeLockBuilder,
fakeClock,
tempViewUiEventLogger,
+ featureFlags
)
+ featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
chipbarCoordinator.start()
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index c193d83..4facc7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -52,7 +52,7 @@
val currentScene by collectLastValue(underTest.currentScene)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
- underTest.setCurrentScene(SceneModel(SceneKey.Shade))
+ underTest.setCurrentScene(SceneModel(SceneKey.Shade), "reason")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade))
}
@@ -79,10 +79,10 @@
val isVisible by collectLastValue(underTest.isVisible)
assertThat(isVisible).isTrue()
- underTest.setVisible(false)
+ underTest.setVisible(false, "reason")
assertThat(isVisible).isFalse()
- underTest.setVisible(true)
+ underTest.setVisible(true, "reason")
assertThat(isVisible).isTrue()
}
@@ -92,7 +92,7 @@
assertThat(transitions).isNull()
val initialSceneKey = underTest.currentScene.value.key
- underTest.setCurrentScene(SceneModel(SceneKey.Shade))
+ underTest.setCurrentScene(SceneModel(SceneKey.Shade), "reason")
assertThat(transitions)
.isEqualTo(
SceneTransitionModel(
@@ -101,7 +101,7 @@
)
)
- underTest.setCurrentScene(SceneModel(SceneKey.QuickSettings))
+ underTest.setCurrentScene(SceneModel(SceneKey.QuickSettings), "reason")
assertThat(transitions)
.isEqualTo(
SceneTransitionModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index b6bd31f..6be19b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -73,6 +73,7 @@
featureFlags = featureFlags,
sysUiState = sysUiState,
displayId = Display.DEFAULT_DISPLAY,
+ sceneLogger = mock(),
)
@Before
@@ -97,7 +98,7 @@
assertThat(isVisible).isFalse()
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Shade))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Shade), "reason")
assertThat(isVisible).isTrue()
}
@@ -117,10 +118,10 @@
underTest.start()
assertThat(isVisible).isTrue()
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone), "reason")
assertThat(isVisible).isTrue()
- sceneInteractor.setCurrentScene(SceneModel(SceneKey.Shade))
+ sceneInteractor.setCurrentScene(SceneModel(SceneKey.Shade), "reason")
assertThat(isVisible).isTrue()
}
@@ -326,7 +327,7 @@
SceneKey.QuickSettings,
)
.forEachIndexed { index, sceneKey ->
- sceneInteractor.setCurrentScene(SceneModel(sceneKey))
+ sceneInteractor.setCurrentScene(SceneModel(sceneKey), "reason")
runCurrent()
verify(sysUiState, times(index + 1)).commitUpdate(Display.DEFAULT_DISPLAY)
@@ -342,7 +343,7 @@
featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled)
authenticationRepository.setUnlocked(isDeviceUnlocked)
keyguardRepository.setBypassEnabled(isBypassEnabled)
- initialSceneKey?.let { sceneInteractor.setCurrentScene(SceneModel(it)) }
+ initialSceneKey?.let { sceneInteractor.setCurrentScene(SceneModel(it), "reason") }
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 0ab98ad..9f3b12b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -52,10 +52,10 @@
val isVisible by collectLastValue(underTest.isVisible)
assertThat(isVisible).isTrue()
- interactor.setVisible(false)
+ interactor.setVisible(false, "reason")
assertThat(isVisible).isFalse()
- interactor.setVisible(true)
+ interactor.setVisible(true, "reason")
assertThat(isVisible).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 40d9cc7..c3540cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -19,6 +19,7 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
import static com.google.common.truth.Truth.assertThat;
@@ -221,7 +222,7 @@
@Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@Mock protected KeyguardStateController mKeyguardStateController;
@Mock protected DozeLog mDozeLog;
- @Mock protected ShadeLogger mShadeLog;
+ private final ShadeLogger mShadeLog = new ShadeLogger(logcatLogBuffer());
@Mock protected CommandQueue mCommandQueue;
@Mock protected VibratorHelper mVibratorHelper;
@Mock protected LatencyTracker mLatencyTracker;
@@ -309,7 +310,7 @@
@Mock protected MotionEvent mDownMotionEvent;
@Mock protected CoroutineDispatcher mMainDispatcher;
@Mock protected KeyguardSliceViewController mKeyguardSliceViewController;
- @Mock protected KeyguardLogger mKeyguardLogger;
+ private final KeyguardLogger mKeyguardLogger = new KeyguardLogger(logcatLogBuffer());
@Mock protected KeyguardStatusView mKeyguardStatusView;
@Captor
protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
@@ -518,7 +519,7 @@
mKeyguardBypassController,
mDozeParameters,
mScreenOffAnimationController,
- mock(NotificationWakeUpCoordinatorLogger.class));
+ new NotificationWakeUpCoordinatorLogger(logcatLogBuffer()));
mConfigurationController = new ConfigurationControllerImpl(mContext);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 893123d..7b3e89d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -50,6 +50,7 @@
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -117,6 +118,7 @@
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
+ private val notificationExpansionRepository = NotificationExpansionRepository()
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var interactionEventHandler: InteractionEventHandler
@@ -175,6 +177,7 @@
mock(KeyguardMessageAreaController.Factory::class.java),
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
+ notificationExpansionRepository,
featureFlags,
FakeSystemClock(),
BouncerMessageInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index ed4ac35c..5c3ce71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -50,6 +50,7 @@
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -185,6 +186,7 @@
Mockito.mock(KeyguardMessageAreaController.Factory::class.java),
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
+ NotificationExpansionRepository(),
featureFlags,
FakeSystemClock(),
BouncerMessageInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 2bc112d..112a09b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -144,27 +144,52 @@
@Test
fun testSmallScreen_updateResources_splitShadeHeightIsSet() {
overrideResource(R.bool.config_use_large_screen_shade_header, false)
- overrideResource(R.dimen.qs_header_height, 1)
- overrideResource(R.dimen.large_screen_shade_header_height, 2)
+ overrideResource(R.dimen.qs_header_height, 10)
+ overrideResource(R.dimen.large_screen_shade_header_height, 20)
+
+ // ensure the estimated height (would be 3 here) wouldn't impact this test case
+ overrideResource(R.dimen.large_screen_shade_header_min_height, 1)
+ overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 1)
underTest.updateResources()
val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
verify(view).applyConstraints(capture(captor))
- assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(1)
+ assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(10)
}
@Test
fun testLargeScreen_updateResources_splitShadeHeightIsSet() {
overrideResource(R.bool.config_use_large_screen_shade_header, true)
- overrideResource(R.dimen.qs_header_height, 1)
- overrideResource(R.dimen.large_screen_shade_header_height, 2)
+ overrideResource(R.dimen.qs_header_height, 10)
+ overrideResource(R.dimen.large_screen_shade_header_height, 20)
+
+ // ensure the estimated height (would be 3 here) wouldn't impact this test case
+ overrideResource(R.dimen.large_screen_shade_header_min_height, 1)
+ overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 1)
underTest.updateResources()
val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
verify(view).applyConstraints(capture(captor))
- assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(2)
+ assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(20)
+ }
+
+ @Test
+ fun testSmallScreen_estimatedHeightIsLargerThanDimenValue_shadeHeightIsSetToEstimatedHeight() {
+ overrideResource(R.bool.config_use_large_screen_shade_header, false)
+ overrideResource(R.dimen.qs_header_height, 10)
+ overrideResource(R.dimen.large_screen_shade_header_height, 20)
+
+ // make the estimated height (would be 15 here) larger than qs_header_height
+ overrideResource(R.dimen.large_screen_shade_header_min_height, 5)
+ overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 5)
+
+ underTest.updateResources()
+
+ val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
+ verify(view).applyConstraints(capture(captor))
+ assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(15)
}
@Test
@@ -388,6 +413,10 @@
val largeScreenHeaderHeight = 100
overrideResource(R.dimen.large_screen_shade_header_height, largeScreenHeaderHeight)
+ // ensure the estimated height (would be 30 here) wouldn't impact this test case
+ overrideResource(R.dimen.large_screen_shade_header_min_height, 10)
+ overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 10)
+
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index a504818..8d3c4b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -143,27 +143,52 @@
@Test
fun testSmallScreen_updateResources_splitShadeHeightIsSet() {
overrideResource(R.bool.config_use_large_screen_shade_header, false)
- overrideResource(R.dimen.qs_header_height, 1)
- overrideResource(R.dimen.large_screen_shade_header_height, 2)
+ overrideResource(R.dimen.qs_header_height, 10)
+ overrideResource(R.dimen.large_screen_shade_header_height, 20)
+
+ // ensure the estimated height (would be 3 here) wouldn't impact this test case
+ overrideResource(R.dimen.large_screen_shade_header_min_height, 1)
+ overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 1)
underTest.updateResources()
val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
verify(view).applyConstraints(capture(captor))
- assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(1)
+ assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(10)
}
@Test
fun testLargeScreen_updateResources_splitShadeHeightIsSet() {
overrideResource(R.bool.config_use_large_screen_shade_header, true)
- overrideResource(R.dimen.qs_header_height, 1)
- overrideResource(R.dimen.large_screen_shade_header_height, 2)
+ overrideResource(R.dimen.qs_header_height, 10)
+ overrideResource(R.dimen.large_screen_shade_header_height, 20)
+
+ // ensure the estimated height (would be 3 here) wouldn't impact this test case
+ overrideResource(R.dimen.large_screen_shade_header_min_height, 1)
+ overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 1)
underTest.updateResources()
val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
verify(view).applyConstraints(capture(captor))
- assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(2)
+ assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(20)
+ }
+
+ @Test
+ fun testSmallScreen_estimatedHeightIsLargerThanDimenValue_shadeHeightIsSetToEstimatedHeight() {
+ overrideResource(R.bool.config_use_large_screen_shade_header, false)
+ overrideResource(R.dimen.qs_header_height, 10)
+ overrideResource(R.dimen.large_screen_shade_header_height, 20)
+
+ // make the estimated height (would be 15 here) larger than qs_header_height
+ overrideResource(R.dimen.large_screen_shade_header_min_height, 5)
+ overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 5)
+
+ underTest.updateResources()
+
+ val captor = ArgumentCaptor.forClass(ConstraintSet::class.java)
+ verify(view).applyConstraints(capture(captor))
+ assertThat(captor.value.getHeight(R.id.split_shade_status_bar)).isEqualTo(15)
}
@Test
@@ -376,6 +401,10 @@
val largeScreenHeaderHeight = 100
overrideResource(R.dimen.large_screen_shade_header_height, largeScreenHeaderHeight)
+ // ensure the estimated height (would be 30 here) wouldn't impact this test case
+ overrideResource(R.dimen.large_screen_shade_header_min_height, 10)
+ overrideResource(R.dimen.new_qs_header_non_clickable_element_height, 10)
+
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index fe18fb5..b6da20f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -19,6 +19,7 @@
import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static junit.framework.Assert.assertFalse;
@@ -45,7 +46,6 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import org.junit.After;
@@ -73,9 +73,7 @@
// Number of notifications to use in tests requiring multiple notifications
private static final int TEST_NUM_NOTIFICATIONS = 4;
protected static final int TEST_TIMEOUT_TIME = 15000;
- protected final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true;
-
- private AlertingNotificationManager mAlertingNotificationManager;
+ protected final Runnable mTestTimeoutRunnable = () -> mTimedOut = true;
protected NotificationEntry mEntry;
protected Handler mTestHandler;
@@ -84,11 +82,11 @@
@Mock protected ExpandableNotificationRow mRow;
- private final class TestableAlertingNotificationManager extends AlertingNotificationManager {
+ private static class TestableAlertingNotificationManager extends AlertingNotificationManager {
private AlertEntry mLastCreatedEntry;
private TestableAlertingNotificationManager(Handler handler) {
- super(mock(HeadsUpManagerLogger.class), handler);
+ super(new HeadsUpManagerLogger(logcatLogBuffer()), handler);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mStickyDisplayTime = TEST_STICKY_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -112,8 +110,8 @@
}
}
- protected AlertingNotificationManager createAlertingNotificationManager(Handler handler) {
- return new TestableAlertingNotificationManager(handler);
+ protected AlertingNotificationManager createAlertingNotificationManager() {
+ return new TestableAlertingNotificationManager(mTestHandler);
}
protected StatusBarNotification createNewSbn(int id, Notification n) {
@@ -169,8 +167,6 @@
.setSbn(mSbn)
.build();
mEntry.setRow(mRow);
-
- mAlertingNotificationManager = createAlertingNotificationManager(mTestHandler);
}
@After
@@ -180,68 +176,74 @@
@Test
public void testShowNotification_addsEntry() {
- mAlertingNotificationManager.showNotification(mEntry);
+ AlertingNotificationManager alm = createAlertingNotificationManager();
- assertTrue(mAlertingNotificationManager.isAlerting(mEntry.getKey()));
- assertTrue(mAlertingNotificationManager.hasNotifications());
- assertEquals(mEntry, mAlertingNotificationManager.getEntry(mEntry.getKey()));
+ alm.showNotification(mEntry);
+
+ assertTrue(alm.isAlerting(mEntry.getKey()));
+ assertTrue(alm.hasNotifications());
+ assertEquals(mEntry, alm.getEntry(mEntry.getKey()));
}
@Test
public void testShowNotification_autoDismisses() {
- mAlertingNotificationManager.showNotification(mEntry);
- mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_TIMEOUT_TIME);
+ AlertingNotificationManager alm = createAlertingNotificationManager();
+
+ alm.showNotification(mEntry);
+ mTestHandler.postDelayed(mTestTimeoutRunnable, TEST_TIMEOUT_TIME);
// Wait for remove runnable and then process it immediately
TestableLooper.get(this).processMessages(1);
assertFalse("Test timed out", mTimedOut);
- assertFalse(mAlertingNotificationManager.isAlerting(mEntry.getKey()));
+ assertFalse(alm.isAlerting(mEntry.getKey()));
}
@Test
public void testRemoveNotification_removeDeferred() {
- mAlertingNotificationManager.showNotification(mEntry);
+ AlertingNotificationManager alm = createAlertingNotificationManager();
+ alm.showNotification(mEntry);
// Try to remove but defer, since the notification has not been shown long enough.
- mAlertingNotificationManager.removeNotification(
- mEntry.getKey(), false /* releaseImmediately */);
+ alm.removeNotification(mEntry.getKey(), false /* releaseImmediately */);
- assertTrue(mAlertingNotificationManager.isAlerting(mEntry.getKey()));
+ assertTrue(alm.isAlerting(mEntry.getKey()));
}
@Test
public void testRemoveNotification_forceRemove() {
- mAlertingNotificationManager.showNotification(mEntry);
+ AlertingNotificationManager alm = createAlertingNotificationManager();
+ alm.showNotification(mEntry);
// Remove forcibly with releaseImmediately = true.
- mAlertingNotificationManager.removeNotification(
- mEntry.getKey(), true /* releaseImmediately */);
+ alm.removeNotification(mEntry.getKey(), true /* releaseImmediately */);
- assertFalse(mAlertingNotificationManager.isAlerting(mEntry.getKey()));
+ assertFalse(alm.isAlerting(mEntry.getKey()));
}
@Test
public void testReleaseAllImmediately() {
+ AlertingNotificationManager alm = createAlertingNotificationManager();
for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
StatusBarNotification sbn = createNewNotification(i);
NotificationEntry entry = new NotificationEntryBuilder()
.setSbn(sbn)
.build();
entry.setRow(mRow);
- mAlertingNotificationManager.showNotification(entry);
+ alm.showNotification(entry);
}
- mAlertingNotificationManager.releaseAllImmediately();
+ alm.releaseAllImmediately();
- assertEquals(0, mAlertingNotificationManager.getAllEntries().count());
+ assertEquals(0, alm.getAllEntries().count());
}
@Test
public void testCanRemoveImmediately_notShownLongEnough() {
- mAlertingNotificationManager.showNotification(mEntry);
+ AlertingNotificationManager alm = createAlertingNotificationManager();
+ alm.showNotification(mEntry);
// The entry has just been added so we should not remove immediately.
- assertFalse(mAlertingNotificationManager.canRemoveImmediately(mEntry.getKey()));
+ assertFalse(alm.canRemoveImmediately(mEntry.getKey()));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 305f48b..764f7b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -82,9 +84,9 @@
mPowerInteractor,
mStateController,
mRemoteInputUriController,
- mock(RemoteInputControllerLogger.class),
+ new RemoteInputControllerLogger(logcatLogBuffer()),
mClickNotifier,
- mock(ActionClickLogger.class),
+ new ActionClickLogger(logcatLogBuffer()),
mock(DumpManager.class));
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
index 0cfca61..2e223f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
@@ -24,9 +24,9 @@
import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
-import androidx.core.animation.AnimatorTestRule
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index 2af0ceb..414256f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -22,9 +22,9 @@
import android.testing.TestableLooper.RunWithLooper
import android.view.View
import android.widget.FrameLayout
-import androidx.core.animation.AnimatorTestRule
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index f4458bb..e66eb70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
@@ -6,7 +6,8 @@
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
-import com.android.systemui.shade.NotificationShadeWindowViewController
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -14,6 +15,7 @@
import com.android.systemui.statusbar.policy.HeadsUpUtil
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import kotlinx.coroutines.test.TestScope
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -27,7 +29,6 @@
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
- @Mock lateinit var notificationShadeWindowViewController: NotificationShadeWindowViewController
@Mock lateinit var notificationListContainer: NotificationListContainer
@Mock lateinit var headsUpManager: HeadsUpManagerPhone
@Mock lateinit var jankMonitor: InteractionJankMonitor
@@ -36,6 +37,9 @@
private lateinit var notificationTestHelper: NotificationTestHelper
private lateinit var notification: ExpandableNotificationRow
private lateinit var controller: NotificationLaunchAnimatorController
+ private val notificationExpansionRepository = NotificationExpansionRepository()
+
+ private val testScope = TestScope()
private val notificationKey: String
get() = notification.entry.sbn.key
@@ -49,7 +53,7 @@
NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
notification = notificationTestHelper.createRow()
controller = NotificationLaunchAnimatorController(
- notificationShadeWindowViewController,
+ notificationExpansionRepository,
notificationListContainer,
headsUpManager,
notification,
@@ -69,6 +73,11 @@
assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification))
assertFalse(notification.entry.isExpandAnimationRunning)
+ val isExpandAnimationRunning by testScope.collectLastValue(
+ notificationExpansionRepository.isExpandAnimationRunning
+ )
+ assertFalse(isExpandAnimationRunning!!)
+
verify(headsUpManager).removeNotification(
notificationKey, true /* releaseImmediately */, true /* animate */)
verify(onFinishAnimationCallback).run()
@@ -81,6 +90,11 @@
assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification))
assertFalse(notification.entry.isExpandAnimationRunning)
+ val isExpandAnimationRunning by testScope.collectLastValue(
+ notificationExpansionRepository.isExpandAnimationRunning
+ )
+ assertFalse(isExpandAnimationRunning!!)
+
verify(headsUpManager).removeNotification(
notificationKey, true /* releaseImmediately */, true /* animate */)
verify(onFinishAnimationCallback).run()
@@ -93,6 +107,11 @@
assertFalse(HeadsUpUtil.isClickedHeadsUpNotification(notification))
assertFalse(notification.entry.isExpandAnimationRunning)
+ val isExpandAnimationRunning by testScope.collectLastValue(
+ notificationExpansionRepository.isExpandAnimationRunning
+ )
+ assertFalse(isExpandAnimationRunning!!)
+
verify(headsUpManager).removeNotification(
notificationKey, true /* releaseImmediately */, false /* animate */)
verify(onFinishAnimationCallback).run()
@@ -103,5 +122,9 @@
controller.onIntentStarted(willAnimate = true)
assertTrue(notification.entry.isExpandAnimationRunning)
+ val isExpandAnimationRunning by testScope.collectLastValue(
+ notificationExpansionRepository.isExpandAnimationRunning
+ )
+ assertTrue(isExpandAnimationRunning!!)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
index da3a9f6..40edea2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
@@ -18,10 +18,11 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import androidx.core.animation.AnimatorTestRule
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeViewController.Companion.WAKEUP_ANIMATION_DELAY_MS
import com.android.systemui.statusbar.StatusBarState
@@ -59,7 +60,7 @@
private val bypassController: KeyguardBypassController = mock()
private val dozeParameters: DozeParameters = mock()
private val screenOffAnimationController: ScreenOffAnimationController = mock()
- private val logger: NotificationWakeUpCoordinatorLogger = mock()
+ private val logger = NotificationWakeUpCoordinatorLogger(logcatLogBuffer())
private val stackScrollerController: NotificationStackScrollLayoutController = mock()
private val wakeUpListener: NotificationWakeUpCoordinator.WakeUpListener = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 9037df8..104b751 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -26,6 +26,7 @@
import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
@@ -47,6 +48,7 @@
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -120,7 +122,7 @@
@Mock private IStatusBarService mStatusBarService;
@Mock private NotifPipelineFlags mNotifPipelineFlags;
- @Mock private NotifCollectionLogger mLogger;
+ private final NotifCollectionLogger mLogger = spy(new NotifCollectionLogger(logcatLogBuffer()));
@Mock private LogBufferEulogizer mEulogizer;
@Mock private Handler mMainHandler;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index a869038..bfa03ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.notification.collection.ListDumper.dumpTree;
import static com.android.systemui.statusbar.notification.collection.ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS;
@@ -101,10 +102,10 @@
public class ShadeListBuilderTest extends SysuiTestCase {
private ShadeListBuilder mListBuilder;
- private FakeSystemClock mSystemClock = new FakeSystemClock();
-
- @Mock private NotifPipelineFlags mNotifPipelineFlags;
- @Mock private ShadeListBuilderLogger mLogger;
+ private final FakeSystemClock mSystemClock = new FakeSystemClock();
+ private final NotifPipelineFlags mNotifPipelineFlags = mock(NotifPipelineFlags.class);
+ private final ShadeListBuilderLogger mLogger = new ShadeListBuilderLogger(
+ mNotifPipelineFlags, logcatLogBuffer());
@Mock private DumpManager mDumpManager;
@Mock private NotifCollection mNotifCollection;
@Mock private NotificationInteractionTracker mInteractionTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
index ac9a570..3dcfcfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coalescer;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.clearInvocations;
@@ -62,8 +64,7 @@
@Mock private NotificationListener mListenerService;
@Mock private GroupCoalescer.BatchableNotificationHandler mListener;
- @Mock private GroupCoalescerLogger mLogger;
-
+ private final GroupCoalescerLogger mLogger = new GroupCoalescerLogger(logcatLogBuffer());
@Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor;
private final NoManSimulator mNoMan = new NoManSimulator();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
index 4143647..362da0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
@@ -20,6 +20,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -28,6 +29,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager
import com.android.systemui.statusbar.notification.row.NotificationGuts
+import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -36,6 +38,7 @@
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
@@ -52,8 +55,9 @@
@Mock private lateinit var notifGutsViewManager: NotifGutsViewManager
@Mock private lateinit var pipeline: NotifPipeline
@Mock private lateinit var dumpManager: DumpManager
- @Mock private lateinit var logger: GutsCoordinatorLogger
+ private val logger = GutsCoordinatorLogger(logcatLogBuffer())
@Mock private lateinit var lifetimeExtenderCallback: OnEndLifetimeExtensionCallback
+ @Mock private lateinit var notificationGuts: NotificationGuts
@Before
fun setUp() {
@@ -69,12 +73,13 @@
notifLifetimeExtender.setCallback(lifetimeExtenderCallback)
entry1 = NotificationEntryBuilder().setId(1).build()
entry2 = NotificationEntryBuilder().setId(2).build()
+ whenever(notificationGuts.gutsContent).thenReturn(mock(GutsContent::class.java))
}
@Test
fun testSimpleLifetimeExtension() {
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
- notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
+ notifGutsViewListener.onGutsOpen(entry1, notificationGuts)
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
notifGutsViewListener.onGutsClose(entry1)
verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
@@ -84,9 +89,9 @@
@Test
fun testDoubleOpenLifetimeExtension() {
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
- notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
+ notifGutsViewListener.onGutsOpen(entry1, notificationGuts)
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
- notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
+ notifGutsViewListener.onGutsOpen(entry1, notificationGuts)
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
notifGutsViewListener.onGutsClose(entry1)
verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
@@ -97,10 +102,10 @@
fun testTwoEntryLifetimeExtension() {
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse()
- notifGutsViewListener.onGutsOpen(entry1, mock(NotificationGuts::class.java))
+ notifGutsViewListener.onGutsOpen(entry1, notificationGuts)
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse()
- notifGutsViewListener.onGutsOpen(entry2, mock(NotificationGuts::class.java))
+ notifGutsViewListener.onGutsOpen(entry2, notificationGuts)
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isTrue()
notifGutsViewListener.onGutsClose(entry1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index ea70e9e..fbd61f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.advanceTimeBy
import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -606,7 +607,7 @@
keyguardNotifVisibilityProvider,
keyguardRepository,
keyguardTransitionRepository,
- mock<KeyguardCoordinatorLogger>(),
+ KeyguardCoordinatorLogger(logcatLogBuffer()),
testScope.backgroundScope,
sectionHeaderVisibilityProvider,
fakeSettings,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index b5e77e0..548ecde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -18,6 +18,7 @@
import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
import static org.junit.Assert.assertFalse;
@@ -134,7 +135,7 @@
setSectionIsLowPriority(false);
PreparationCoordinator coordinator = new PreparationCoordinator(
- mock(PreparationCoordinatorLogger.class),
+ new PreparationCoordinatorLogger(logcatLogBuffer()),
mNotifInflater,
mErrorManager,
mock(NotifViewBarn.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
index 5793364..069eec2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
@@ -21,6 +21,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -50,7 +51,7 @@
private lateinit var entry2: NotificationEntry
@Mock private lateinit var pipeline: NotifPipeline
- @Mock private lateinit var logger: ShadeEventCoordinatorLogger
+ private val logger = ShadeEventCoordinatorLogger(logcatLogBuffer())
@Mock private lateinit var executor: Executor
@Mock private lateinit var notifRemovedByUserCallback: Runnable
@Mock private lateinit var shadeEmptiedCallback: Runnable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
index 6eb391a..0b61a8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
@@ -21,14 +21,15 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
@@ -36,7 +37,7 @@
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class NotifCollectionInconsistencyTrackerTest : SysuiTestCase() {
- private val logger: NotifCollectionLogger = mock()
+ private val logger = spy(NotifCollectionLogger(logcatLogBuffer()))
private val entry1: NotificationEntry = NotificationEntryBuilder().setId(1).build()
private val entry2: NotificationEntry = NotificationEntryBuilder().setId(2).build()
private val collectionSet = mutableSetOf<String>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index ac254ab..bad56a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -18,6 +18,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
@@ -46,7 +47,7 @@
private val sectionsFeatureManager: NotificationSectionsFeatureManager = mock()
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
private val viewBarn: NotifViewBarn = mock()
- private val logger: NodeSpecBuilderLogger = mock()
+ private val logger = NodeSpecBuilderLogger(mock(), logcatLogBuffer())
private var rootController: NodeController = buildFakeController("rootController")
private var headerController0: NodeController = buildFakeController("header0")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
index 6167b46..9a60272 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
@@ -22,7 +22,7 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.dump.logcatLogBuffer
import org.junit.Assert
import org.junit.Before
import org.junit.Test
@@ -30,6 +30,7 @@
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.matches
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
@SmallTest
@@ -44,7 +45,7 @@
private val controller5 = FakeController(mContext, "Controller5")
private val controller6 = FakeController(mContext, "Controller6")
private val controller7 = FakeController(mContext, "Controller7")
- private val logger: ShadeViewDifferLogger = mock()
+ private val logger = spy(ShadeViewDifferLogger(logcatLogBuffer()))
@Before
fun setUp() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepositoryTest.kt
new file mode 100644
index 0000000..f28d9ab
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepositoryTest.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@SmallTest
+class NotificationExpansionRepositoryTest : SysuiTestCase() {
+ private val underTest = NotificationExpansionRepository()
+
+ @Test
+ fun setIsExpandAnimationRunning_startsAsFalse() = runTest {
+ val latest by collectLastValue(underTest.isExpandAnimationRunning)
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun setIsExpandAnimationRunning_false_emitsTrue() = runTest {
+ val latest by collectLastValue(underTest.isExpandAnimationRunning)
+
+ underTest.setIsExpandAnimationRunning(true)
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun setIsExpandAnimationRunning_false_emitsFalse() = runTest {
+ val latest by collectLastValue(underTest.isExpandAnimationRunning)
+ underTest.setIsExpandAnimationRunning(true)
+
+ // WHEN the animation is no longer running
+ underTest.setIsExpandAnimationRunning(false)
+
+ // THEN the flow emits false
+ assertThat(latest).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
index ca65987..04ffab3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
@@ -16,9 +16,12 @@
package com.android.systemui.statusbar.notification.interruption;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -50,7 +53,8 @@
private HeadsUpViewBinder mViewBinder;
@Mock private NotificationMessagingUtil mNotificationMessagingUtil;
@Mock private RowContentBindStage mBindStage;
- @Mock private HeadsUpViewBinderLogger mLogger;
+ private final HeadsUpViewBinderLogger mLogger = spy(
+ new HeadsUpViewBinderLogger(logcatLogBuffer()));
@Mock private NotificationEntry mEntry;
@Mock private ExpandableNotificationRow mRow;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 4d4d319..764005b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -17,10 +17,6 @@
package com.android.systemui.statusbar.notification.row
-import android.app.Notification
-import android.net.Uri
-import android.os.UserHandle
-import android.os.UserHandle.USER_ALL
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -28,21 +24,18 @@
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.PluginManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.statusbar.SmartReplyController
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
import com.android.systemui.statusbar.notification.collection.render.FakeNodeController
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
import com.android.systemui.statusbar.notification.logging.NotificationLogger
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController.BUBBLES_SETTING_URI
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -53,9 +46,9 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.time.SystemClock
import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
import junit.framework.Assert
import org.junit.After
import org.junit.Before
@@ -63,10 +56,8 @@
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import java.util.*
import org.mockito.Mockito.`when` as whenever
@SmallTest
@@ -81,7 +72,7 @@
private val activableNotificationViewController: ActivatableNotificationViewController = mock()
private val rivSubComponentFactory: RemoteInputViewSubcomponent.Factory = mock()
private val metricsLogger: MetricsLogger = mock()
- private val logBufferLogger: NotificationRowLogger = mock()
+ private val logBufferLogger = NotificationRowLogger(logcatLogBuffer(), logcatLogBuffer())
private val listContainer: NotificationListContainer = mock()
private val childrenContainer: NotificationChildrenContainer = mock()
private val smartReplyConstants: SmartReplyConstants = mock()
@@ -103,10 +94,10 @@
private val featureFlags: FeatureFlags = mock()
private val peopleNotificationIdentifier: PeopleNotificationIdentifier = mock()
private val bubblesManager: BubblesManager = mock()
- private val settingsController: NotificationSettingsController = mock()
private val dragController: ExpandableNotificationRowDragController = mock()
private val dismissibilityProvider: NotificationDismissibilityProvider = mock()
private val statusBarService: IStatusBarService = mock()
+
private lateinit var controller: ExpandableNotificationRowController
@Before
@@ -119,7 +110,7 @@
rivSubComponentFactory,
metricsLogger,
logBufferLogger,
- mock<NotificationChildrenContainerLogger>(),
+ NotificationChildrenContainerLogger(logcatLogBuffer()),
listContainer,
smartReplyConstants,
smartReplyController,
@@ -143,16 +134,11 @@
featureFlags,
peopleNotificationIdentifier,
Optional.of(bubblesManager),
- settingsController,
dragController,
dismissibilityProvider,
statusBarService
)
whenever(view.childrenContainer).thenReturn(childrenContainer)
-
- val notification = Notification.Builder(mContext).build()
- val sbn = SbnBuilder().setNotification(notification).build()
- whenever(view.entry).thenReturn(NotificationEntryBuilder().setSbn(sbn).build())
}
@After
@@ -165,13 +151,13 @@
whenever(view.isParentDismissed).thenReturn(true)
Assert.assertTrue(controller.offerToKeepInParentForAnimation())
- Mockito.verify(view).setKeepInParentForDismissAnimation(true)
+ verify(view).setKeepInParentForDismissAnimation(true)
}
@Test
fun offerKeepInParent_parentNotDismissed() {
Assert.assertFalse(controller.offerToKeepInParentForAnimation())
- Mockito.verify(view, never()).setKeepInParentForDismissAnimation(anyBoolean())
+ verify(view, never()).setKeepInParentForDismissAnimation(anyBoolean())
}
@Test
@@ -181,7 +167,7 @@
whenever(view.keepInParentForDismissAnimation()).thenReturn(true)
Assert.assertTrue(controller.removeFromParentIfKeptForAnimation())
- Mockito.verify(parentView).removeChildNotification(view)
+ verify(parentView).removeChildNotification(view)
}
@Test
@@ -202,9 +188,9 @@
controller.removeChild(childNodeController, /* isTransfer= */ true)
// VERIFY the listContainer is not notified
- Mockito.verify(childView).isChangingPosition = eq(true)
- Mockito.verify(view).removeChildNotification(eq(childView))
- Mockito.verify(listContainer, never()).notifyGroupChildRemoved(any(), any())
+ verify(childView).isChangingPosition = eq(true)
+ verify(view).removeChildNotification(eq(childView))
+ verify(listContainer, never()).notifyGroupChildRemoved(any(), any())
}
@Test
@@ -216,78 +202,8 @@
controller.removeChild(childNodeController, /* isTransfer= */ false)
// VERIFY the listContainer is passed the childrenContainer for transient animations
- Mockito.verify(childView, never()).isChangingPosition = any()
- Mockito.verify(view).removeChildNotification(eq(childView))
- Mockito.verify(listContainer).notifyGroupChildRemoved(eq(childView), eq(childrenContainer))
- }
-
- @Test
- fun registerSettingsListener_forBubbles() {
- controller.init(mock(NotificationEntry::class.java))
- val viewStateObserver = withArgCaptor {
- verify(view).addOnAttachStateChangeListener(capture());
- }
- viewStateObserver.onViewAttachedToWindow(view);
- verify(settingsController).addCallback(any(), any());
- }
-
- @Test
- fun unregisterSettingsListener_forBubbles() {
- controller.init(mock(NotificationEntry::class.java))
- val viewStateObserver = withArgCaptor {
- verify(view).addOnAttachStateChangeListener(capture());
- }
- viewStateObserver.onViewDetachedFromWindow(view);
- verify(settingsController).removeCallback(any(), any());
- }
-
- @Test
- fun settingsListener_invalidUri() {
- controller.mSettingsListener.onSettingChanged(Uri.EMPTY, view.entry.sbn.userId, "1")
-
- verify(view, never()).getPrivateLayout()
- }
-
- @Test
- fun settingsListener_invalidUserId() {
- controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, -1000, "1")
- controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, -1000, null)
-
- verify(view, never()).getPrivateLayout()
- }
-
- @Test
- fun settingsListener_validUserId() {
- val childView: NotificationContentView = mock()
- whenever(view.privateLayout).thenReturn(childView)
-
- controller.mSettingsListener.onSettingChanged(
- BUBBLES_SETTING_URI, view.entry.sbn.userId, "1")
- verify(childView).setBubblesEnabledForUser(true)
-
- controller.mSettingsListener.onSettingChanged(
- BUBBLES_SETTING_URI, view.entry.sbn.userId, "9")
- verify(childView).setBubblesEnabledForUser(false)
- }
-
- @Test
- fun settingsListener_userAll() {
- val childView: NotificationContentView = mock()
- whenever(view.privateLayout).thenReturn(childView)
-
- val notification = Notification.Builder(mContext).build()
- val sbn = SbnBuilder().setNotification(notification)
- .setUser(UserHandle.of(USER_ALL))
- .build()
- whenever(view.entry).thenReturn(NotificationEntryBuilder()
- .setSbn(sbn)
- .setUser(UserHandle.of(USER_ALL))
- .build())
-
- controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, 9, "1")
- verify(childView).setBubblesEnabledForUser(true)
-
- controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, 1, "0")
- verify(childView).setBubblesEnabledForUser(false)
+ verify(childView, never()).isChangingPosition = any()
+ verify(view).removeChildNotification(eq(childView))
+ verify(listContainer).notifyGroupChildRemoved(eq(childView), eq(childrenContainer))
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
index bdd82fd..cf5b3cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -61,7 +63,7 @@
mBindPipeline = new NotifBindPipeline(
collection,
- mock(NotifBindPipelineLogger.class),
+ new NotifBindPipelineLogger(logcatLogBuffer()),
TestableLooper.get(this).getLooper());
mBindPipeline.setStage(mStage);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index ba6c7fd..0b90ebe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -250,9 +250,6 @@
.thenReturn(actionListMarginTarget)
view.setContainingNotification(mockContainingNotification)
- // Given: controller says bubbles are enabled for the user
- view.setBubblesEnabledForUser(true);
-
// When: call NotificationContentView.setExpandedChild() to set the expandedChild
view.expandedChild = mockExpandedChild
@@ -304,9 +301,6 @@
view.expandedChild = mockExpandedChild
assertEquals(notificationContentMargin, getMarginBottom(actionListMarginTarget))
- // Given: controller says bubbles are enabled for the user
- view.setBubblesEnabledForUser(true);
-
// When: call NotificationContentView.onNotificationUpdated() to update the
// NotificationEntry, which should show bubble button
view.onNotificationUpdated(createMockNotificationEntry(true))
@@ -411,6 +405,7 @@
val userMock: UserHandle = mock()
whenever(this.sbn).thenReturn(sbnMock)
whenever(sbnMock.user).thenReturn(userMock)
+ doReturn(showButton).whenever(view).shouldShowBubbleButton(this)
}
private fun createLinearLayoutWithBottomMargin(bottomMargin: Int): LinearLayout {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
deleted file mode 100644
index 2bccdca..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * 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.
- *
- */
-
-package com.android.systemui.statusbar.notification.row
-
-import android.app.ActivityManager
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.provider.Settings.Secure
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.notification.row.NotificationSettingsController.Listener
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.SecureSettings
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class NotificationSettingsControllerTest : SysuiTestCase() {
-
- val setting1: String = Secure.NOTIFICATION_BUBBLES
- val setting2: String = Secure.ACCESSIBILITY_ENABLED
- val settingUri1: Uri = Secure.getUriFor(setting1)
- val settingUri2: Uri = Secure.getUriFor(setting2)
-
- @Mock
- private lateinit var userTracker: UserTracker
- private lateinit var handler: Handler
- private lateinit var testableLooper: TestableLooper
- @Mock
- private lateinit var secureSettings: SecureSettings
- @Mock
- private lateinit var dumpManager: DumpManager
-
- @Captor
- private lateinit var userTrackerCallbackCaptor: ArgumentCaptor<UserTracker.Callback>
- @Captor
- private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
-
- private lateinit var controller: NotificationSettingsController
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- testableLooper = TestableLooper.get(this)
- handler = Handler(testableLooper.looper)
- allowTestableLooperAsMainThread()
- controller =
- NotificationSettingsController(
- userTracker,
- handler,
- secureSettings,
- dumpManager
- )
- }
-
- @After
- fun tearDown() {
- disallowTestableLooperAsMainThread()
- }
-
- @Test
- fun creationRegistersCallbacks() {
- verify(userTracker).addCallback(any(), any())
- verify(dumpManager).registerNormalDumpable(anyString(), eq(controller))
- }
- @Test
- fun updateContentObserverRegistration_onUserChange_noSettingsListeners() {
- verify(userTracker).addCallback(capture(userTrackerCallbackCaptor), any())
- val userCallback = userTrackerCallbackCaptor.value
- val userId = 9
-
- // When: User is changed
- userCallback.onUserChanged(userId, context)
-
- // Validate: Nothing to do, since we aren't monitoring settings
- verify(secureSettings, never()).unregisterContentObserver(any())
- verify(secureSettings, never()).registerContentObserverForUser(
- any(Uri::class.java), anyBoolean(), any(), anyInt())
- }
- @Test
- fun updateContentObserverRegistration_onUserChange_withSettingsListeners() {
- // When: someone is listening to a setting
- controller.addCallback(settingUri1,
- Mockito.mock(Listener::class.java))
-
- verify(userTracker).addCallback(capture(userTrackerCallbackCaptor), any())
- val userCallback = userTrackerCallbackCaptor.value
- val userId = 9
-
- // Then: User is changed
- userCallback.onUserChanged(userId, context)
-
- // Validate: The tracker is unregistered and re-registered with the new user
- verify(secureSettings).unregisterContentObserver(any())
- verify(secureSettings).registerContentObserverForUser(
- eq(settingUri1), eq(false), any(), eq(userId))
- }
-
- @Test
- fun addCallback_onlyFirstForUriRegistersObserver() {
- controller.addCallback(settingUri1,
- Mockito.mock(Listener::class.java))
- verify(secureSettings).registerContentObserverForUser(
- eq(settingUri1), eq(false), any(), eq(ActivityManager.getCurrentUser()))
-
- controller.addCallback(settingUri1,
- Mockito.mock(Listener::class.java))
- verify(secureSettings).registerContentObserverForUser(
- any(Uri::class.java), anyBoolean(), any(), anyInt())
- }
-
- @Test
- fun addCallback_secondUriRegistersObserver() {
- controller.addCallback(settingUri1,
- Mockito.mock(Listener::class.java))
- verify(secureSettings).registerContentObserverForUser(
- eq(settingUri1), eq(false), any(), eq(ActivityManager.getCurrentUser()))
-
- controller.addCallback(settingUri2,
- Mockito.mock(Listener::class.java))
- verify(secureSettings).registerContentObserverForUser(
- eq(settingUri2), eq(false), any(), eq(ActivityManager.getCurrentUser()))
- verify(secureSettings).registerContentObserverForUser(
- eq(settingUri1), anyBoolean(), any(), anyInt())
- }
-
- @Test
- fun removeCallback_lastUnregistersObserver() {
- val listenerSetting1 : Listener = mock()
- val listenerSetting2 : Listener = mock()
- controller.addCallback(settingUri1, listenerSetting1)
- verify(secureSettings).registerContentObserverForUser(
- eq(settingUri1), eq(false), any(), eq(ActivityManager.getCurrentUser()))
-
- controller.addCallback(settingUri2, listenerSetting2)
- verify(secureSettings).registerContentObserverForUser(
- eq(settingUri2), anyBoolean(), any(), anyInt())
-
- controller.removeCallback(settingUri2, listenerSetting2)
- verify(secureSettings, never()).unregisterContentObserver(any())
-
- controller.removeCallback(settingUri1, listenerSetting1)
- verify(secureSettings).unregisterContentObserver(any())
- }
-
- @Test
- fun addCallback_updatesCurrentValue() {
- whenever(secureSettings.getStringForUser(
- setting1, ActivityManager.getCurrentUser())).thenReturn("9")
- whenever(secureSettings.getStringForUser(
- setting2, ActivityManager.getCurrentUser())).thenReturn("5")
-
- val listenerSetting1a : Listener = mock()
- val listenerSetting1b : Listener = mock()
- val listenerSetting2 : Listener = mock()
-
- controller.addCallback(settingUri1, listenerSetting1a)
- controller.addCallback(settingUri1, listenerSetting1b)
- controller.addCallback(settingUri2, listenerSetting2)
-
- testableLooper.processAllMessages()
-
- verify(listenerSetting1a).onSettingChanged(
- settingUri1, ActivityManager.getCurrentUser(), "9")
- verify(listenerSetting1b).onSettingChanged(
- settingUri1, ActivityManager.getCurrentUser(), "9")
- verify(listenerSetting2).onSettingChanged(
- settingUri2, ActivityManager.getCurrentUser(), "5")
- }
-
- @Test
- fun removeCallback_noMoreUpdates() {
- whenever(secureSettings.getStringForUser(
- setting1, ActivityManager.getCurrentUser())).thenReturn("9")
-
- val listenerSetting1a : Listener = mock()
- val listenerSetting1b : Listener = mock()
-
- // First, register
- controller.addCallback(settingUri1, listenerSetting1a)
- controller.addCallback(settingUri1, listenerSetting1b)
- testableLooper.processAllMessages()
-
- verify(secureSettings).registerContentObserverForUser(
- any(Uri::class.java), anyBoolean(), capture(settingsObserverCaptor), anyInt())
- verify(listenerSetting1a).onSettingChanged(
- settingUri1, ActivityManager.getCurrentUser(), "9")
- verify(listenerSetting1b).onSettingChanged(
- settingUri1, ActivityManager.getCurrentUser(), "9")
- Mockito.clearInvocations(listenerSetting1b)
- Mockito.clearInvocations(listenerSetting1a)
-
- // Remove one of them
- controller.removeCallback(settingUri1, listenerSetting1a)
-
- // On update, only remaining listener should get the callback
- settingsObserverCaptor.value.onChange(false, settingUri1)
- testableLooper.processAllMessages()
-
- verify(listenerSetting1a, never()).onSettingChanged(
- settingUri1, ActivityManager.getCurrentUser(), "9")
- verify(listenerSetting1b).onSettingChanged(
- settingUri1, ActivityManager.getCurrentUser(), "9")
- }
-
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index d21029d..1ab2b38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -21,6 +21,7 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static org.junit.Assert.assertEquals;
@@ -178,13 +179,13 @@
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
mock(NotifInflationErrorManager.class),
- mock(RowContentBindStageLogger.class));
+ new RowContentBindStageLogger(logcatLogBuffer()));
CommonNotifCollection collection = mock(CommonNotifCollection.class);
mBindPipeline = new NotifBindPipeline(
collection,
- mock(NotifBindPipelineLogger.class),
+ new NotifBindPipelineLogger(logcatLogBuffer()),
mTestLooper.getLooper());
mBindPipeline.setStage(mBindStage);
@@ -596,7 +597,7 @@
mock(NotificationGutsManager.class),
mDismissibilityProvider,
mock(MetricsLogger.class),
- mock(NotificationChildrenContainerLogger.class),
+ new NotificationChildrenContainerLogger(logcatLogBuffer()),
mock(SmartReplyConstants.class),
mock(SmartReplyController.class),
mFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index 7c99568..32f0fe7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
@@ -68,7 +69,7 @@
mRowContentBindStage = new RowContentBindStage(
mBinder,
mock(NotifInflationErrorManager.class),
- mock(RowContentBindStageLogger.class));
+ new RowContentBindStageLogger(logcatLogBuffer()));
mRowContentBindStage.createStageParams(mEntry);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 07eadf7c..6f431be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -154,8 +155,10 @@
@Mock private VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
@Mock private ShadeController mShadeController;
@Mock private InteractionJankMonitor mJankMonitor;
- @Mock private StackStateLogger mStackLogger;
- @Mock private NotificationStackScrollLogger mLogger;
+ private final StackStateLogger mStackLogger = new StackStateLogger(logcatLogBuffer(),
+ logcatLogBuffer());
+ private final NotificationStackScrollLogger mLogger = new NotificationStackScrollLogger(
+ logcatLogBuffer(), logcatLogBuffer(), logcatLogBuffer());
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Mock private NotificationTargetsHelper mNotificationTargetsHelper;
@Mock private SecureSettings mSecureSettings;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 4872deb..61da901 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -159,6 +159,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
@@ -514,6 +515,7 @@
mNotificationShelfController,
mStackScrollerController,
mNotificationPresenter,
+ new NotificationExpansionRepository(),
mDozeParameters,
mScrimController,
mLockscreenWallpaperLazy,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 6fda56c..72522ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -30,6 +32,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.AlertingNotificationManager;
@@ -61,7 +64,8 @@
private HeadsUpManagerPhone mHeadsUpManager;
- @Mock private HeadsUpManagerLogger mHeadsUpManagerLogger;
+ private final HeadsUpManagerLogger mHeadsUpManagerLogger = new HeadsUpManagerLogger(
+ logcatLogBuffer());
@Mock private GroupMembershipManager mGroupManager;
@Mock private VisualStabilityProvider mVSProvider;
@Mock private StatusBarStateController mStatusBarStateController;
@@ -104,11 +108,13 @@
}
}
+ @Override
protected AlertingNotificationManager createAlertingNotificationManager() {
return mHeadsUpManager;
}
@Before
+ @Override
public void setUp() {
AccessibilityManagerWrapper accessibilityMgr =
mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
@@ -116,8 +122,10 @@
.thenReturn(TEST_AUTO_DISMISS_TIME);
when(mVSProvider.isReorderingAllowed()).thenReturn(true);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
- super.setUp();
+ mContext.getOrCreateTestableResources().addOverride(
+ R.integer.ambient_notification_extension_time, 500);
+ super.setUp();
mHeadsUpManager = new TestableHeadsUpManagerPhone(
mContext,
mHeadsUpManagerLogger,
@@ -134,8 +142,9 @@
}
@After
+ @Override
public void tearDown() {
- mTestHandler.removeCallbacksAndMessages(null);
+ super.tearDown();
}
@Test
@@ -181,7 +190,6 @@
assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.getKey()));
}
-
@Test
public void testExtendHeadsUp() {
mHeadsUpManager.showNotification(mEntry);
@@ -189,7 +197,7 @@
() -> mLivesPastNormalTime = mHeadsUpManager.isAlerting(mEntry.getKey());
mTestHandler.postDelayed(pastNormalTimeRunnable,
TEST_AUTO_DISMISS_TIME + mHeadsUpManager.mExtensionTime / 2);
- mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_TIMEOUT_TIME);
+ mTestHandler.postDelayed(mTestTimeoutRunnable, TEST_TIMEOUT_TIME);
mHeadsUpManager.extendHeadsUp();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 33144f2..3151ad1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -20,6 +20,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
import static com.google.common.truth.Truth.assertThat;
@@ -42,6 +43,7 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.os.Handler;
@@ -72,7 +74,6 @@
import com.android.systemui.power.data.repository.FakePowerRepository;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.ShadeControllerImpl;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.NotificationClickNotifier;
@@ -85,6 +86,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -220,8 +222,8 @@
HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class);
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider =
new NotificationLaunchAnimatorControllerProvider(
- mock(NotificationShadeWindowViewController.class), mock(
- NotificationListContainer.class),
+ new NotificationExpansionRepository(),
+ mock(NotificationListContainer.class),
headsUpManager,
mJankMonitor);
mNotificationActivityStarter =
@@ -248,7 +250,7 @@
mock(StatusBarRemoteInputCallback.class),
mActivityIntentHelper,
mock(MetricsLogger.class),
- mock(StatusBarNotificationActivityStarterLogger.class),
+ new StatusBarNotificationActivityStarterLogger(logcatLogBuffer()),
mOnUserInteractionCallback,
mock(NotificationPresenter.class),
mock(ShadeViewController.class),
@@ -410,10 +412,12 @@
@Test
public void testOnFullScreenIntentWhenDozing_wakeUpDevice() {
// GIVEN entry that can has a full screen intent that can show
+ PendingIntent fullScreenIntent = PendingIntent.getActivity(mContext, 1,
+ new Intent("fake_full_screen"), PendingIntent.FLAG_IMMUTABLE);
Notification.Builder nb = new Notification.Builder(mContext, "a")
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
- .setFullScreenIntent(mock(PendingIntent.class), true);
+ .setFullScreenIntent(fullScreenIntent, true);
StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0,
"tag" + System.currentTimeMillis(), 0, 0,
nb.build(), new UserHandle(0), null, 0);
@@ -437,6 +441,7 @@
// GIVEN entry that can has a full screen intent that can show
PendingIntent mockFullScreenIntent = mock(PendingIntent.class);
when(mockFullScreenIntent.getCreatorUid()).thenReturn(kTestUid);
+ when(mockFullScreenIntent.getIntent()).thenReturn(new Intent("fake_full_screen"));
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = new ActivityInfo();
resolveInfo.activityInfo.name = kTestActivityName;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 5dcb901..823155b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -45,12 +45,12 @@
import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
-import androidx.core.animation.AnimatorTestRule;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.log.LogBuffer;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
index 2617613..2ce060c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
@@ -19,9 +19,9 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
-import androidx.core.animation.AnimatorTestRule
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.AnimatorTestRule
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index a797e03..14edf3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
@@ -26,6 +28,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -70,7 +73,7 @@
@Mock private NotificationEntry mEntry;
@Mock private StatusBarNotification mSbn;
@Mock private Notification mNotification;
- @Mock private HeadsUpManagerLogger mLogger;
+ private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
@Mock private AccessibilityManagerWrapper mAccessibilityMgr;
private final class TestableHeadsUpManager extends HeadsUpManager {
@@ -86,14 +89,17 @@
}
}
+ @Override
protected AlertingNotificationManager createAlertingNotificationManager() {
return mHeadsUpManager;
}
@Before
+ @Override
public void setUp() {
initMocks(this);
when(mEntry.getSbn()).thenReturn(mSbn);
+ when(mEntry.getKey()).thenReturn("entryKey");
when(mSbn.getNotification()).thenReturn(mNotification);
super.setUp();
mHeadsUpManager = new TestableHeadsUpManager(mContext, mLogger, mTestHandler,
@@ -101,8 +107,9 @@
}
@After
+ @Override
public void tearDown() {
- mTestHandler.removeCallbacksAndMessages(null);
+ super.tearDown();
}
@Test
@@ -169,7 +176,7 @@
() -> mLivesPastNormalTime = mHeadsUpManager.isAlerting(mEntry.getKey());
mTestHandler.postDelayed(pastNormalTimeRunnable,
(TEST_A11Y_AUTO_DISMISS_TIME + TEST_AUTO_DISMISS_TIME) / 2);
- mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_A11Y_TIMEOUT_TIME);
+ mTestHandler.postDelayed(mTestTimeoutRunnable, TEST_A11Y_TIMEOUT_TIME);
TestableLooper.get(this).processMessages(2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 7c285b8..ef39ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -62,7 +62,6 @@
import android.window.WindowOnBackInvokedDispatcher;
import androidx.annotation.NonNull;
-import androidx.core.animation.AnimatorTestRule;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -70,6 +69,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 03834e0..8c21fac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -21,6 +21,7 @@
import android.os.VibrationEffect
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@@ -41,6 +42,8 @@
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -91,6 +94,7 @@
private lateinit var fakeExecutor: FakeExecutor
private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var uiEventLogger: TemporaryViewUiEventLogger
+ private val featureFlags = FakeFeatureFlags()
@Before
fun setUp() {
@@ -127,8 +131,10 @@
fakeWakeLockBuilder,
fakeClock,
uiEventLogger,
+ featureFlags
)
underTest.start()
+ featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
}
@Test
@@ -488,6 +494,23 @@
)
}
+ @Test
+ fun displayView_oneWayHapticsEnabled_usesPerformHapticFeedback() {
+ featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
+ val constant: Int = HapticFeedbackConstants.CONFIRM
+ underTest.displayView(
+ createChipbarInfo(
+ Icon.Resource(R.id.check_box, null),
+ Text.Loaded("text"),
+ endItem = null,
+ vibrationEffect = null,
+ vibrationConstant = constant
+ )
+ )
+
+ verify(vibratorHelper).performHapticFeedback(any(), eq(constant))
+ }
+
/** Regression test for b/266119467. */
@Test
fun displayView_animationFailure_viewsStillBecomeVisible() {
@@ -706,12 +729,14 @@
endItem: ChipbarEndItem?,
vibrationEffect: VibrationEffect? = null,
allowSwipeToDismiss: Boolean = false,
+ vibrationConstant: Int = HapticFeedbackConstants.NO_HAPTICS,
): ChipbarInfo {
return ChipbarInfo(
TintedIcon(startIcon, tint = null),
text,
endItem,
vibrationEffect,
+ vibrationConstant,
allowSwipeToDismiss,
windowTitle = WINDOW_TITLE,
wakeReason = WAKE_REASON,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index f299ad4..7593e84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -18,6 +18,8 @@
import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -63,7 +65,6 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
@@ -110,8 +111,7 @@
@Mock private IAccessibilityManager mAccessibilityManager;
@Mock private PluginManager mPluginManager;
@Mock private DumpManager mDumpManager;
- @Mock private ToastLogger mToastLogger;
- @Mock private FeatureFlags mFeatureFlags;
+ private final ToastLogger mToastLogger = spy(new ToastLogger(logcatLogBuffer()));
@Mock private PackageManager mPackageManager;
@Mock private ITransientNotificationCallback mCallback;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index bf54d42..aa49287 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -20,6 +20,7 @@
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Handler
+import android.os.Looper
import android.testing.AndroidTestingRunner
import androidx.core.util.Consumer
import androidx.test.filters.SmallTest
@@ -38,6 +39,7 @@
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.fail
import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
@@ -55,16 +57,20 @@
@Mock private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider
- @Mock private lateinit var handler: Handler
-
@Mock private lateinit var rotationChangeProvider: RotationChangeProvider
@Mock private lateinit var unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider
@Mock private lateinit var resources: Resources
+ @Mock private lateinit var handler: Handler
+
+ @Mock private lateinit var mainLooper: Looper
+
@Mock private lateinit var context: Context
+ @Mock private lateinit var thread: Thread
+
@Captor private lateinit var rotationListener: ArgumentCaptor<RotationListener>
private val foldProvider = TestFoldProvider()
@@ -89,6 +95,11 @@
override val halfFoldedTimeoutMillis: Int
get() = HALF_OPENED_TIMEOUT_MILLIS.toInt()
}
+ whenever(mainLooper.isCurrentThread).thenReturn(true)
+ whenever(handler.looper).thenReturn(mainLooper)
+ whenever(mainLooper.isCurrentThread).thenReturn(true)
+ whenever(mainLooper.thread).thenReturn(thread)
+ whenever(thread.name).thenReturn("backgroundThread")
whenever(context.resources).thenReturn(resources)
whenever(context.mainExecutor).thenReturn(mContext.mainExecutor)
@@ -435,6 +446,26 @@
}
@Test
+ fun startOnlyOnce_whenStartTriggeredThrice_startOnlyOnce() {
+ foldStateProvider.start()
+ foldStateProvider.start()
+ foldStateProvider.start()
+
+ assertThat(foldProvider.getNumberOfCallbacks()).isEqualTo(1)
+ }
+
+ @Test(expected = AssertionError::class)
+ fun startMethod_whileNotOnMainThread_throwsException() {
+ whenever(mainLooper.isCurrentThread).thenReturn(true)
+ try {
+ foldStateProvider.start()
+ fail("Should have thrown AssertionError: should be called from the main thread.")
+ } catch (e: AssertionError) {
+ assertThat(e.message).contains("backgroundThread")
+ }
+ }
+
+ @Test
fun startClosingEvent_whileNotOnKeyguard_triggersAfterThreshold() {
setKeyguardVisibility(visible = false)
setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES)
@@ -658,6 +689,10 @@
fun notifyFolded(isFolded: Boolean) {
callbacks.forEach { it.onFoldUpdated(isFolded) }
}
+
+ fun getNumberOfCallbacks(): Int{
+ return callbacks.size
+ }
}
private class TestScreenOnStatusProvider : ScreenStatusProvider {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
index 8e4f184..53e5e7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
@@ -188,6 +188,25 @@
}
@Test
+ public void queryWalletCards_walletEnabled_queryMultipleCards() {
+ mController.queryWalletCards(mCardsRetriever, 5);
+
+ verify(mQuickAccessWalletClient)
+ .getWalletCards(
+ eq(MoreExecutors.directExecutor()), mRequestCaptor.capture(),
+ eq(mCardsRetriever));
+
+ GetWalletCardsRequest request = mRequestCaptor.getValue();
+ assertEquals(5, mRequestCaptor.getValue().getMaxCards());
+ assertEquals(
+ mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width),
+ request.getCardWidthPx());
+ assertEquals(
+ mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height),
+ request.getCardHeightPx());
+ }
+
+ @Test
public void queryWalletCards_walletFeatureNotAvailable_noQuery() {
when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
index 3901d72..d5bdb59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
@@ -215,7 +215,7 @@
cards: List<WalletCard> = emptyList(),
shouldFail: Boolean = false
) {
- whenever(walletController.queryWalletCards(any())).thenAnswer { invocation ->
+ whenever(walletController.queryWalletCards(any(), anyInt())).thenAnswer { invocation ->
with(
invocation.arguments[0] as QuickAccessWalletClient.OnWalletCardsRetrievedCallback
) {
diff --git a/packages/SystemUI/tests/utils/src/android/animation/AnimatorIsolationWorkaroundRule.kt b/packages/SystemUI/tests/utils/src/android/animation/AnimatorIsolationWorkaroundRule.kt
new file mode 100644
index 0000000..b74ddae
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/animation/AnimatorIsolationWorkaroundRule.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+package android.animation
+
+import android.os.Looper
+import android.util.Log
+import com.android.systemui.util.test.TestExceptionDeferrer
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * This rule is intended to be used by System UI tests that are otherwise blocked from using
+ * animators because of [PlatformAnimatorIsolationRule]. It is preferred that test authors use
+ * [AnimatorTestRule], as that rule allows test authors to step through animations and removes the
+ * need for tests to handle multiple threads. However, many System UI tests were written before this
+ * was conceivable, so this rule is intended to support those legacy tests.
+ */
+class AnimatorIsolationWorkaroundRule(
+ private val requiredLooper: Looper? = Looper.getMainLooper(),
+) : TestRule {
+ private inner class IsolationWorkaroundHandler(ruleThread: Thread) : AnimationHandler() {
+ private val exceptionDeferrer = TestExceptionDeferrer(TAG, ruleThread)
+ private val addedCallbacks = mutableSetOf<AnimationFrameCallback>()
+
+ fun tearDownAndThrowDeferred() {
+ addedCallbacks.forEach { super.removeCallback(it) }
+ exceptionDeferrer.throwDeferred()
+ }
+
+ override fun addAnimationFrameCallback(callback: AnimationFrameCallback?, delay: Long) {
+ checkLooper()
+ if (callback != null) {
+ addedCallbacks.add(callback)
+ }
+ super.addAnimationFrameCallback(callback, delay)
+ }
+
+ override fun addOneShotCommitCallback(callback: AnimationFrameCallback?) {
+ checkLooper()
+ super.addOneShotCommitCallback(callback)
+ }
+
+ override fun removeCallback(callback: AnimationFrameCallback?) {
+ super.removeCallback(callback)
+ }
+
+ override fun setProvider(provider: AnimationFrameCallbackProvider?) {
+ checkLooper()
+ super.setProvider(provider)
+ }
+
+ override fun autoCancelBasedOn(objectAnimator: ObjectAnimator?) {
+ checkLooper()
+ super.autoCancelBasedOn(objectAnimator)
+ }
+
+ private fun checkLooper() {
+ exceptionDeferrer.check(requiredLooper == null || Looper.myLooper() == requiredLooper) {
+ "Animations are being registered on a different looper than the expected one!" +
+ " expected=$requiredLooper actual=${Looper.myLooper()}"
+ }
+ }
+ }
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return object : Statement() {
+ @Throws(Throwable::class)
+ override fun evaluate() {
+ val workaroundHandler = IsolationWorkaroundHandler(Thread.currentThread())
+ val prevInstance = AnimationHandler.setTestHandler(workaroundHandler)
+ check(PlatformAnimatorIsolationRule.isIsolatingHandler(prevInstance)) {
+ "AnimatorIsolationWorkaroundRule must be used within " +
+ "PlatformAnimatorIsolationRule, but test handler was $prevInstance"
+ }
+ try {
+ base.evaluate()
+ val count = AnimationHandler.getAnimationCount()
+ if (count > 0) {
+ Log.w(TAG, "Animations still running: $count")
+ }
+ } finally {
+ val handlerAtEnd = AnimationHandler.setTestHandler(prevInstance)
+ check(workaroundHandler == handlerAtEnd) {
+ "Test handler was altered: expected=$workaroundHandler actual=$handlerAtEnd"
+ }
+ // Pass or fail, errors caught here should be the reason the test fails
+ workaroundHandler.tearDownAndThrowDeferred()
+ }
+ }
+ }
+ }
+
+ private companion object {
+ private const val TAG = "AnimatorIsolationWorkaroundRule"
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java b/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java
new file mode 100644
index 0000000..19c68e8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java
@@ -0,0 +1,264 @@
+/*
+ * 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.
+ */
+
+package android.animation;
+
+import android.animation.AnimationHandler.AnimationFrameCallback;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.util.AndroidRuntimeException;
+import android.view.Choreographer;
+
+import com.android.internal.util.Preconditions;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * JUnit {@link TestRule} that can be used to run {@link Animator}s without actually waiting for the
+ * duration of the animation. This also helps the test to be written in a deterministic manner.
+ *
+ * Create an instance of {@code AnimatorTestRule} and specify it as a {@link org.junit.Rule}
+ * of the test class. Use {@link #advanceTimeBy(long)} to advance animators that have been started.
+ * Note that {@link #advanceTimeBy(long)} should be called from the same thread you have used to
+ * start the animator.
+ *
+ * <pre>
+ * {@literal @}SmallTest
+ * {@literal @}RunWith(AndroidJUnit4.class)
+ * public class SampleAnimatorTest {
+ *
+ * {@literal @}Rule
+ * public AnimatorTestRule sAnimatorTestRule = new AnimatorTestRule();
+ *
+ * {@literal @}UiThreadTest
+ * {@literal @}Test
+ * public void sample() {
+ * final ValueAnimator animator = ValueAnimator.ofInt(0, 1000);
+ * animator.setDuration(1000L);
+ * assertThat(animator.getAnimatedValue(), is(0));
+ * animator.start();
+ * sAnimatorTestRule.advanceTimeBy(500L);
+ * assertThat(animator.getAnimatedValue(), is(500));
+ * }
+ * }
+ * </pre>
+ */
+public final class AnimatorTestRule implements TestRule {
+
+ private final Object mLock = new Object();
+ private final TestHandler mTestHandler = new TestHandler();
+ /**
+ * initializing the start time with {@link SystemClock#uptimeMillis()} reduces the discrepancies
+ * with various internals of classes like ValueAnimator which can sometimes read that clock via
+ * {@link android.view.animation.AnimationUtils#currentAnimationTimeMillis()}.
+ */
+ private final long mStartTime = SystemClock.uptimeMillis();
+ private long mTotalTimeDelta = 0;
+
+ @NonNull
+ @Override
+ public Statement apply(@NonNull final Statement base, @NonNull Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ AnimationHandler objAtStart = AnimationHandler.setTestHandler(mTestHandler);
+ try {
+ base.evaluate();
+ } finally {
+ AnimationHandler objAtEnd = AnimationHandler.setTestHandler(objAtStart);
+ if (mTestHandler != objAtEnd) {
+ // pass or fail, inner logic not restoring the handler needs to be reported.
+ // noinspection ThrowFromFinallyBlock
+ throw new IllegalStateException("Test handler was altered: expected="
+ + mTestHandler + " actual=" + objAtEnd);
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * If any new {@link Animator}s have been registered since the last time the frame time was
+ * advanced, initialize them with the current frame time. Failing to do this will result in the
+ * animations beginning on the *next* advancement instead, so this is done automatically for
+ * test authors inside of {@link #advanceTimeBy}. However this is exposed in case authors want
+ * to validate operations performed by onStart listeners.
+ * <p>
+ * NOTE: This is only required of the platform ValueAnimator because its start() method calls
+ * {@link AnimationHandler#addAnimationFrameCallback} BEFORE it calls startAnimation(), so this
+ * rule can't synchronously trigger the callback at that time.
+ */
+ public void initNewAnimators() {
+ requireLooper("AnimationTestRule#initNewAnimators()");
+ long currentTime = getCurrentTime();
+ List<AnimationFrameCallback> newCallbacks = new ArrayList<>(mTestHandler.mNewCallbacks);
+ mTestHandler.mNewCallbacks.clear();
+ for (AnimationFrameCallback newCallback : newCallbacks) {
+ newCallback.doAnimationFrame(currentTime);
+ }
+ }
+
+ /**
+ * Advances the animation clock by the given amount of delta in milliseconds. This call will
+ * produce an animation frame to all the ongoing animations. This method needs to be
+ * called on the same thread as {@link Animator#start()}.
+ *
+ * @param timeDelta the amount of milliseconds to advance
+ */
+ public void advanceTimeBy(long timeDelta) {
+ advanceTimeBy(timeDelta, null);
+ }
+
+ /**
+ * Advances the animation clock by the given amount of delta in milliseconds. This call will
+ * produce an animation frame to all the ongoing animations. This method needs to be
+ * called on the same thread as {@link Animator#start()}.
+ * <p>
+ * This method is not for test authors, but for rule authors to ensure that multiple animators
+ * can be advanced in sync.
+ *
+ * @param timeDelta the amount of milliseconds to advance
+ * @param preFrameAction a consumer to be passed the timeDelta following the time advancement
+ * but prior to the frame production.
+ */
+ public void advanceTimeBy(long timeDelta, @Nullable Consumer<Long> preFrameAction) {
+ Preconditions.checkArgumentNonnegative(timeDelta, "timeDelta must not be negative");
+ requireLooper("AnimationTestRule#advanceTimeBy(long)");
+ if (timeDelta == 0) {
+ // If time is not being advanced, all animators will get a tick; don't double tick these
+ mTestHandler.mNewCallbacks.clear();
+ } else {
+ // before advancing time, start new animators with the current time
+ initNewAnimators();
+ }
+ synchronized (mLock) {
+ // advance time
+ mTotalTimeDelta += timeDelta;
+ }
+ if (preFrameAction != null) {
+ preFrameAction.accept(timeDelta);
+ // After letting other code run, clear any new callbacks to avoid double-ticking them
+ mTestHandler.mNewCallbacks.clear();
+ }
+ // produce a frame
+ mTestHandler.doFrame();
+ }
+
+ /**
+ * Returns the current time in milliseconds tracked by AnimationHandler. Note that this is a
+ * different time than the time tracked by {@link SystemClock} This method needs to be called on
+ * the same thread as {@link Animator#start()}.
+ */
+ public long getCurrentTime() {
+ requireLooper("AnimationTestRule#getCurrentTime()");
+ synchronized (mLock) {
+ return mStartTime + mTotalTimeDelta;
+ }
+ }
+
+ private static void requireLooper(String method) {
+ if (Looper.myLooper() == null) {
+ throw new AndroidRuntimeException(method + " may only be called on Looper threads");
+ }
+ }
+
+ private class TestHandler extends AnimationHandler {
+ public final TestProvider mTestProvider = new TestProvider();
+ private final List<AnimationFrameCallback> mNewCallbacks = new ArrayList<>();
+
+ TestHandler() {
+ setProvider(mTestProvider);
+ }
+
+ public void doFrame() {
+ mTestProvider.animateFrame();
+ mTestProvider.commitFrame();
+ }
+
+ @Override
+ public void addAnimationFrameCallback(AnimationFrameCallback callback, long delay) {
+ // NOTE: using the delay is infeasible because the AnimationHandler uses
+ // SystemClock.uptimeMillis(); -- If we fix this to use an overridable method, then we
+ // could fix this for tests.
+ super.addAnimationFrameCallback(callback, 0);
+ if (delay <= 0) {
+ mNewCallbacks.add(callback);
+ }
+ }
+
+ @Override
+ public void removeCallback(AnimationFrameCallback callback) {
+ super.removeCallback(callback);
+ mNewCallbacks.remove(callback);
+ }
+ }
+
+ private class TestProvider implements AnimationHandler.AnimationFrameCallbackProvider {
+ private long mFrameDelay = 10;
+ private Choreographer.FrameCallback mFrameCallback = null;
+ private final List<Runnable> mCommitCallbacks = new ArrayList<>();
+
+ public void animateFrame() {
+ Choreographer.FrameCallback frameCallback = mFrameCallback;
+ mFrameCallback = null;
+ if (frameCallback != null) {
+ frameCallback.doFrame(getFrameTime());
+ }
+ }
+
+ public void commitFrame() {
+ List<Runnable> commitCallbacks = new ArrayList<>(mCommitCallbacks);
+ mCommitCallbacks.clear();
+ for (Runnable commitCallback : commitCallbacks) {
+ commitCallback.run();
+ }
+ }
+
+ @Override
+ public void postFrameCallback(Choreographer.FrameCallback callback) {
+ assert mFrameCallback == null;
+ mFrameCallback = callback;
+ }
+
+ @Override
+ public void postCommitCallback(Runnable runnable) {
+ mCommitCallbacks.add(runnable);
+ }
+
+ @Override
+ public void setFrameDelay(long delay) {
+ mFrameDelay = delay;
+ }
+
+ @Override
+ public long getFrameDelay() {
+ return mFrameDelay;
+ }
+
+ @Override
+ public long getFrameTime() {
+ return getCurrentTime();
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt b/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt
new file mode 100644
index 0000000..43a26f3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package android.animation
+
+import com.android.systemui.util.test.TestExceptionDeferrer
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * This rule is used by [com.android.systemui.SysuiTestCase] to fail any test which attempts to
+ * start a Platform [Animator] without using [android.animation.AnimatorTestRule].
+ *
+ * TODO(b/291645410): enable this; currently this causes hundreds of test failures.
+ */
+class PlatformAnimatorIsolationRule : TestRule {
+
+ private class IsolatingAnimationHandler(ruleThread: Thread) : AnimationHandler() {
+ private val exceptionDeferrer = TestExceptionDeferrer(TAG, ruleThread)
+ override fun addOneShotCommitCallback(callback: AnimationFrameCallback?) = onError()
+ override fun removeCallback(callback: AnimationFrameCallback?) = onError()
+ override fun setProvider(provider: AnimationFrameCallbackProvider?) = onError()
+ override fun autoCancelBasedOn(objectAnimator: ObjectAnimator?) = onError()
+ override fun addAnimationFrameCallback(callback: AnimationFrameCallback?, delay: Long) =
+ onError()
+
+ private fun onError() =
+ exceptionDeferrer.fail(
+ "Test's animations are not isolated! " +
+ "Did you forget to add an AnimatorTestRule to your test class?"
+ )
+
+ fun throwDeferred() = exceptionDeferrer.throwDeferred()
+ }
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return object : Statement() {
+ @Throws(Throwable::class)
+ override fun evaluate() {
+ val isolationHandler = IsolatingAnimationHandler(Thread.currentThread())
+ val originalHandler = AnimationHandler.setTestHandler(isolationHandler)
+ try {
+ base.evaluate()
+ } finally {
+ val handlerAtEnd = AnimationHandler.setTestHandler(originalHandler)
+ check(isolationHandler == handlerAtEnd) {
+ "Test handler was altered: expected=$isolationHandler actual=$handlerAtEnd"
+ }
+ // Pass or fail, a deferred exception should be the failure reason
+ isolationHandler.throwDeferred()
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "PlatformAnimatorIsolationRule"
+
+ fun isIsolatingHandler(handler: AnimationHandler?): Boolean =
+ handler is IsolatingAnimationHandler
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt b/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt
index 026372f..7a97029 100644
--- a/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt
+++ b/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt
@@ -16,40 +16,51 @@
package androidx.core.animation
+import com.android.systemui.util.test.TestExceptionDeferrer
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
+/**
+ * This rule is used by [com.android.systemui.SysuiTestCase] to fail any test which attempts to
+ * start an AndroidX [Animator] without using [androidx.core.animation.AnimatorTestRule].
+ */
class AndroidXAnimatorIsolationRule : TestRule {
- private class TestAnimationHandler : AnimationHandler(null) {
- override fun addAnimationFrameCallback(callback: AnimationFrameCallback?) = doFail()
- override fun removeCallback(callback: AnimationFrameCallback?) = doFail()
- override fun onAnimationFrame(frameTime: Long) = doFail()
- override fun setFrameDelay(frameDelay: Long) = doFail()
- override fun getFrameDelay(): Long = doFail()
+ private class IsolatingAnimationHandler(ruleThread: Thread) : AnimationHandler(null) {
+ private val exceptionDeferrer = TestExceptionDeferrer(TAG, ruleThread)
+ override fun addAnimationFrameCallback(callback: AnimationFrameCallback?) = onError()
+ override fun removeCallback(callback: AnimationFrameCallback?) = onError()
+ override fun onAnimationFrame(frameTime: Long) = onError()
+ override fun setFrameDelay(frameDelay: Long) = onError()
+
+ private fun onError() =
+ exceptionDeferrer.fail(
+ "Test's animations are not isolated! " +
+ "Did you forget to add an AnimatorTestRule to your test class?"
+ )
+
+ fun throwDeferred() = exceptionDeferrer.throwDeferred()
}
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
- AnimationHandler.setTestHandler(testHandler)
+ val isolationHandler = IsolatingAnimationHandler(Thread.currentThread())
+ AnimationHandler.setTestHandler(isolationHandler)
try {
base.evaluate()
} finally {
AnimationHandler.setTestHandler(null)
+ // Pass or fail, a deferred exception should be the failure reason
+ isolationHandler.throwDeferred()
}
}
}
}
- companion object {
- private val testHandler = TestAnimationHandler()
- private fun doFail(): Nothing =
- error(
- "Test's animations are not isolated! " +
- "Did you forget to add an AnimatorTestRule to your test class?"
- )
+ private companion object {
+ private const val TAG = "AndroidXAnimatorIsolationRule"
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt
new file mode 100644
index 0000000..0ced19e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.animation
+
+import java.util.function.Consumer
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * A rule that wraps both [androidx.core.animation.AnimatorTestRule] and
+ * [android.animation.AnimatorTestRule] such that the clocks of the two animation handlers can be
+ * advanced together.
+ */
+class AnimatorTestRule : TestRule {
+ private val androidxRule = androidx.core.animation.AnimatorTestRule()
+ private val platformRule = android.animation.AnimatorTestRule()
+ private val advanceAndroidXTimeBy =
+ Consumer<Long> { timeDelta -> androidxRule.advanceTimeBy(timeDelta) }
+
+ /**
+ * Chain is for simplicity not to force a particular order; order should not matter, because
+ * each rule affects a different AnimationHandler classes, and no callbacks to code under test
+ * should be triggered by these rules
+ */
+ private val ruleChain = RuleChain.emptyRuleChain().around(androidxRule).around(platformRule)
+
+ override fun apply(base: Statement, description: Description): Statement =
+ ruleChain.apply(base, description)
+
+ /**
+ * Advances the animation clock by the given amount of delta in milliseconds. This call will
+ * produce an animation frame to all the ongoing animations.
+ *
+ * @param timeDelta the amount of milliseconds to advance
+ */
+ fun advanceTimeBy(timeDelta: Long) {
+ // NOTE: To avoid errors with order, we have to ensure that we advance the time within both
+ // rules before either rule does its frame output. Failing to do this could cause the
+ // animation from one to start later than the other.
+ platformRule.advanceTimeBy(timeDelta, advanceAndroidXTimeBy)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 70d15a0..6cffb66 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -92,6 +92,7 @@
)
}
}
+
private val context = test.context
fun fakeSceneContainerRepository(
@@ -124,6 +125,7 @@
): SceneInteractor {
return SceneInteractor(
repository = repository,
+ logger = mock(),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/test/TestExceptionDeferrer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/test/TestExceptionDeferrer.kt
new file mode 100644
index 0000000..90281ca
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/test/TestExceptionDeferrer.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.util.test
+
+import android.util.Log
+
+/**
+ * Helper class that intercepts test errors which may be occurring on the wrong thread, and saves
+ * them so that they can be rethrown back on the correct thread.
+ */
+class TestExceptionDeferrer(private val tag: String, private val testThread: Thread) {
+ private val deferredErrors = mutableListOf<IllegalStateException>()
+
+ /** Ensure the [value] is `true`; otherwise [fail] with the produced [message] */
+ fun check(value: Boolean, message: () -> Any?) {
+ if (value) return
+ fail(message().toString())
+ }
+
+ /**
+ * If the [Thread.currentThread] is the [testThread], then [error], otherwise [Log] and defer
+ * the error until [throwDeferred] is called.
+ */
+ fun fail(message: String) {
+ if (testThread == Thread.currentThread()) {
+ error(message)
+ } else {
+ val exception = IllegalStateException(message)
+ Log.e(tag, "Deferring error: ", exception)
+ deferredErrors.add(exception)
+ }
+ }
+
+ /** If any [fail] or failed [check] has happened, throw the first one. */
+ fun throwDeferred() {
+ deferredErrors.firstOrNull()?.let { firstError ->
+ Log.e(tag, "Deferred errors: ${deferredErrors.size}")
+ deferredErrors.clear()
+ throw firstError
+ }
+ }
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 8c5244e..6743515 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -62,6 +62,7 @@
private val hingeAngleListener = HingeAngleListener()
private val screenListener = ScreenStatusListener()
private val foldStateListener = FoldStateListener()
+ private val mainLooper = handler.looper
private val timeoutRunnable = Runnable { cancelAnimation() }
private val rotationListener = RotationListener {
if (isTransitionInProgress) cancelAnimation()
@@ -77,22 +78,28 @@
private var isFolded = false
private var isScreenOn = false
private var isUnfoldHandled = true
+ private var isStarted = false
override fun start() {
+ assertMainThread()
+ if (isStarted) return
foldProvider.registerCallback(foldStateListener, mainExecutor)
screenStatusProvider.addCallback(screenListener)
hingeAngleProvider.addCallback(hingeAngleListener)
rotationChangeProvider.addCallback(rotationListener)
activityTypeProvider.init()
+ isStarted = true
}
override fun stop() {
+ assertMainThread()
screenStatusProvider.removeCallback(screenListener)
foldProvider.unregisterCallback(foldStateListener)
hingeAngleProvider.removeCallback(hingeAngleListener)
hingeAngleProvider.stop()
rotationChangeProvider.removeCallback(rotationListener)
activityTypeProvider.uninit()
+ isStarted = false
}
override fun addCallback(listener: FoldUpdatesListener) {
@@ -292,6 +299,14 @@
onHingeAngle(angle)
}
}
+
+ private fun assertMainThread() {
+ check(mainLooper.isCurrentThread) {
+ ("should be called from the main thread." +
+ " sMainLooper.threadName=" + mainLooper.thread.name +
+ " Thread.currentThread()=" + Thread.currentThread().name)
+ }
+ }
}
fun @receiver:FoldUpdate Int.name() =
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d959de3..3ccede4 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2414,7 +2414,7 @@
// Even if the service is already a FGS, we need to update the notification,
// so we need to call it again.
signalForegroundServiceObserversLocked(r);
- r.postNotification();
+ r.postNotification(true);
if (r.app != null) {
updateServiceForegroundLocked(psr, true);
}
@@ -2472,7 +2472,7 @@
} else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
// if it's been deferred, force to visibility
if (!r.mFgsNotificationShown) {
- r.postNotification();
+ r.postNotification(false);
}
dropFgsNotificationStateLocked(r);
if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
@@ -2916,7 +2916,7 @@
// in the interval, so we lazy check whether we still need to show
// the notification.
if (r.isForeground && r.app != null) {
- r.postNotification();
+ r.postNotification(true);
r.mFgsNotificationShown = true;
} else {
if (DEBUG_FOREGROUND_SERVICE) {
@@ -5338,7 +5338,7 @@
thread.scheduleCreateService(r, r.serviceInfo,
null /* compatInfo (unused but need to keep method signature) */,
app.mState.getReportedProcState());
- r.postNotification();
+ r.postNotification(false);
created = true;
} catch (DeadObjectException e) {
Slog.w(TAG, "Application dead when creating service " + r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index de6522e..ae24f1e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -1027,7 +1027,7 @@
private static final String KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION =
"enable_wait_for_finish_attach_application";
- private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = true;
+ private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = false;
/** @see #KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION */
public volatile boolean mEnableWaitForFinishAttachApplication =
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index e588a9e..be514c5 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -238,7 +238,7 @@
private static final String ATRACE_COMPACTION_TRACK = "Compaction";
private static final String ATRACE_FREEZER_TRACK = "Freezer";
- private static final int FREEZE_BINDER_TIMEOUT_MS = 100;
+ private static final int FREEZE_BINDER_TIMEOUT_MS = 0;
private static final int FREEZE_DEADLOCK_TIMEOUT_MS = 1000;
@VisibleForTesting static final boolean ENABLE_FILE_COMPACT = false;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index aabab61..f7bbc8b 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -1324,7 +1324,7 @@
});
}
- public void postNotification() {
+ public void postNotification(boolean byForegroundService) {
if (isForeground && foregroundNoti != null && app != null) {
final int appUid = appInfo.uid;
final int appPid = app.getPid();
@@ -1432,7 +1432,7 @@
}
nm.enqueueNotification(localPackageName, localPackageName,
appUid, appPid, null, localForegroundId, localForegroundNoti,
- userId);
+ userId, byForegroundService /* byForegroundService */);
foregroundNoti = localForegroundNoti; // save it for amending next time
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index 919fc71..c240bcb 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -27,6 +27,9 @@
NotificationChannelGroup getNotificationChannelGroup(String pkg, int uid, String channelId);
void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId);
+ void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
+ String tag, int id, Notification notification, int userId,
+ boolean byForegroundService);
void cancelNotification(String pkg, String basePkg, int callingUid, int callingPid,
String tag, int id, int userId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c2b21be..15a8c0f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2531,7 +2531,8 @@
}
enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(),
r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(),
- r.getSbn().getId(), r.getSbn().getNotification(), userId, muteOnReturn);
+ r.getSbn().getId(), r.getSbn().getNotification(), userId, muteOnReturn,
+ false /* byForegroundService */);
} catch (Exception e) {
Slog.e(TAG, "Cannot un-snooze notification", e);
}
@@ -3526,7 +3527,8 @@
public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Notification notification, int userId) throws RemoteException {
enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
- Binder.getCallingPid(), tag, id, notification, userId);
+ Binder.getCallingPid(), tag, id, notification, userId,
+ false /* byForegroundService */);
}
@Override
@@ -6095,7 +6097,7 @@
}
if (summaryRecord != null && checkDisqualifyingFeatures(userId, uid,
summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
- true)) {
+ true, false)) {
return summaryRecord;
}
}
@@ -6424,7 +6426,15 @@
public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId) {
enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
- userId);
+ userId, false /* byForegroundService */);
+ }
+
+ @Override
+ public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
+ String tag, int id, Notification notification, int userId,
+ boolean byForegroundService) {
+ enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
+ userId, byForegroundService);
}
@Override
@@ -6602,19 +6612,19 @@
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
- int incomingUserId) {
+ int incomingUserId, boolean byForegroundService) {
enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
- incomingUserId, false);
+ incomingUserId, false /* postSilently */, byForegroundService);
}
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
- int incomingUserId, boolean postSilently) {
+ int incomingUserId, boolean postSilently, boolean byForegroundService) {
PostNotificationTracker tracker = acquireWakeLockForPost(pkg, callingUid);
boolean enqueued = false;
try {
enqueued = enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id,
- notification, incomingUserId, postSilently, tracker);
+ notification, incomingUserId, postSilently, tracker, byForegroundService);
} finally {
if (!enqueued) {
tracker.cancel();
@@ -6645,10 +6655,10 @@
* @return True if we successfully processed the notification and handed off the task of
* enqueueing it to a background thread; false otherwise.
*/
- private boolean enqueueNotificationInternal(final String pkg, final String opPkg,
+ private boolean enqueueNotificationInternal(final String pkg, final String opPkg, //HUI
final int callingUid, final int callingPid, final String tag, final int id,
final Notification notification, int incomingUserId, boolean postSilently,
- PostNotificationTracker tracker) {
+ PostNotificationTracker tracker, boolean byForegroundService) {
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
+ " notification=" + notification);
@@ -6794,7 +6804,7 @@
mPreferencesHelper.hasUserDemotedInvalidMsgApp(pkg, notificationUid));
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
- r.getSbn().getOverrideGroupKey() != null)) {
+ r.getSbn().getOverrideGroupKey() != null, byForegroundService)) {
return false;
}
@@ -7214,7 +7224,7 @@
* Has side effects.
*/
boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
- NotificationRecord r, boolean isAutogroup) {
+ NotificationRecord r, boolean isAutogroup, boolean byForegroundService) {
Notification n = r.getNotification();
final String pkg = r.getSbn().getPackageName();
final boolean isSystemNotification =
@@ -7305,7 +7315,8 @@
if (n.isStyle(Notification.CallStyle.class)) {
boolean hasFullScreenIntent = n.fullScreenIntent != null;
boolean requestedFullScreenIntent = (n.flags & FLAG_FSI_REQUESTED_BUT_DENIED) != 0;
- if (!n.isFgsOrUij() && !hasFullScreenIntent && !requestedFullScreenIntent) {
+ if (!n.isFgsOrUij() && !hasFullScreenIntent && !requestedFullScreenIntent
+ && !byForegroundService) {
throw new IllegalArgumentException(r.getKey() + " Not posted."
+ " CallStyle notifications must be for a foreground service or"
+ " user initated job or use a fullScreenIntent.");
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index bab4886..bed69fc 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1401,7 +1401,7 @@
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.userId == userId) {
- if (info.agent.isTrustableOrWaitingForDowngrade()) {
+ if (info.agent.isManagingTrust()) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index b681c19..ed3fad0 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -303,8 +303,7 @@
mOrientationListener.setCurrentRotation(mRotation);
mSettingsObserver = new SettingsObserver(uiHandler);
mSettingsObserver.observe();
- if (mSupportAutoRotation && mContext.getResources().getBoolean(
- R.bool.config_windowManagerHalfFoldAutoRotateOverride)) {
+ if (mSupportAutoRotation && isFoldable(mContext)) {
mFoldController = new FoldController();
} else {
mFoldController = null;
@@ -314,6 +313,10 @@
}
}
+ private static boolean isFoldable(Context context) {
+ return context.getResources().getIntArray(R.array.config_foldedDeviceStates).length > 0;
+ }
+
@VisibleForTesting
@Nullable
DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy(
@@ -1463,11 +1466,6 @@
return false;
}
- // Do not show rotation choice when fold controller blocks rotation sensor
- if (mFoldController != null && mFoldController.shouldIgnoreSensorRotation()) {
- return false;
- }
-
// Don't show rotation choice if we are in tabletop or book modes.
if (isTabletopAutoRotateOverrideEnabled()) return false;
@@ -1775,8 +1773,11 @@
private SensorEventListener mHingeAngleSensorEventListener;
private final Set<Integer> mTabletopRotations;
private final Runnable mActivityBoundsUpdateCallback;
+ private final boolean mAllowHalfFoldAutoRotationOverride;
FoldController() {
+ mAllowHalfFoldAutoRotationOverride = mContext.getResources().getBoolean(
+ R.bool.config_windowManagerHalfFoldAutoRotateOverride);
mTabletopRotations = new ArraySet<>();
int[] tabletop_rotations = mContext.getResources().getIntArray(
R.array.config_deviceTabletopRotations);
@@ -1894,12 +1895,14 @@
}
boolean overrideFrozenRotation() {
- return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
+ return mAllowHalfFoldAutoRotationOverride
+ && mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
}
boolean shouldRevertOverriddenRotation() {
// When transitioning to open.
- return mDeviceState == DeviceStateController.DeviceState.OPEN
+ return mAllowHalfFoldAutoRotationOverride
+ && mDeviceState == DeviceStateController.DeviceState.OPEN
&& !mShouldIgnoreSensorRotation // Ignore if the hinge angle still moving
&& mInHalfFoldTransition
&& mDisplayContent.getRotationReversionController().isOverrideActive(
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 5f3d517..02f5c21 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -81,8 +81,8 @@
private boolean mIsLeashReadyForDispatching;
private final Rect mSourceFrame = new Rect();
private final Rect mLastSourceFrame = new Rect();
- private final Rect mLastContainerBounds = new Rect();
private @NonNull Insets mInsetsHint = Insets.NONE;
+ private boolean mInsetsHintStale = true;
private @Flags int mFlagsFromFrameProvider;
private @Flags int mFlagsFromServer;
@@ -238,6 +238,10 @@
mSource.setFlags(mFlagsFromFrameProvider | mFlagsFromServer);
}
updateSourceFrameForServerVisibility();
+ if (!mLastSourceFrame.equals(mSourceFrame)) {
+ mLastSourceFrame.set(mSourceFrame);
+ mInsetsHintStale = true;
+ }
if (mOverrideFrameProviders != null) {
// Not necessary to clear the mOverrideFrames here. It will be cleared every time the
@@ -279,28 +283,29 @@
// visible. (i.e. No surface, pending insets that were given during layout, etc..)
if (mServerVisible) {
mSource.setFrame(mSourceFrame);
- updateInsetsHint();
} else {
mSource.setFrame(0, 0, 0, 0);
}
}
- // To be called when mSourceFrame or the window container bounds is changed.
- private void updateInsetsHint() {
- if (!mControllable || !mServerVisible) {
- return;
- }
- final Rect bounds = mWindowContainer.getBounds();
- if (mSourceFrame.equals(mLastSourceFrame) && bounds.equals(mLastContainerBounds)) {
- return;
- }
- mLastSourceFrame.set(mSourceFrame);
- mLastContainerBounds.set(bounds);
- mInsetsHint = mSource.calculateInsets(bounds, true /* ignoreVisibility */);
+ void onWindowContainerBoundsChanged() {
+ mInsetsHintStale = true;
}
@VisibleForTesting
Insets getInsetsHint() {
+ if (!mServerVisible) {
+ return mInsetsHint;
+ }
+ final WindowState win = mWindowContainer.asWindowState();
+ if (win != null && win.mGivenInsetsPending) {
+ return mInsetsHint;
+ }
+ if (mInsetsHintStale) {
+ final Rect bounds = mWindowContainer.getBounds();
+ mInsetsHint = mSource.calculateInsets(bounds, true /* ignoreVisibility */);
+ mInsetsHintStale = false;
+ }
return mInsetsHint;
}
@@ -359,8 +364,9 @@
mSetLeashPositionConsumer.accept(t);
}
}
- if (!mControl.getInsetsHint().equals(mInsetsHint)) {
- mControl.setInsetsHint(mInsetsHint);
+ final Insets insetsHint = getInsetsHint();
+ if (!mControl.getInsetsHint().equals(insetsHint)) {
+ mControl.setInsetsHint(insetsHint);
changed = true;
}
if (changed) {
@@ -494,7 +500,7 @@
mControlTarget = target;
updateVisibility();
mControl = new InsetsSourceControl(mSource.getId(), mSource.getType(), leash,
- mClientVisible, surfacePosition, mInsetsHint);
+ mClientVisible, surfacePosition, getInsetsHint());
ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
"InsetsSource Control %s for target %s", mControl, mControlTarget);
@@ -605,6 +611,9 @@
if (mControllable) {
pw.print(prefix + "mInsetsHint=");
pw.print(mInsetsHint);
+ if (mInsetsHintStale) {
+ pw.print(" stale");
+ }
pw.println();
}
pw.print(prefix);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f33af5e..fd25edf 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -959,20 +959,25 @@
}
// Need to update layers on involved displays since they were all paused while
// the animation played. This puts the layers back into the correct order.
- mController.mBuildingFinishLayers = true;
- try {
- for (int i = displays.size() - 1; i >= 0; --i) {
- if (displays.valueAt(i) == null) continue;
- displays.valueAt(i).assignChildLayers(t);
- }
- } finally {
- mController.mBuildingFinishLayers = false;
+ for (int i = displays.size() - 1; i >= 0; --i) {
+ if (displays.valueAt(i) == null) continue;
+ updateDisplayLayers(displays.valueAt(i), t);
}
+
for (int i = 0; i < info.getRootCount(); ++i) {
t.reparent(info.getRoot(i).getLeash(), null);
}
}
+ private static void updateDisplayLayers(DisplayContent dc, SurfaceControl.Transaction t) {
+ dc.mTransitionController.mBuildingFinishLayers = true;
+ try {
+ dc.assignChildLayers(t);
+ } finally {
+ dc.mTransitionController.mBuildingFinishLayers = false;
+ }
+ }
+
/**
* Build a transaction that cleans-up transition-only surfaces (transition root and snapshots).
* This will ALWAYS be applied on transition finish just in-case
@@ -2346,8 +2351,9 @@
final WindowContainer<?> wc = sortedTargets.get(i).mContainer;
// Don't include wallpapers since they are in a different DA.
if (isWallpaper(wc)) continue;
- final int endDisplayId = getDisplayId(wc);
- if (endDisplayId < 0) continue;
+ final DisplayContent dc = wc.getDisplayContent();
+ if (dc == null) continue;
+ final int endDisplayId = dc.getDisplayId();
// Check if Root was already created for this display with a higher-Z window
if (outInfo.findRootIndex(endDisplayId) >= 0) continue;
@@ -2369,6 +2375,9 @@
final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
"Transition Root: " + leashReference.getName()).build();
rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots");
+ // Update layers to start transaction because we prevent assignment during collect, so
+ // the layer of transition root can be correct.
+ updateDisplayLayers(dc, startT);
startT.setLayer(rootLeash, leashReference.getLastLayer());
outInfo.addRootLeash(endDisplayId, rootLeash,
ancestor.getBounds().left, ancestor.getBounds().top);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 1565341..48cca32 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -558,7 +558,9 @@
return wc.asWindowState() == null;
}
// Always allow WindowState to assign layers since it won't affect transition.
- return wc.asWindowState() != null || !isPlaying();
+ return wc.asWindowState() != null || (!isPlaying()
+ // Don't assign task while collecting.
+ && !(wc.asTask() != null && isCollecting()));
}
@WindowConfiguration.WindowingMode
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 457a555..dae61da 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1143,6 +1143,9 @@
}
void onResize() {
+ if (mControllableInsetProvider != null) {
+ mControllableInsetProvider.onWindowContainerBoundsChanged();
+ }
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
wc.onParentResize();
@@ -1162,6 +1165,9 @@
}
void onMovedByResize() {
+ if (mControllableInsetProvider != null) {
+ mControllableInsetProvider.onWindowContainerBoundsChanged();
+ }
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
wc.onMovedByResize();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 6b225fc..bdee99b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -10244,7 +10244,7 @@
try {
mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
- r.getSbn().getTag(), r,false);
+ r.getSbn().getTag(), r, false, false);
fail("Allowed a contextual direct reply with an immutable intent to be posted");
} catch (IllegalArgumentException e) {
// good
@@ -10275,7 +10275,7 @@
r.applyAdjustments();
mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
- r.getSbn().getTag(), r,false);
+ r.getSbn().getTag(), r, false, false);
}
@Test
@@ -10309,7 +10309,7 @@
r.applyAdjustments();
mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
- r.getSbn().getTag(), r,false);
+ r.getSbn().getTag(), r, false, false);
}
@Test
@@ -10522,7 +10522,7 @@
// normal blocked notifications - blocked
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isFalse();
// just using the style - blocked
nb.setStyle(new Notification.MediaStyle());
@@ -10531,7 +10531,7 @@
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isFalse();
// using the style, but incorrect type in session - blocked
nb.setStyle(new Notification.MediaStyle());
@@ -10543,7 +10543,7 @@
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isFalse();
// style + media session - bypasses block
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
@@ -10552,7 +10552,7 @@
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
}
@Test
@@ -10635,7 +10635,7 @@
// normal blocked notifications - blocked
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isFalse();
// just using the style - blocked
Person person = new Person.Builder()
@@ -10649,36 +10649,36 @@
r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isFalse();
// style + managed call - bypasses block
when(mTelecomManager.isInManagedCall()).thenReturn(true);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
// style + self managed call - bypasses block
when(mTelecomManager.isInSelfManagedCall(
r.getSbn().getPackageName(), r.getUser())).thenReturn(true);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
// set telecom manager to null - blocked
mService.setTelecomManager(null);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false))
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false))
.isFalse();
// set telecom feature to false - blocked
when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(false);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false))
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false))
.isFalse();
// telecom manager is not ready - blocked
mService.setTelecomManager(mTelecomManager);
when(mTelecomManager.isInCall()).thenThrow(new IllegalStateException("not ready"));
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false))
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false))
.isFalse();
}
@@ -11243,7 +11243,7 @@
try {
mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false);
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false);
assertFalse("CallStyle should not be allowed without a valid use case", true);
} catch (IllegalArgumentException error) {
assertThat(error.getMessage()).contains("CallStyle");
@@ -11263,7 +11263,25 @@
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
+ }
+
+ @Test
+ public void checkCallStyleNotification_allowedForByForegroundService() throws Exception {
+ Person person = new Person.Builder().setName("caller").build();
+ Notification n = new Notification.Builder(mContext, "test")
+ // Without FLAG_FOREGROUND_SERVICE.
+ //.setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .setStyle(Notification.CallStyle.forOngoingCall(
+ person, mock(PendingIntent.class)))
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false,
+ true /* byForegroundService */)).isTrue();
}
@Test
@@ -11279,7 +11297,7 @@
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
}
@Test
@@ -11295,7 +11313,7 @@
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
}
@Test
@@ -11311,7 +11329,7 @@
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
- r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 2a8f0ffc..f757330 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -73,6 +73,7 @@
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.UiThread;
@@ -879,6 +880,33 @@
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
}
+ @Test
+ public void sensorRotation_locked_halfFolded_configOff_rotationUnchanged() throws Exception {
+ mBuilder.setIsFoldable(true);
+ mBuilder.setSupportHalfFoldAutoRotateOverride(false);
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+ enableOrientationSensor();
+
+ mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
+ freezeRotation(Surface.ROTATION_270);
+
+ mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
+ assertTrue(waitForUiHandler());
+ // No rotation...
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+
+ // ... half-fold -> still no rotation
+ mTarget.foldStateChanged(DeviceStateController.DeviceState.HALF_FOLDED);
+ assertTrue(waitForUiHandler());
+ verify(sMockWm).updateRotation(false, false);
+ assertTrue(waitForUiHandler());
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+ }
+
// =================================
// Tests for Policy based Rotation
// =================================
@@ -1016,7 +1044,7 @@
@Test
public void testSensorRotationAfterDisplayChangeBeforeTimeout_ignoresSensor() throws Exception {
- mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ mBuilder.setIsFoldable(true)
.setPauseRotationWhenUnfolding(true)
.setDisplaySwitchRotationBlockTimeMs(1000)
.build();
@@ -1034,7 +1062,7 @@
@Test
public void testSensorRotationAfterDisplayChangeAfterTimeout_usesSensor() throws Exception {
- mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ mBuilder.setIsFoldable(true)
.setPauseRotationWhenUnfolding(true)
.setDisplaySwitchRotationBlockTimeMs(1000)
.build();
@@ -1052,7 +1080,7 @@
@Test
public void testSensorRotationAfterHingeEventBeforeTimeout_ignoresSensor() throws Exception {
- mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ mBuilder.setIsFoldable(true)
.setPauseRotationWhenUnfolding(true)
.setMaxHingeAngle(165)
.setHingeAngleRotationBlockTimeMs(400)
@@ -1072,7 +1100,7 @@
@Test
public void testSensorRotationAfterHingeEventBeforeTimeoutFlagDisabled_usesSensorData()
throws Exception {
- mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ mBuilder.setIsFoldable(true)
.setPauseRotationWhenUnfolding(false)
.setMaxHingeAngle(165)
.setHingeAngleRotationBlockTimeMs(400)
@@ -1091,7 +1119,7 @@
@Test
public void testSensorRotationAfterHingeEventAfterTimeout_usesSensorData() throws Exception {
- mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ mBuilder.setIsFoldable(true)
.setPauseRotationWhenUnfolding(true)
.setMaxHingeAngle(165)
.setHingeAngleRotationBlockTimeMs(400)
@@ -1111,7 +1139,7 @@
@Test
public void testSensorRotationAfterLargeHingeEventBeforeTimeout_usesSensor() throws Exception {
- mBuilder.setSupportHalfFoldAutoRotateOverride(true)
+ mBuilder.setIsFoldable(true)
.setPauseRotationWhenUnfolding(true)
.setMaxHingeAngle(165)
.setHingeAngleRotationBlockTimeMs(400)
@@ -1228,6 +1256,7 @@
private int mCarDockRotation;
private int mDeskDockRotation;
private int mUndockedHdmiRotation;
+ private boolean mIsFoldable;
private DisplayRotationBuilder setIsDefaultDisplay(boolean isDefaultDisplay) {
mIsDefaultDisplay = isDefaultDisplay;
@@ -1282,9 +1311,17 @@
return this;
}
+ private DisplayRotationBuilder setIsFoldable(boolean value) {
+ mIsFoldable = value;
+ return this;
+ }
+
private DisplayRotationBuilder setSupportHalfFoldAutoRotateOverride(
boolean supportHalfFoldAutoRotateOverride) {
mSupportHalfFoldAutoRotateOverride = supportHalfFoldAutoRotateOverride;
+ if (supportHalfFoldAutoRotateOverride) {
+ mIsFoldable = true;
+ }
return this;
}
@@ -1429,6 +1466,11 @@
when(mMockContext.getResources().getBoolean(
com.android.internal.R.bool.config_windowManagerHalfFoldAutoRotateOverride))
.thenReturn(mSupportHalfFoldAutoRotateOverride);
+
+ when(mMockContext.getResources().getIntArray(
+ R.array.config_foldedDeviceStates))
+ .thenReturn(mIsFoldable ? new int[]{0} : new int[]{});
+
mMockDisplayRotationReversionController =
mock(DisplayRotationReversionController.class);
when(mMockDisplayContent.getRotationReversionController())