summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp3
-rw-r--r--core/api/system-current.txt4
-rw-r--r--core/java/android/app/Activity.java22
-rw-r--r--core/java/android/app/LocaleConfig.java1
-rw-r--r--core/java/android/content/pm/PackageInstaller.java2
-rw-r--r--core/java/android/content/pm/flags.aconfig7
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java21
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl10
-rw-r--r--core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java10
-rw-r--r--core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java15
-rw-r--r--core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java28
-rw-r--r--core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java19
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java30
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java11
-rw-r--r--graphics/java/android/graphics/YuvImage.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt43
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt37
-rw-r--r--libs/hwui/CanvasTransform.cpp13
-rw-r--r--libs/hwui/CanvasTransform.h3
-rw-r--r--libs/hwui/DisplayList.h2
-rw-r--r--libs/hwui/RecordingCanvas.cpp29
-rw-r--r--libs/hwui/RecordingCanvas.h4
-rw-r--r--libs/hwui/RenderNode.cpp17
-rw-r--r--libs/hwui/jni/YuvToJpegEncoder.cpp35
-rw-r--r--libs/hwui/jni/YuvToJpegEncoder.h5
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.h2
-rw-r--r--libs/hwui/tests/unit/RenderNodeTests.cpp28
-rw-r--r--packages/CrashRecovery/OWNERS3
-rw-r--r--packages/CrashRecovery/framework/Android.bp9
-rw-r--r--packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java (renamed from core/java/android/service/watchdog/ExplicitHealthCheckService.java)0
-rw-r--r--packages/CrashRecovery/framework/java/android/service/watchdog/IExplicitHealthCheckService.aidl (renamed from core/java/android/service/watchdog/IExplicitHealthCheckService.aidl)1
-rw-r--r--packages/CrashRecovery/framework/java/android/service/watchdog/OWNERS (renamed from core/java/android/service/watchdog/OWNERS)0
-rw-r--r--packages/CrashRecovery/framework/java/android/service/watchdog/PackageConfig.aidl (renamed from core/java/android/service/watchdog/PackageConfig.aidl)0
-rw-r--r--packages/CrashRecovery/services/Android.bp9
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/ExplicitHealthCheckController.java (renamed from services/core/java/com/android/server/ExplicitHealthCheckController.java)0
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java (renamed from services/core/java/com/android/server/PackageWatchdog.java)0
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/RescueParty.java (renamed from services/core/java/com/android/server/RescueParty.java)0
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java (renamed from services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java)0
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java (renamed from services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java)0
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java3
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt92
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt83
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt101
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt107
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardMediaControllerLog.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt70
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/model/LocationTileModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt183
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt143
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepositoryKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorKosmos.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/doze/util/BurnInHelperWrapperKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryKosmos.kt (renamed from packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt)14
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorKosmos.kt37
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt46
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlowsKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/location/LocationTileKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepositoryKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt28
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeLocationController.java19
-rw-r--r--services/core/Android.bp3
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/PinnerService.java1
-rw-r--r--services/core/java/com/android/server/TEST_MAPPING24
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java17
-rw-r--r--services/core/java/com/android/server/pm/PackageArchiver.java132
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java31
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java5
-rw-r--r--services/core/java/com/android/server/pm/RemovePackageHelper.java12
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java13
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java42
-rw-r--r--telecomm/java/android/telecom/InCallService.java5
113 files changed, 2321 insertions, 671 deletions
diff --git a/Android.bp b/Android.bp
index 78ffd6fb5e69..c1fb41f845e3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -140,6 +140,9 @@ filegroup {
// For the generated R.java and Manifest.java
":framework-res{.aapt.srcjar}",
+ // Java/AIDL sources to be moved out to CrashRecovery module
+ ":framework-crashrecovery-sources",
+
// etc.
":framework-javastream-protos",
":statslog-framework-java-gen", // FrameworkStatsLog.java
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c282e4b6f3eb..01ca6d924605 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3867,7 +3867,7 @@ package android.content.pm {
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
- method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
+ method @FlaggedApi("android.content.pm.read_install_info") @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void reportUnarchivalStatus(int, int, long, @Nullable android.app.PendingIntent) throws android.content.pm.PackageManager.NameNotFoundException;
method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String, @NonNull android.content.IntentSender) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
@@ -3902,7 +3902,7 @@ package android.content.pm {
public static class PackageInstaller.InstallInfo {
method public long calculateInstalledSize(@NonNull android.content.pm.PackageInstaller.SessionParams) throws java.io.IOException;
- method public long calculateInstalledSize(@NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
+ method @FlaggedApi("android.content.pm.read_install_info") public long calculateInstalledSize(@NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
method public int getInstallLocation();
method @NonNull public String getPackageName();
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6c10f495e7bf..00432dcf152c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -24,9 +24,7 @@ import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.inMultiWindowMode;
import static android.os.Process.myUid;
-
import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
-
import static java.lang.Character.MIN_VALUE;
import android.annotation.AnimRes;
@@ -7604,17 +7602,15 @@ public class Activity extends ContextThemeWrapper
* @param taskDescription The TaskDescription properties that describe the task with this activity
*/
public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
- if (taskDescription == null || mTaskDescription.equals(taskDescription)) {
- return;
- }
-
- mTaskDescription.copyFromPreserveHiddenFields(taskDescription);
- // Scale the icon down to something reasonable if it is provided
- if (taskDescription.getIconFilename() == null && taskDescription.getIcon() != null) {
- final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
- final Bitmap icon = Bitmap.createScaledBitmap(taskDescription.getIcon(), size, size,
- true);
- mTaskDescription.setIcon(Icon.createWithBitmap(icon));
+ if (mTaskDescription != taskDescription) {
+ mTaskDescription.copyFromPreserveHiddenFields(taskDescription);
+ // Scale the icon down to something reasonable if it is provided
+ if (taskDescription.getIconFilename() == null && taskDescription.getIcon() != null) {
+ final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
+ final Bitmap icon = Bitmap.createScaledBitmap(taskDescription.getIcon(), size, size,
+ true);
+ mTaskDescription.setIcon(Icon.createWithBitmap(icon));
+ }
}
ActivityClient.getInstance().setTaskDescription(mToken, mTaskDescription);
}
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index 369a78144fd3..b2be27f70ccc 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -201,6 +201,7 @@ public class LocaleConfig implements Parcelable {
String defaultLocale = null;
if (android.content.res.Flags.defaultLocale()) {
+ // Read the defaultLocale attribute of the LocaleConfig element
TypedArray att = res.obtainAttributes(
attrs, com.android.internal.R.styleable.LocaleConfig);
defaultLocale = att.getString(
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 4f0bfc7437c7..6df1f600c3ef 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2337,6 +2337,7 @@ public class PackageInstaller {
*/
@SystemApi
@NonNull
+ @FlaggedApi(Flags.FLAG_READ_INSTALL_INFO)
public InstallInfo readInstallInfo(@NonNull ParcelFileDescriptor pfd,
@Nullable String debugPathName, int flags) throws PackageParsingException {
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
@@ -2516,6 +2517,7 @@ public class PackageInstaller {
* and all relevant native code.
* @throws IOException when size of native binaries cannot be calculated.
*/
+ @FlaggedApi(Flags.FLAG_READ_INSTALL_INFO)
public long calculateInstalledSize(@NonNull SessionParams params,
@NonNull ParcelFileDescriptor pfd) throws IOException {
return InstallLocationUtils.calculateInstalledSize(mPkg, params.abiOverride,
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index bb5fdb714761..a565f6825e7a 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -87,3 +87,10 @@ flag {
description: "Feature flag to detect the invisible labels in Launcher Apps"
bug: "299586370"
}
+
+flag {
+ name: "read_install_info"
+ namespace: "package_manager_service"
+ description: "Feature flag to read install related information from an APK."
+ bug: "275658500"
+}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 42203d4b0d71..c716cd2e4a9c 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -387,7 +387,6 @@ public class VoiceInteractionService extends Service {
VoiceInteractionService::onShutdownInternal, VoiceInteractionService.this));
};
-
private void onShutdownInternal() {
onShutdown();
// Stop any active recognitions when shutting down.
@@ -1025,6 +1024,26 @@ public class VoiceInteractionService extends Service {
}
}
+ /** Set sandboxed detection training data egress op.
+ *
+ * <p> This method can be called by a preinstalled assistant to allow/disallow training data
+ * egress from trusted process.
+ *
+ * @return whether was able to update sandboxed detection op successfully.
+ * @throws SecurityException if assistant is not a preinstalled assistant.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
+ public boolean setSandboxedDetectionTrainingDataOp(int opMode) {
+ Log.i(TAG, "Setting training data egress op-mode to " + opMode);
+ try {
+ return mSystemService.setSandboxedDetectionTrainingDataOp(opMode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Creates an {@link KeyphraseModelManager} to use for enrolling voice models outside of the
* pre-bundled system voice models.
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 68e2b48c8f08..ea4fc3910d89 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -388,4 +388,14 @@ interface IVoiceInteractionManagerService {
oneway void notifyActivityEventChanged(
in IBinder activityToken,
int type);
+
+ /**
+ * Sets the sandboxed detection training data egress op to provided op-mode.
+ * Caller must be the active assistant and a preinstalled assistant.
+ *
+ * @param opMode app-op mode to set training data egress op to.
+ *
+ * @return whether was able to successfully set training data egress op.
+ */
+ boolean setSandboxedDetectionTrainingDataOp(int opMode);
}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java
index d4a88c49b38f..a3a1d3a5e7a2 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramListTest.java
@@ -34,9 +34,7 @@ import android.content.pm.ApplicationInfo;
import android.os.Build;
import android.os.Parcel;
import android.os.RemoteException;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArraySet;
import com.google.common.truth.Expect;
@@ -150,7 +148,7 @@ public final class ProgramListTest {
@Rule
public final Expect mExpect = Expect.create();
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Test
public void getIdentifierTypes_forFilter() {
@@ -631,8 +629,8 @@ public final class ProgramListTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void getProgramInfos() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
createRadioTuner();
mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
registerListCallbacks(/* numCallbacks= */ 1);
@@ -648,8 +646,8 @@ public final class ProgramListTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void getProgramInfos_withIdNotFound() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
createRadioTuner();
mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
registerListCallbacks(/* numCallbacks= */ 1);
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
index 03de1430fec8..89464d14d1c7 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
@@ -32,9 +32,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.RemoteException;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import org.junit.Rule;
@@ -168,7 +166,7 @@ public final class RadioManagerTest {
private ICloseHandle mCloseHandleMock;
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Test
public void getType_forBandDescriptor() {
@@ -962,22 +960,25 @@ public final class RadioManagerTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void isSignalAcquired_forProgramInfo() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+
assertWithMessage("Signal acquisition status for HD program info")
.that(HD_PROGRAM_INFO.isSignalAcquired()).isTrue();
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void isHdSisAvailable_forProgramInfo() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+
assertWithMessage("SIS information acquisition status for HD program")
.that(HD_PROGRAM_INFO.isHdSisAvailable()).isTrue();
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void isHdAudioAvailable_forProgramInfo() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+
assertWithMessage("Audio acquisition status for HD program")
.that(HD_PROGRAM_INFO.isHdAudioAvailable()).isFalse();
}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
index 3891accbba44..7b9121eb6085 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
@@ -22,10 +22,7 @@ import static org.junit.Assert.assertThrows;
import android.graphics.Bitmap;
import android.os.Parcel;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import org.junit.Rule;
import org.junit.Test;
@@ -52,7 +49,7 @@ public final class RadioMetadataTest {
private Bitmap mBitmapValue;
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Test
public void describeContents_forClock() {
@@ -128,8 +125,8 @@ public final class RadioMetadataTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void putStringArray_withIllegalKey_throwsException() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
String invalidStringArrayKey = RadioMetadata.METADATA_KEY_HD_STATION_NAME_LONG;
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
@@ -142,8 +139,9 @@ public final class RadioMetadataTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void putStringArray_withNullKey_throwsException() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+
NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
mBuilder.putStringArray(/* key= */ null, UFIDS_VALUE);
});
@@ -153,8 +151,9 @@ public final class RadioMetadataTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void putStringArray_withNullString_throwsException() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+
NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
mBuilder.putStringArray(RadioMetadata.METADATA_KEY_UFIDS, /* value= */ null);
});
@@ -281,8 +280,8 @@ public final class RadioMetadataTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void getStringArray_withKeyInMetadata() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
String key = RadioMetadata.METADATA_KEY_UFIDS;
RadioMetadata metadata = mBuilder.putStringArray(key, UFIDS_VALUE).build();
@@ -291,8 +290,8 @@ public final class RadioMetadataTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void getStringArray_withKeyNotInMetadata() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
String key = RadioMetadata.METADATA_KEY_UFIDS;
RadioMetadata metadata = mBuilder.build();
@@ -305,8 +304,8 @@ public final class RadioMetadataTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void getStringArray_withNullKey() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
RadioMetadata metadata = mBuilder.build();
NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
@@ -318,8 +317,8 @@ public final class RadioMetadataTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void getStringArray_withInvalidKey() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
String invalidClockKey = RadioMetadata.METADATA_KEY_HD_STATION_NAME_LONG;
RadioMetadata metadata = mBuilder.build();
@@ -413,7 +412,6 @@ public final class RadioMetadataTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void writeToParcel_forRadioMetadata() {
RadioMetadata metadataExpected = mBuilder
.putInt(RadioMetadata.METADATA_KEY_RDS_PI, INT_KEY_VALUE)
@@ -430,8 +428,8 @@ public final class RadioMetadataTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void writeToParcel_forRadioMetadata_withStringArrayTypeMetadata() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
RadioMetadata metadataExpected = mBuilder
.putInt(RadioMetadata.METADATA_KEY_RDS_PI, INT_KEY_VALUE)
.putString(RadioMetadata.METADATA_KEY_ARTIST, ARTIST_KEY_VALUE)
@@ -443,7 +441,7 @@ public final class RadioMetadataTest {
parcel.setDataPosition(0);
RadioMetadata metadataFromParcel = RadioMetadata.CREATOR.createFromParcel(parcel);
- assertWithMessage("Radio metadata created from parcel")
+ assertWithMessage("Radio metadata created from parcel with string array type metadata")
.that(metadataFromParcel).isEqualTo(metadataExpected);
}
}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
index 7ca806b49b68..4841711f712d 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
@@ -36,10 +36,7 @@ import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.RemoteException;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import org.junit.After;
import org.junit.Before;
@@ -84,7 +81,7 @@ public final class TunerAdapterTest {
private RadioTuner.Callback mCallbackMock;
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() throws Exception {
@@ -613,9 +610,9 @@ public final class TunerAdapterTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void isConfigFlagSet_withForceAnalogWhenFmForceAnalogSupported()
throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
when(mTunerMock.isConfigFlagSupported(anyInt())).thenReturn(true);
when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG_FM))
.thenReturn(true);
@@ -626,9 +623,9 @@ public final class TunerAdapterTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void isConfigFlagSet_withForceAnalogWhenFmForceAnalogNotSupported()
throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
when(mTunerMock.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG_FM))
.thenReturn(false);
when(mTunerMock.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG)).thenReturn(true);
@@ -640,9 +637,9 @@ public final class TunerAdapterTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void isConfigFlagSet_withForceAnalogWhenHdRadioImprovedFeatureNotEnabled()
throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
when(mTunerMock.isConfigFlagSupported(anyInt())).thenReturn(true);
when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG)).thenReturn(false);
@@ -683,8 +680,8 @@ public final class TunerAdapterTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void setConfigFlag_withForceAnalogWhenFmForceAnalogSupported() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
when(mTunerMock.isConfigFlagSupported(anyInt())).thenReturn(true);
mRadioTuner.setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, /* value= */ false);
@@ -695,8 +692,8 @@ public final class TunerAdapterTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void setConfigFlag_withForceAnalogWhenFmForceAnalogNotSupported() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
when(mTunerMock.isConfigFlagSupported(anyInt())).thenReturn(true);
when(mTunerMock.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG_FM))
.thenReturn(false);
@@ -709,9 +706,9 @@ public final class TunerAdapterTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void setConfigFlag_withForceAnalogWhenHdRadioImprovedFeatureNotEnabled()
throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
when(mTunerMock.isConfigFlagSupported(anyInt())).thenReturn(true);
mRadioTuner.setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, /* value= */ false);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index 49a7ba855908..15bb66b3e303 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -40,10 +40,7 @@ import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
import android.hardware.radio.UniqueProgramIdentifier;
import android.os.ServiceSpecificException;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
@@ -159,7 +156,7 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
@Rule
public final Expect expect = Expect.create();
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Override
protected void initializeSession(StaticMockitoSessionBuilder builder) {
@@ -317,9 +314,10 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void identifierToHalProgramIdentifier_withFlagEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
ProgramSelector.Identifier hdLocationId = createHdStationLocationIdWithFlagEnabled();
+
ProgramIdentifier halHdLocationId =
ConversionUtils.identifierToHalProgramIdentifier(hdLocationId);
@@ -336,10 +334,11 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void identifierFromHalProgramIdentifier_withFlagEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
ProgramSelector.Identifier hdLocationIdExpected =
createHdStationLocationIdWithFlagEnabled();
+
ProgramSelector.Identifier hdLocationId =
ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_HD_STATION_LOCATION_ID);
@@ -348,8 +347,9 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void identifierFromHalProgramIdentifier_withFlagDisabled_returnsNull() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+
ProgramSelector.Identifier hdLocationId =
ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_HD_STATION_LOCATION_ID);
@@ -432,8 +432,8 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void programSelectorMeetsSdkVersionRequirement_withLowerVersionSecondaryId() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
ProgramSelector hdSelector = createHdSelectorWithFlagEnabled();
expect.withMessage("Selector %s with secondary id requiring higher-version SDK version",
@@ -449,8 +449,8 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void programSelectorMeetsSdkVersionRequirement_withRequiredVersionAndFlagEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
ProgramSelector hdSelector = createHdSelectorWithFlagEnabled();
expect.withMessage("Selector %s with required SDK version and feature flag enabled",
@@ -548,8 +548,8 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void configFlagMeetsSdkVersionRequirement_withRequiredSdkVersionAndFlagEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
int halForceAmAnalogFlag = ConfigFlag.FORCE_ANALOG_FM;
expect.withMessage("Force Analog FM flag with required SDK version and feature flag"
@@ -558,8 +558,8 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void configFlagMeetsSdkVersionRequirement_withRequiredSdkVersionAndFlagDisabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
int halForceAmAnalogFlag = ConfigFlag.FORCE_ANALOG_FM;
expect.withMessage("Force Analog FM with required SDK version and with feature flag"
@@ -586,8 +586,9 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void radioMetadataFromHalMetadata_withFlagEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+
RadioMetadata convertedMetadata = ConversionUtils.radioMetadataFromHalMetadata(
new Metadata[]{TEST_HAL_SONG_TITLE, TEST_HAL_HD_SUBCHANNELS, TEST_HAL_ALBUM_ART});
@@ -605,8 +606,9 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void radioMetadataFromHalMetadata_withFlagDisabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+
RadioMetadata convertedMetadata = ConversionUtils.radioMetadataFromHalMetadata(
new Metadata[]{TEST_HAL_SONG_TITLE, TEST_HAL_HD_SUBCHANNELS, TEST_HAL_ALBUM_ART});
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 7bef5abc3d63..296c45136292 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -53,10 +53,7 @@ import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -164,7 +161,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
@Rule
public final Expect expect = Expect.create();
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Override
protected void initializeSession(StaticMockitoSessionBuilder builder) {
@@ -1231,8 +1228,8 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void onConfigFlagUpdated_withRequiredFlagEnabled_invokesCallbacks() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
openAidlClients(/* numClients= */ 1);
mHalTunerCallback.onConfigFlagUpdated(ConfigFlag.FORCE_ANALOG_FM, true);
@@ -1242,9 +1239,9 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_HD_RADIO_IMPROVED)
public void onConfigFlagUpdated_withRequiredFlagDisabled_doesNotInvokeCallbacks()
throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
openAidlClients(/* numClients= */ 1);
mHalTunerCallback.onConfigFlagUpdated(ConfigFlag.FORCE_ANALOG_FM, true);
diff --git a/graphics/java/android/graphics/YuvImage.java b/graphics/java/android/graphics/YuvImage.java
index b3bed00ac6f6..ce35b55d526f 100644
--- a/graphics/java/android/graphics/YuvImage.java
+++ b/graphics/java/android/graphics/YuvImage.java
@@ -339,7 +339,8 @@ public class YuvImage {
return nativeCompressToJpegR(mData, mColorSpace.getDataSpace(),
sdr.getYuvData(), sdr.getColorSpace().getDataSpace(),
mWidth, mHeight, quality, stream,
- new byte[WORKING_COMPRESS_STORAGE], exif);
+ new byte[WORKING_COMPRESS_STORAGE], exif,
+ mStrides, sdr.getStrides());
}
@@ -451,5 +452,6 @@ public class YuvImage {
private static native boolean nativeCompressToJpegR(byte[] hdr, int hdrColorSpaceId,
byte[] sdr, int sdrColorSpaceId, int width, int height, int quality,
- OutputStream stream, byte[] tempStorage, byte[] exif);
+ OutputStream stream, byte[] tempStorage, byte[] exif,
+ int[] hdrStrides, int[] sdrStrides);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 75d27d99b9ac..95d7ad5c416f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -25,12 +25,14 @@ import android.window.TransitionRequestInfo
import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
import com.android.wm.shell.transition.Transitions.TransitionHandler
+import com.android.wm.shell.util.KtProtoLog
import com.android.wm.shell.util.TransitionUtil
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
@@ -68,6 +70,10 @@ class DragToDesktopTransitionHandler(
private var splitScreenController: SplitScreenController? = null
private var transitionState: TransitionState? = null
+ /** Whether a drag-to-desktop transition is in progress. */
+ val inProgress: Boolean
+ get() = transitionState != null
+
/** Sets a listener to receive callback about events during the transition animation. */
fun setDragToDesktopStateListener(listener: DragToDesktopStateListener) {
dragToDesktopStateListener = listener
@@ -92,19 +98,22 @@ class DragToDesktopTransitionHandler(
dragToDesktopAnimator: MoveToDesktopAnimator,
windowDecoration: DesktopModeWindowDecoration
) {
- if (transitionState != null) {
+ if (inProgress) {
error("A drag to desktop is already in progress")
}
val options = ActivityOptions.makeBasic().apply {
setTransientLaunch()
setSourceInfo(SourceInfo.TYPE_DESKTOP_ANIMATION, SystemClock.uptimeMillis())
+ pendingIntentCreatorBackgroundActivityStartMode =
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
}
val pendingIntent = PendingIntent.getActivity(
context,
0 /* requestCode */,
launchHomeIntent,
- FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT
+ FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT,
+ options.toBundle()
)
val wct = WindowContainerTransaction()
wct.sendPendingIntent(pendingIntent, launchHomeIntent, options.toBundle())
@@ -135,6 +144,12 @@ class DragToDesktopTransitionHandler(
* inside the desktop drop zone.
*/
fun finishDragToDesktopTransition(wct: WindowContainerTransaction) {
+ if (requireTransitionState().startAborted) {
+ // Don't attempt to complete the drag-to-desktop since the start transition didn't
+ // succeed as expected. Just reset the state as if nothing happened.
+ clearState()
+ return
+ }
transitions.startTransition(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, wct, this)
}
@@ -147,6 +162,12 @@ class DragToDesktopTransitionHandler(
*/
fun cancelDragToDesktopTransition() {
val state = requireTransitionState()
+ if (state.startAborted) {
+ // Don't attempt to cancel the drag-to-desktop since the start transition didn't
+ // succeed as expected. Just reset the state as if nothing happened.
+ clearState()
+ return
+ }
state.cancelled = true
if (state.draggedTaskChange != null) {
// Regular case, transient launch of Home happened as is waiting for the cancel
@@ -409,6 +430,21 @@ class DragToDesktopTransitionHandler(
return null
}
+ override fun onTransitionConsumed(
+ transition: IBinder,
+ aborted: Boolean,
+ finishTransaction: SurfaceControl.Transaction?
+ ) {
+ val state = transitionState ?: return
+ if (aborted && state.startTransitionToken == transition) {
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DragToDesktop: onTransitionConsumed() start transition aborted"
+ )
+ state.startAborted = true
+ }
+ }
+
private fun isHomeChange(change: Change): Boolean {
return change.taskInfo?.activityType == ACTIVITY_TYPE_HOME
}
@@ -508,6 +544,7 @@ class DragToDesktopTransitionHandler(
abstract var homeToken: WindowContainerToken?
abstract var draggedTaskChange: Change?
abstract var cancelled: Boolean
+ abstract var startAborted: Boolean
data class FromFullscreen(
override val draggedTaskId: Int,
@@ -520,6 +557,7 @@ class DragToDesktopTransitionHandler(
override var homeToken: WindowContainerToken? = null,
override var draggedTaskChange: Change? = null,
override var cancelled: Boolean = false,
+ override var startAborted: Boolean = false,
) : TransitionState()
data class FromSplit(
override val draggedTaskId: Int,
@@ -532,6 +570,7 @@ class DragToDesktopTransitionHandler(
override var homeToken: WindowContainerToken? = null,
override var draggedTaskChange: Change? = null,
override var cancelled: Boolean = false,
+ override var startAborted: Boolean = false,
var splitRootChange: Change? = null,
) : TransitionState()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index e1d177af2331..6ec91e0e28dd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -548,8 +548,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
private PointF offsetCaptionLocation(MotionEvent ev) {
final PointF result = new PointF(ev.getX(), ev.getY());
- final Point positionInParent = mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId)
- .positionInParent;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId);
+ if (taskInfo == null) return result;
+ final Point positionInParent = taskInfo.positionInParent;
result.offset(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY);
result.offset(-positionInParent.x, -positionInParent.y);
return result;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index b355ab0bf311..3bc90ade898e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -13,6 +13,7 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
import android.window.TransitionInfo
import android.window.TransitionInfo.FLAG_IS_WALLPAPER
+import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
@@ -20,9 +21,11 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
import java.util.function.Supplier
+import junit.framework.Assert.assertFalse
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -113,6 +116,40 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
}
@Test
+ fun startDragToDesktop_aborted_finishDropped() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+ // Simulate transition is started.
+ val transition = startDragToDesktopTransition(task, dragAnimator)
+ // But the transition was aborted.
+ handler.onTransitionConsumed(transition, aborted = true, mock())
+
+ // Attempt to finish the failed drag start.
+ handler.finishDragToDesktopTransition(WindowContainerTransaction())
+
+ // Should not be attempted and state should be reset.
+ verify(transitions, never())
+ .startTransition(eq(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP), any(), any())
+ assertFalse(handler.inProgress)
+ }
+
+ @Test
+ fun startDragToDesktop_aborted_cancelDropped() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+ // Simulate transition is started.
+ val transition = startDragToDesktopTransition(task, dragAnimator)
+ // But the transition was aborted.
+ handler.onTransitionConsumed(transition, aborted = true, mock())
+
+ // Attempt to finish the failed drag start.
+ handler.cancelDragToDesktopTransition()
+
+ // Should not be attempted and state should be reset.
+ assertFalse(handler.inProgress)
+ }
+
+ @Test
fun cancelDragToDesktop_startWasReady_cancel() {
val task = createTask()
val dragAnimator = mock<MoveToDesktopAnimator>()
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index cd4fae86aa52..b667daf9c850 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -80,6 +80,19 @@ SkColor transformColorInverse(ColorTransform transform, SkColor color) {
static void applyColorTransform(ColorTransform transform, SkPaint& paint) {
if (transform == ColorTransform::None) return;
+ if (transform == ColorTransform::Invert) {
+ auto filter = SkHighContrastFilter::Make(
+ {/* grayscale= */ false, SkHighContrastConfig::InvertStyle::kInvertLightness,
+ /* contrast= */ 0.0f});
+
+ if (paint.getColorFilter()) {
+ paint.setColorFilter(SkColorFilters::Compose(filter, paint.refColorFilter()));
+ } else {
+ paint.setColorFilter(filter);
+ }
+ return;
+ }
+
SkColor newColor = transformColor(transform, paint.getColor());
paint.setColor(newColor);
diff --git a/libs/hwui/CanvasTransform.h b/libs/hwui/CanvasTransform.h
index 291f4cf7193b..288dca4de5c1 100644
--- a/libs/hwui/CanvasTransform.h
+++ b/libs/hwui/CanvasTransform.h
@@ -29,12 +29,15 @@ enum class UsageHint {
Unknown = 0,
Background = 1,
Foreground = 2,
+ // Contains foreground (usually text), like a button or chip
+ Container = 3
};
enum class ColorTransform {
None,
Light,
Dark,
+ Invert
};
// True if the paint was modified, false otherwise
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 8c180da9c84f..b1c5bf49ede5 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -145,6 +145,8 @@ public:
return mImpl && mImpl->hasText();
}
+ [[nodiscard]] bool hasFill() const { return mImpl && mImpl->hasFill(); }
+
void applyColorTransform(ColorTransform transform) {
if (mImpl) {
mImpl->applyColorTransform(transform);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index ff0d8d74831c..3b694c5d399b 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -718,6 +718,27 @@ static constexpr inline bool is_power_of_two(int value) {
return (value & (value - 1)) == 0;
}
+template <typename T>
+constexpr bool doesPaintHaveFill(T& paint) {
+ using T1 = std::remove_cv_t<T>;
+ if constexpr (std::is_same_v<T1, SkPaint>) {
+ return paint.getStyle() != SkPaint::Style::kStroke_Style;
+ } else if constexpr (std::is_same_v<T1, SkPaint&>) {
+ return paint.getStyle() != SkPaint::Style::kStroke_Style;
+ } else if constexpr (std::is_same_v<T1, SkPaint*>) {
+ return paint && paint->getStyle() != SkPaint::Style::kStroke_Style;
+ } else if constexpr (std::is_same_v<T1, const SkPaint*>) {
+ return paint && paint->getStyle() != SkPaint::Style::kStroke_Style;
+ }
+
+ return false;
+}
+
+template <typename... Args>
+constexpr bool hasPaintWithFill(Args&&... args) {
+ return (... || doesPaintHaveFill(args));
+}
+
template <typename T, typename... Args>
void* DisplayListData::push(size_t pod, Args&&... args) {
size_t skip = SkAlignPtr(sizeof(T) + pod);
@@ -736,6 +757,14 @@ void* DisplayListData::push(size_t pod, Args&&... args) {
new (op) T{std::forward<Args>(args)...};
op->type = (uint32_t)T::kType;
op->skip = skip;
+
+ // check if this is a fill op or not, in case we need to avoid messing with it with force invert
+ if constexpr (!std::is_same_v<T, DrawTextBlob>) {
+ if (hasPaintWithFill(args...)) {
+ mHasFill = true;
+ }
+ }
+
return op + 1;
}
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 4f54ee286a56..afadbfda7471 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -110,7 +110,7 @@ class RecordingCanvas;
class DisplayListData final {
public:
- DisplayListData() : mHasText(false) {}
+ DisplayListData() : mHasText(false), mHasFill(false) {}
~DisplayListData();
void draw(SkCanvas* canvas) const;
@@ -121,6 +121,7 @@ public:
void applyColorTransform(ColorTransform transform);
bool hasText() const { return mHasText; }
+ bool hasFill() const { return mHasFill; }
size_t usedSize() const { return fUsed; }
size_t allocatedSize() const { return fReserved; }
@@ -192,6 +193,7 @@ private:
size_t fReserved = 0;
bool mHasText : 1;
+ bool mHasFill : 1;
};
class RecordingCanvas final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 3e131bc44d39..0b42c88aa448 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -427,7 +427,13 @@ void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
children.push_back(node);
});
if (mDisplayList.hasText()) {
- usage = UsageHint::Foreground;
+ if (mDisplayList.hasFill()) {
+ // Handle a special case for custom views that draw both text and background in the
+ // same RenderNode, which would otherwise be altered to white-on-white text.
+ usage = UsageHint::Container;
+ } else {
+ usage = UsageHint::Foreground;
+ }
}
if (usage == UsageHint::Unknown) {
if (children.size() > 1) {
@@ -453,8 +459,13 @@ void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
drawn.join(bounds);
}
}
- mDisplayList.applyColorTransform(
- usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light);
+
+ if (usage == UsageHint::Container) {
+ mDisplayList.applyColorTransform(ColorTransform::Invert);
+ } else {
+ mDisplayList.applyColorTransform(usage == UsageHint::Background ? ColorTransform::Dark
+ : ColorTransform::Light);
+ }
}
void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) {
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 8c5cc30ba076..c55066af3612 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -332,7 +332,8 @@ ultrahdr_transfer_function P010Yuv420ToJpegREncoder::findHdrTransferFunction(JNI
bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env,
SkWStream* stream, void* hdr, int hdrColorSpace, void* sdr, int sdrColorSpace,
- int width, int height, int jpegQuality, ScopedByteArrayRO* jExif) {
+ int width, int height, int jpegQuality, ScopedByteArrayRO* jExif,
+ ScopedIntArrayRO* jHdrStrides, ScopedIntArrayRO* jSdrStrides) {
// Check SDR color space. Now we only support SRGB transfer function
if ((sdrColorSpace & ADataSpace::TRANSFER_MASK) != ADataSpace::TRANSFER_SRGB) {
jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException");
@@ -340,6 +341,19 @@ bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env,
"The requested SDR color space is not supported. Transfer function must be SRGB");
return false;
}
+ // Check HDR and SDR strides length.
+ // HDR is YCBCR_P010 color format, and its strides length must be 2 (Y, chroma (Cb, Cr)).
+ // SDR is YUV_420_888 color format, and its strides length must be 3 (Y, Cb, Cr).
+ if (jHdrStrides->size() != 2) {
+ jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException");
+ env->ThrowNew(IllegalArgumentException, "HDR stride length must be 2.");
+ return false;
+ }
+ if (jSdrStrides->size() != 3) {
+ jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException");
+ env->ThrowNew(IllegalArgumentException, "SDR stride length must be 3.");
+ return false;
+ }
ultrahdr_color_gamut hdrColorGamut = findColorGamut(env, hdrColorSpace);
ultrahdr_color_gamut sdrColorGamut = findColorGamut(env, sdrColorSpace);
@@ -351,18 +365,26 @@ bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env,
return false;
}
+ const int* hdrStrides = reinterpret_cast<const int*>(jHdrStrides->get());
+ const int* sdrStrides = reinterpret_cast<const int*>(jSdrStrides->get());
+
JpegR jpegREncoder;
jpegr_uncompressed_struct p010;
p010.data = hdr;
p010.width = width;
p010.height = height;
+ // Divided by 2 because unit in libultrader is pixel and in YuvImage it is byte.
+ p010.luma_stride = (hdrStrides[0] + 1) / 2;
+ p010.chroma_stride = (hdrStrides[1] + 1) / 2;
p010.colorGamut = hdrColorGamut;
jpegr_uncompressed_struct yuv420;
yuv420.data = sdr;
yuv420.width = width;
yuv420.height = height;
+ yuv420.luma_stride = sdrStrides[0];
+ yuv420.chroma_stride = sdrStrides[1];
yuv420.colorGamut = sdrColorGamut;
jpegr_exif_struct exif;
@@ -420,22 +442,27 @@ static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv,
static jboolean YuvImage_compressToJpegR(JNIEnv* env, jobject, jbyteArray inHdr,
jint hdrColorSpace, jbyteArray inSdr, jint sdrColorSpace,
jint width, jint height, jint quality, jobject jstream,
- jbyteArray jstorage, jbyteArray jExif) {
+ jbyteArray jstorage, jbyteArray jExif,
+ jintArray jHdrStrides, jintArray jSdrStrides) {
jbyte* hdr = env->GetByteArrayElements(inHdr, NULL);
jbyte* sdr = env->GetByteArrayElements(inSdr, NULL);
ScopedByteArrayRO exif(env, jExif);
+ ScopedIntArrayRO hdrStrides(env, jHdrStrides);
+ ScopedIntArrayRO sdrStrides(env, jSdrStrides);
SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
P010Yuv420ToJpegREncoder encoder;
jboolean result = JNI_FALSE;
if (encoder.encode(env, strm, hdr, hdrColorSpace, sdr, sdrColorSpace,
- width, height, quality, &exif)) {
+ width, height, quality, &exif,
+ &hdrStrides, &sdrStrides)) {
result = JNI_TRUE;
}
env->ReleaseByteArrayElements(inHdr, hdr, 0);
env->ReleaseByteArrayElements(inSdr, sdr, 0);
+
delete strm;
return result;
}
@@ -444,7 +471,7 @@ static jboolean YuvImage_compressToJpegR(JNIEnv* env, jobject, jbyteArray inHdr,
static const JNINativeMethod gYuvImageMethods[] = {
{ "nativeCompressToJpeg", "([BIII[I[IILjava/io/OutputStream;[B)Z",
(void*)YuvImage_compressToJpeg },
- { "nativeCompressToJpegR", "([BI[BIIIILjava/io/OutputStream;[B[B)Z",
+ { "nativeCompressToJpegR", "([BI[BIIIILjava/io/OutputStream;[B[B[I[I)Z",
(void*)YuvImage_compressToJpegR }
};
diff --git a/libs/hwui/jni/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h
index f3f2c658d1cd..a3a322453be7 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.h
+++ b/libs/hwui/jni/YuvToJpegEncoder.h
@@ -92,11 +92,14 @@ public:
* @param height Height of the Yuv data in terms of pixels.
* @param jpegQuality Picture quality in [0, 100].
* @param exif Buffer holds EXIF package.
+ * @param hdrStrides The number of row bytes in each image plane of the HDR input.
+ * @param sdrStrides The number of row bytes in each image plane of the SDR input.
* @return true if successfully compressed the stream.
*/
bool encode(JNIEnv* env,
SkWStream* stream, void* hdr, int hdrColorSpace, void* sdr, int sdrColorSpace,
- int width, int height, int jpegQuality, ScopedByteArrayRO* exif);
+ int width, int height, int jpegQuality, ScopedByteArrayRO* exif,
+ ScopedIntArrayRO* hdrStrides, ScopedIntArrayRO* sdrStrides);
/** Map data space (defined in DataSpace.java and data_space.h) to the color gamut
* used in JPEG/R
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index e5bd5c9b2a3b..b9dc1c49f09e 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -96,6 +96,8 @@ public:
bool hasText() const { return mDisplayList.hasText(); }
+ bool hasFill() const { return mDisplayList.hasFill(); }
+
/**
* Attempts to reset and reuse this DisplayList.
*
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 8273524167f9..e727ea899098 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -331,3 +331,31 @@ RENDERTHREAD_TEST(DISABLED_RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage);
canvasContext->destroy();
}
+
+TEST(RenderNode, hasNoFill) {
+ auto rootNode =
+ TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) {
+ Paint paint;
+ paint.setStyle(SkPaint::Style::kStroke_Style);
+ canvas.drawRect(10, 10, 100, 100, paint);
+ });
+
+ TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode);
+
+ EXPECT_FALSE(rootNode.get()->getDisplayList().hasFill());
+ EXPECT_FALSE(rootNode.get()->getDisplayList().hasText());
+}
+
+TEST(RenderNode, hasFill) {
+ auto rootNode =
+ TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) {
+ Paint paint;
+ paint.setStyle(SkPaint::kStrokeAndFill_Style);
+ canvas.drawRect(10, 10, 100, 100, paint);
+ });
+
+ TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode);
+
+ EXPECT_TRUE(rootNode.get()->getDisplayList().hasFill());
+ EXPECT_FALSE(rootNode.get()->getDisplayList().hasText());
+}
diff --git a/packages/CrashRecovery/OWNERS b/packages/CrashRecovery/OWNERS
new file mode 100644
index 000000000000..daa02111f71f
--- /dev/null
+++ b/packages/CrashRecovery/OWNERS
@@ -0,0 +1,3 @@
+ancr@google.com
+harshitmahajan@google.com
+robertogil@google.com
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
new file mode 100644
index 000000000000..b2af315ef2c9
--- /dev/null
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -0,0 +1,9 @@
+filegroup {
+ name: "framework-crashrecovery-sources",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ path: "java",
+ visibility: ["//frameworks/base:__subpackages__"],
+}
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
index 7befbfb0f370..7befbfb0f370 100644
--- a/core/java/android/service/watchdog/ExplicitHealthCheckService.java
+++ b/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
diff --git a/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl b/packages/CrashRecovery/framework/java/android/service/watchdog/IExplicitHealthCheckService.aidl
index 78c0328d36f0..90965092ac2b 100644
--- a/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
+++ b/packages/CrashRecovery/framework/java/android/service/watchdog/IExplicitHealthCheckService.aidl
@@ -21,6 +21,7 @@ import android.os.RemoteCallback;
/**
* @hide
*/
+@PermissionManuallyEnforced
oneway interface IExplicitHealthCheckService
{
void setCallback(in @nullable RemoteCallback callback);
diff --git a/core/java/android/service/watchdog/OWNERS b/packages/CrashRecovery/framework/java/android/service/watchdog/OWNERS
index 1c045e10c0ec..1c045e10c0ec 100644
--- a/core/java/android/service/watchdog/OWNERS
+++ b/packages/CrashRecovery/framework/java/android/service/watchdog/OWNERS
diff --git a/core/java/android/service/watchdog/PackageConfig.aidl b/packages/CrashRecovery/framework/java/android/service/watchdog/PackageConfig.aidl
index 013158676f79..013158676f79 100644
--- a/core/java/android/service/watchdog/PackageConfig.aidl
+++ b/packages/CrashRecovery/framework/java/android/service/watchdog/PackageConfig.aidl
diff --git a/packages/CrashRecovery/services/Android.bp b/packages/CrashRecovery/services/Android.bp
new file mode 100644
index 000000000000..27ddff93247e
--- /dev/null
+++ b/packages/CrashRecovery/services/Android.bp
@@ -0,0 +1,9 @@
+filegroup {
+ name: "services-crashrecovery-sources",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ path: "java",
+ visibility: ["//frameworks/base:__subpackages__"],
+}
diff --git a/services/core/java/com/android/server/ExplicitHealthCheckController.java b/packages/CrashRecovery/services/java/com/android/server/ExplicitHealthCheckController.java
index 3d610d3747c9..3d610d3747c9 100644
--- a/services/core/java/com/android/server/ExplicitHealthCheckController.java
+++ b/packages/CrashRecovery/services/java/com/android/server/ExplicitHealthCheckController.java
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
index d256aead97e8..d256aead97e8 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
diff --git a/services/core/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
index dd543349fc4d..dd543349fc4d 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 2007079ea5ca..2007079ea5ca 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
index f9ef994a523a..f9ef994a523a 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 9c67817cbef4..04c69daffc5a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -176,7 +176,8 @@ public class UninstallerActivity extends Activity {
try {
mDialogInfo.appInfo = pm.getApplicationInfo(mPackageName,
- PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER));
+ PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER
+ | PackageManager.MATCH_ARCHIVED_PACKAGES));
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Unable to get packageName. Package manager is dead?");
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index 4a93bf80ae39..d113878a3181 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -189,7 +189,8 @@ public class UninstallAlertDialogFragment extends DialogFragment implements
boolean suggestToKeepAppData;
try {
- PackageInfo pkgInfo = pm.getPackageInfo(pkg, 0);
+ PackageInfo pkgInfo = pm.getPackageInfo(pkg,
+ PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ARCHIVED_PACKAGES));
suggestToKeepAppData = pkgInfo.applicationInfo.hasFragileUserData();
} catch (PackageManager.NameNotFoundException e) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index cc95a4b72731..30536547ae5c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -144,8 +144,7 @@ private fun SceneScope.LockscreenScene(
modifier = Modifier.fillMaxSize(),
)
- val notificationStackPosition by
- viewModel.keyguardRoot.notificationPositionOnLockscreen.collectAsState()
+ val notificationStackPosition by viewModel.keyguardRoot.notificationBounds.collectAsState()
Layout(
modifier = Modifier.fillMaxSize(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index c9d31fdcb8e5..c49c19785624 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -72,8 +72,8 @@ object Notifications {
@Composable
fun SceneScope.HeadsUpNotificationSpace(
viewModel: NotificationsPlaceholderViewModel,
- isPeekFromBottom: Boolean = false,
modifier: Modifier = Modifier,
+ isPeekFromBottom: Boolean = false,
) {
NotificationPlaceholder(
viewModel = viewModel,
@@ -149,11 +149,11 @@ private fun SceneScope.NotificationPlaceholder(
form: Form,
modifier: Modifier = Modifier,
) {
- val key = Notifications.Elements.NotificationPlaceholder
+ val elementKey = Notifications.Elements.NotificationPlaceholder
Box(
modifier =
modifier
- .element(key)
+ .element(elementKey)
.debugBackground(viewModel)
.onSizeChanged { size: IntSize ->
debugLog(viewModel) { "STACK onSizeChanged: size=$size" }
@@ -166,7 +166,7 @@ private fun SceneScope.NotificationPlaceholder(
" bounds=${coordinates.boundsInWindow()}"
}
val boundsInWindow = coordinates.boundsInWindow()
- viewModel.setPlaceholderPositionInWindow(
+ viewModel.onBoundsChanged(
top = boundsInWindow.top,
bottom = boundsInWindow.bottom,
)
@@ -176,7 +176,7 @@ private fun SceneScope.NotificationPlaceholder(
animateSharedFloatAsState(
value = if (form == Form.HunFromTop) 0f else 1f,
key = SharedExpansionValue,
- element = key
+ element = elementKey
)
debugLog(viewModel) { "STACK composed: expansion=$animatedExpansion" }
if (viewModel.isPlaceholderTextVisible) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
new file mode 100644
index 000000000000..820c0564c1a7
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.qs.tiles.impl.location.domain
+
+import android.graphics.drawable.Drawable
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
+import com.android.systemui.qs.tiles.impl.location.qsLocationTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth
+import junit.framework.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LocationTileMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val qsTileConfig = kosmos.qsLocationTileConfig
+ private val mapper by lazy { LocationTileMapper(context) }
+
+ @Test
+ fun mapsDisabledDataToInactiveState() {
+ val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
+
+ val actualActivationState = tileState.activationState
+ Assert.assertEquals(QSTileState.ActivationState.INACTIVE, actualActivationState)
+ }
+
+ @Test
+ fun mapsEnabledDataToActiveState() {
+ val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
+
+ val actualActivationState = tileState.activationState
+ Assert.assertEquals(QSTileState.ActivationState.ACTIVE, actualActivationState)
+ }
+
+ @Test
+ fun mapsEnabledDataToOnIconState() {
+ val fakeDrawable = mock<Drawable>()
+ context.orCreateTestableResources.addOverride(R.drawable.qs_location_icon_on, fakeDrawable)
+ val expectedIcon = Icon.Loaded(fakeDrawable, null)
+
+ val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
+
+ val actualIcon = tileState.icon()
+ Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
+ }
+
+ @Test
+ fun mapsDisabledDataToOffIconState() {
+ val fakeDrawable = mock<Drawable>()
+ context.orCreateTestableResources.addOverride(R.drawable.qs_location_icon_off, fakeDrawable)
+ val expectedIcon = Icon.Loaded(fakeDrawable, null)
+
+ val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
+
+ val actualIcon = tileState.icon()
+ Truth.assertThat(actualIcon).isEqualTo(expectedIcon)
+ }
+
+ @Test
+ fun supportsClickAndLongClickActions() {
+ val dontCare = true
+
+ val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(dontCare))
+
+ val supportedActions = tileState.supportedActions
+ Truth.assertThat(supportedActions)
+ .containsExactly(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt
new file mode 100644
index 000000000000..8fdc93be4ba2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.qs.tiles.impl.location.interactor
+
+import android.os.UserHandle
+import android.testing.LeakCheck
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileDataInteractor
+import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
+import com.android.systemui.utils.leaks.FakeLocationController
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LocationTileDataInteractorTest : SysuiTestCase() {
+ private lateinit var controller: FakeLocationController
+ private lateinit var underTest: LocationTileDataInteractor
+
+ @Before
+ fun setup() {
+ controller = FakeLocationController(LeakCheck())
+ underTest = LocationTileDataInteractor(controller)
+ }
+
+ @Test
+ fun isAvailableRegardlessOfController() = runTest {
+ controller.setLocationEnabled(false)
+
+ runCurrent()
+ val availability by collectLastValue(underTest.availability(TEST_USER))
+
+ Truth.assertThat(availability).isTrue()
+ }
+
+ @Test
+ fun dataMatchesController() = runTest {
+ controller.setLocationEnabled(false)
+ val flowValues: List<LocationTileModel> by
+ collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)))
+
+ runCurrent()
+ controller.setLocationEnabled(true)
+ runCurrent()
+ controller.setLocationEnabled(false)
+ runCurrent()
+
+ Truth.assertThat(flowValues.size).isEqualTo(3)
+ Truth.assertThat(flowValues.map { it.isEnabled })
+ .containsExactly(false, true, false)
+ .inOrder()
+ }
+
+ private companion object {
+ val TEST_USER = UserHandle.of(1)!!
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
new file mode 100644
index 000000000000..0fb8ae697190
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.qs.tiles.impl.location.interactor
+
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.intentInputs
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.longClick
+import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
+import com.android.systemui.statusbar.phone.FakeKeyguardStateController
+import com.android.systemui.statusbar.policy.LocationController
+import com.google.common.truth.Truth.assertThat
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LocationTileUserActionInteractorTest : SysuiTestCase() {
+
+ private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
+ private val keyguardController = FakeKeyguardStateController()
+
+ private lateinit var underTest: LocationTileUserActionInteractor
+
+ @Mock private lateinit var locationController: LocationController
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ val kosmos = Kosmos()
+ underTest =
+ LocationTileUserActionInteractor(
+ EmptyCoroutineContext,
+ kosmos.testScope,
+ locationController,
+ qsTileIntentUserActionHandler,
+ activityStarter,
+ keyguardController,
+ )
+ }
+
+ @Test
+ fun handleClickToEnable() = runTest {
+ val stateBeforeClick = false
+
+ underTest.handleInput(click(LocationTileModel(stateBeforeClick)))
+
+ Mockito.verify(locationController).setLocationEnabled(!stateBeforeClick)
+ }
+
+ @Test
+ fun handleClickToDisable() = runTest {
+ val stateBeforeClick = true
+
+ underTest.handleInput(click(LocationTileModel(stateBeforeClick)))
+
+ Mockito.verify(locationController).setLocationEnabled(!stateBeforeClick)
+ }
+
+ @Test
+ fun handleLongClick() = runTest {
+ val dontCare = true
+
+ underTest.handleInput(longClick(LocationTileModel(dontCare)))
+
+ assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1)
+ val intentInput = qsTileIntentUserActionHandler.intentInputs.last()
+ val actualIntentAction = intentInput.intent.action
+ val expectedIntentAction = Settings.ACTION_LOCATION_SOURCE_SETTINGS
+ assertThat(actualIntentAction).isEqualTo(expectedIntentAction)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
new file mode 100644
index 000000000000..f04dfd1b8fdb
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.NotificationContainerBounds
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.shared.flag.sceneContainerFlags
+import com.android.systemui.scene.shared.model.ObservableTransitionState
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationStackAppearanceViewModel
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
+
+ private val kosmos =
+ testKosmos().apply {
+ sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
+ featureFlagsClassic.apply {
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.NSSL_DEBUG_LINES, false)
+ }
+ }
+ private val testScope = kosmos.testScope
+ private val placeholderViewModel = kosmos.notificationsPlaceholderViewModel
+ private val appearanceViewModel = kosmos.notificationStackAppearanceViewModel
+ private val sceneInteractor = kosmos.sceneInteractor
+
+ @Test
+ fun updateBounds() =
+ testScope.runTest {
+ val bounds by collectLastValue(appearanceViewModel.stackBounds)
+
+ val top = 200f
+ val bottom = 550f
+ placeholderViewModel.onBoundsChanged(top, bottom)
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom))
+ }
+
+ @Test
+ fun updateShadeExpansion() =
+ testScope.runTest {
+ val expandFraction by collectLastValue(appearanceViewModel.expandFraction)
+ assertThat(expandFraction).isEqualTo(0f)
+
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(scene = SceneKey.Lockscreen)
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "reason")
+ val transitionProgress = MutableStateFlow(0f)
+ transitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Lockscreen,
+ toScene = SceneKey.Shade,
+ progress = transitionProgress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ val steps = 10
+ repeat(steps) { repetition ->
+ val progress = (1f / steps) * (repetition + 1)
+ transitionProgress.value = progress
+ runCurrent()
+ assertThat(expandFraction).isWithin(0.01f).of(progress)
+ }
+
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
+ assertThat(expandFraction).isWithin(0.01f).of(1f)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
new file mode 100644
index 000000000000..c7411cd78b78
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.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.statusbar.notification.stack.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.NotificationContainerBounds
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationStackAppearanceInteractorTest : SysuiTestCase() {
+
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.notificationStackAppearanceInteractor
+
+ @Test
+ fun stackBounds() =
+ testScope.runTest {
+ val stackBounds by collectLastValue(underTest.stackBounds)
+
+ val bounds1 =
+ NotificationContainerBounds(
+ top = 100f,
+ bottom = 200f,
+ isAnimated = true,
+ )
+ underTest.setStackBounds(bounds1)
+ assertThat(stackBounds).isEqualTo(bounds1)
+
+ val bounds2 =
+ NotificationContainerBounds(
+ top = 200f,
+ bottom = 300f,
+ isAnimated = false,
+ )
+ underTest.setStackBounds(bounds2)
+ assertThat(stackBounds).isEqualTo(bounds2)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun setStackBounds_withImproperBounds_throwsException() =
+ testScope.runTest {
+ underTest.setStackBounds(
+ NotificationContainerBounds(
+ top = 100f,
+ bottom = 99f,
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt
new file mode 100644
index 000000000000..fdd98bec0a2d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.common.shared.model
+
+/** Models the bounds of the notification container. */
+data class NotificationContainerBounds(
+ /** The position of the top of the container in its window coordinate system, in pixels. */
+ val top: Float = 0f,
+ /** The position of the bottom of the container in its window coordinate system, in pixels. */
+ val bottom: Float = 0f,
+ /** Whether any modifications to top/bottom should be smoothly animated. */
+ val isAnimated: Boolean = false,
+) {
+ /** The current height of the notification container. */
+ val height: Float = bottom - top
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index e6f1a5482848..2a0d6a8a77d9 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -44,11 +44,11 @@ object Flags {
// 100 - notification
// TODO(b/297792660): Tracking Bug
@JvmField val UNCLEARED_TRANSIENT_HUN_FIX =
- unreleasedFlag("uncleared_transient_hun_fix", teamfood = false)
+ unreleasedFlag("uncleared_transient_hun_fix", teamfood = true)
// TODO(b/298308067): Tracking Bug
@JvmField val SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX =
- unreleasedFlag("swipe_uncleared_transient_view_fix", teamfood = false)
+ unreleasedFlag("swipe_uncleared_transient_view_fix", teamfood = true)
// TODO(b/254512751): Tracking Bug
val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 91b671599eb0..e58d7710877b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -27,8 +27,8 @@ import com.android.keyguard.KeyguardClockSwitch.ClockSize
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.shared.model.Position
-import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -82,15 +82,14 @@ constructor(
sceneInteractorProvider: Provider<SceneInteractor>,
) {
// TODO(b/296118689): move to a repository
- private val _sharedNotificationContainerPosition =
- MutableStateFlow(SharedNotificationContainerPosition())
+ private val _sharedNotificationContainerBounds = MutableStateFlow(NotificationContainerBounds())
- /** Position information for the shared notification container. */
- val sharedNotificationContainerPosition: StateFlow<SharedNotificationContainerPosition> =
- _sharedNotificationContainerPosition.asStateFlow()
+ /** Bounds of the notification container. */
+ val notificationContainerBounds: StateFlow<NotificationContainerBounds> =
+ _sharedNotificationContainerBounds.asStateFlow()
- fun setSharedNotificationContainerPosition(position: SharedNotificationContainerPosition) {
- _sharedNotificationContainerPosition.value = position
+ fun setNotificationContainerBounds(position: NotificationContainerBounds) {
+ _sharedNotificationContainerBounds.value = position
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 114fd94ebeb2..c0d3d336719e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -308,7 +308,7 @@ object KeyguardRootViewBinder {
private class OnLayoutChange(private val viewModel: KeyguardRootViewModel) :
OnLayoutChangeListener {
override fun onLayoutChange(
- v: View,
+ view: View,
left: Int,
top: Int,
right: Int,
@@ -318,18 +318,16 @@ object KeyguardRootViewBinder {
oldRight: Int,
oldBottom: Int
) {
- val nsslPlaceholder = v.findViewById(R.id.nssl_placeholder) as View?
- if (nsslPlaceholder != null) {
+ view.findViewById<View>(R.id.nssl_placeholder)?.let { notificationListPlaceholder ->
// After layout, ensure the notifications are positioned correctly
- viewModel.onSharedNotificationContainerPositionChanged(
- nsslPlaceholder.top.toFloat(),
- nsslPlaceholder.bottom.toFloat(),
+ viewModel.onNotificationContainerBoundsChanged(
+ notificationListPlaceholder.top.toFloat(),
+ notificationListPlaceholder.bottom.toFloat(),
)
}
- val ksv = v.findViewById(R.id.keyguard_status_view) as View?
- if (ksv != null) {
- viewModel.statusViewTop = ksv.top
+ view.findViewById<View>(R.id.keyguard_status_view)?.let { statusView ->
+ viewModel.statusViewTop = statusView.top
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index b3c7d3790527..524fa1ede90a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -22,7 +22,7 @@ import android.util.MathUtils
import android.view.View.VISIBLE
import com.android.app.animation.Interpolators
import com.android.systemui.Flags.newAodTransition
-import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
@@ -98,9 +98,9 @@ constructor(
val goneToAodTransition = keyguardTransitionInteractor.goneToAodTransition
- /** the shared notification container position *on the lockscreen* */
- val notificationPositionOnLockscreen: StateFlow<SharedNotificationContainerPosition>
- get() = keyguardInteractor.sharedNotificationContainerPosition
+ /** the shared notification container bounds *on the lockscreen* */
+ val notificationBounds: StateFlow<NotificationContainerBounds> =
+ keyguardInteractor.notificationContainerBounds
/** An observable for the alpha level for the entire keyguard root view. */
val alpha: Flow<Float> =
@@ -247,14 +247,13 @@ constructor(
previewMode.value = PreviewMode(true)
}
- fun onSharedNotificationContainerPositionChanged(top: Float, bottom: Float) {
+ fun onNotificationContainerBoundsChanged(top: Float, bottom: Float) {
// Notifications should not be visible in preview mode
if (previewMode.value.isInPreviewMode) {
return
}
- keyguardInteractor.setSharedNotificationContainerPosition(
- SharedNotificationContainerPosition(top, bottom)
- )
+
+ keyguardInteractor.setNotificationContainerBounds(NotificationContainerBounds(top, bottom))
}
/** Is there an expanded pulse, are we animating in response? */
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardMediaControllerLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardMediaControllerLog.kt
new file mode 100644
index 000000000000..346f269edbb2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardMediaControllerLog.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 KeyguardMediaController. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class KeyguardMediaControllerLog
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 0d81940cacbd..0b3bbb5c3d08 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -156,6 +156,14 @@ public class LogModule {
return factory.create("NotifRemoteInputLog", 50 /* maxSize */, false /* systrace */);
}
+ /** Provides a logging buffer for all logs related to keyguard media controller. */
+ @Provides
+ @SysUISingleton
+ @KeyguardMediaControllerLog
+ public static LogBuffer provideKeyguardMediaControllerLogBuffer(LogBufferFactory factory) {
+ return factory.create("KeyguardMediaControllerLog", 50 /* maxSize */, false /* systrace */);
+ }
+
/** Provides a logging buffer for all logs related to unseen notifications. */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index 773c292befcf..945bf9a5c0b2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -23,7 +23,6 @@ import android.net.Uri
import android.os.Handler
import android.os.UserHandle
import android.provider.Settings
-import android.util.Log
import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
@@ -63,22 +62,21 @@ constructor(
@Main private val handler: Handler,
configurationController: ConfigurationController,
private val splitShadeStateController: SplitShadeStateController,
+ private val logger: KeyguardMediaControllerLogger,
dumpManager: DumpManager,
) : Dumpable {
- /** It's added for debugging purpose to directly see last received StatusBarState. */
- private var lastReceivedStatusBarState = -1
+ private var lastUsedStatusBarState = -1
init {
dumpManager.registerDumpable(this)
statusBarStateController.addCallback(
object : StatusBarStateController.StateListener {
override fun onStateChanged(newState: Int) {
- lastReceivedStatusBarState = newState
- refreshMediaPosition()
+ refreshMediaPosition(reason = "StatusBarState.onStateChanged")
}
override fun onDozingChanged(isDozing: Boolean) {
- refreshMediaPosition()
+ refreshMediaPosition(reason = "StatusBarState.onDozingChanged")
}
}
)
@@ -100,7 +98,7 @@ constructor(
true,
UserHandle.USER_CURRENT
)
- refreshMediaPosition()
+ refreshMediaPosition(reason = "allowMediaPlayerOnLockScreen changed")
}
}
}
@@ -132,7 +130,7 @@ constructor(
}
field = value
reattachHostView()
- refreshMediaPosition()
+ refreshMediaPosition(reason = "useSplitShade changed")
}
/** Is the media player visible? */
@@ -147,7 +145,7 @@ constructor(
var isDozeWakeUpAnimationWaiting: Boolean = false
set(value) {
field = value
- refreshMediaPosition()
+ refreshMediaPosition(reason = "isDozeWakeUpAnimationWaiting changed")
}
/** single pane media container placed at the top of the notifications list */
@@ -181,7 +179,7 @@ constructor(
/** Called whenever the media hosts visibility changes */
private fun onMediaHostVisibilityChanged(visible: Boolean) {
- refreshMediaPosition()
+ refreshMediaPosition(reason = "onMediaHostVisibilityChanged")
if (visible) {
mediaHost.hostView.layoutParams.apply {
height = ViewGroup.LayoutParams.WRAP_CONTENT
@@ -194,7 +192,7 @@ constructor(
fun attachSplitShadeContainer(container: ViewGroup) {
splitShadeContainer = container
reattachHostView()
- refreshMediaPosition()
+ refreshMediaPosition(reason = "attachSplitShadeContainer")
}
private fun reattachHostView() {
@@ -217,30 +215,41 @@ constructor(
}
}
- fun refreshMediaPosition() {
+ fun refreshMediaPosition(reason: String) {
val currentState = statusBarStateController.state
- if (lastReceivedStatusBarState != -1 && currentState != lastReceivedStatusBarState) {
- Log.wtfStack(
- TAG,
- "currentState[${StatusBarState.toString(currentState)}] is " +
- "different from the last " +
- "received one[${StatusBarState.toString(lastReceivedStatusBarState)}]."
- )
- }
val keyguardOrUserSwitcher = (currentState == StatusBarState.KEYGUARD)
// mediaHost.visible required for proper animations handling
+ val isMediaHostVisible = mediaHost.visible
+ val isBypassNotEnabled = !bypassController.bypassEnabled
+ val currentAllowMediaPlayerOnLockScreen = allowMediaPlayerOnLockScreen
+ val useSplitShade = useSplitShade
+ val shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade()
+
visible =
- mediaHost.visible &&
- !bypassController.bypassEnabled &&
+ isMediaHostVisible &&
+ isBypassNotEnabled &&
keyguardOrUserSwitcher &&
- allowMediaPlayerOnLockScreen &&
- shouldBeVisibleForSplitShade()
+ currentAllowMediaPlayerOnLockScreen &&
+ shouldBeVisibleForSplitShade
if (visible) {
showMediaPlayer()
} else {
hideMediaPlayer()
}
+ logger.logRefreshMediaPosition(
+ reason = reason,
+ visible = visible,
+ useSplitShade = useSplitShade,
+ currentState = currentState,
+ keyguardOrUserSwitcher = keyguardOrUserSwitcher,
+ mediaHostVisible = isMediaHostVisible,
+ bypassNotEnabled = isBypassNotEnabled,
+ currentAllowMediaPlayerOnLockScreen = currentAllowMediaPlayerOnLockScreen,
+ shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade
+ )
+
+ lastUsedStatusBarState = currentState
}
private fun shouldBeVisibleForSplitShade(): Boolean {
@@ -298,10 +307,10 @@ constructor(
println("isDozeWakeUpAnimationWaiting", isDozeWakeUpAnimationWaiting)
println("singlePaneContainer", singlePaneContainer)
println("splitShadeContainer", splitShadeContainer)
- if (lastReceivedStatusBarState != -1) {
+ if (lastUsedStatusBarState != -1) {
println(
- "lastReceivedStatusBarState",
- StatusBarState.toString(lastReceivedStatusBarState)
+ "lastUsedStatusBarState",
+ StatusBarState.toString(lastUsedStatusBarState)
)
}
println(
@@ -311,8 +320,4 @@ constructor(
}
}
}
-
- private companion object {
- private const val TAG = "KeyguardMediaController"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt
new file mode 100644
index 000000000000..41fef88645a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.media.controls.ui
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.dagger.KeyguardMediaControllerLog
+import com.android.systemui.statusbar.StatusBarState
+import javax.inject.Inject
+
+/** Logger class for [KeyguardMediaController]. */
+open class KeyguardMediaControllerLogger
+@Inject
+constructor(@KeyguardMediaControllerLog private val logBuffer: LogBuffer) {
+
+ fun logRefreshMediaPosition(
+ reason: String,
+ visible: Boolean,
+ useSplitShade: Boolean,
+ currentState: Int,
+ keyguardOrUserSwitcher: Boolean,
+ mediaHostVisible: Boolean,
+ bypassNotEnabled: Boolean,
+ currentAllowMediaPlayerOnLockScreen: Boolean,
+ shouldBeVisibleForSplitShade: Boolean
+ ) =
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = reason
+ bool1 = visible
+ bool2 = useSplitShade
+ int1 = currentState
+ bool3 = keyguardOrUserSwitcher
+ bool4 = mediaHostVisible
+ int2 = if (bypassNotEnabled) 1 else 0
+ str2 = currentAllowMediaPlayerOnLockScreen.toString()
+ str3 = shouldBeVisibleForSplitShade.toString()
+ },
+ {
+ "refreshMediaPosition(reason=$str1, " +
+ "currentState=${StatusBarState.toString(int1)}, " +
+ "visible=$bool1, useSplitShade=$bool2, " +
+ "keyguardOrUserSwitcher=$bool3, " +
+ "mediaHostVisible=$bool4, " +
+ "bypassNotEnabled=${int2 == 1}, " +
+ "currentAllowMediaPlayerOnLockScreen=$str2, " +
+ "shouldBeVisibleForSplitShade=$str3)"
+ }
+ )
+
+ private companion object {
+ private const val TAG = "KeyguardMediaControllerLog"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 5e3a166f5f35..f13add9b62b6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -940,50 +940,47 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private void orientSecondaryHomeHandle() {
if (!canShowSecondaryHandle()) {
+ if (mStartingQuickSwitchRotation == -1) {
+ resetSecondaryHandle();
+ }
return;
}
- if (mStartingQuickSwitchRotation == -1) {
- resetSecondaryHandle();
- } else {
- int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation);
- if (mStartingQuickSwitchRotation == -1 || deltaRotation == -1) {
- // Curious if starting quickswitch can change between the if check and our delta
- Log.d(TAG, "secondary nav delta rotation: " + deltaRotation
- + " current: " + mCurrentRotation
- + " starting: " + mStartingQuickSwitchRotation);
- }
- int height = 0;
- int width = 0;
- Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds();
- mOrientationHandle.setDeltaRotation(deltaRotation);
- switch (deltaRotation) {
- case Surface.ROTATION_90:
- case Surface.ROTATION_270:
- height = dispSize.height();
- width = mView.getHeight();
- break;
- case Surface.ROTATION_180:
- case Surface.ROTATION_0:
- // TODO(b/152683657): Need to determine best UX for this
- if (!mShowOrientedHandleForImmersiveMode) {
- resetSecondaryHandle();
- return;
- }
- width = dispSize.width();
- height = mView.getHeight();
- break;
- }
-
- mOrientationParams.gravity =
- deltaRotation == Surface.ROTATION_0 ? Gravity.BOTTOM :
- (deltaRotation == Surface.ROTATION_90 ? Gravity.LEFT : Gravity.RIGHT);
- mOrientationParams.height = height;
- mOrientationParams.width = width;
- mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams);
- mView.setVisibility(View.GONE);
- mOrientationHandle.setVisibility(View.VISIBLE);
+ int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation);
+ if (mStartingQuickSwitchRotation == -1 || deltaRotation == -1) {
+ // Curious if starting quickswitch can change between the if check and our delta
+ Log.d(TAG, "secondary nav delta rotation: " + deltaRotation
+ + " current: " + mCurrentRotation
+ + " starting: " + mStartingQuickSwitchRotation);
+ }
+ int height = 0;
+ int width = 0;
+ Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds();
+ mOrientationHandle.setDeltaRotation(deltaRotation);
+ switch (deltaRotation) {
+ case Surface.ROTATION_90, Surface.ROTATION_270:
+ height = dispSize.height();
+ width = mView.getHeight();
+ break;
+ case Surface.ROTATION_180, Surface.ROTATION_0:
+ // TODO(b/152683657): Need to determine best UX for this
+ if (!mShowOrientedHandleForImmersiveMode) {
+ resetSecondaryHandle();
+ return;
+ }
+ width = dispSize.width();
+ height = mView.getHeight();
+ break;
}
+
+ mOrientationParams.gravity =
+ deltaRotation == Surface.ROTATION_0 ? Gravity.BOTTOM :
+ (deltaRotation == Surface.ROTATION_90 ? Gravity.LEFT : Gravity.RIGHT);
+ mOrientationParams.height = height;
+ mOrientationParams.width = width;
+ mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams);
+ mView.setVisibility(View.GONE);
+ mOrientationHandle.setVisibility(View.VISIBLE);
}
private void resetSecondaryHandle() {
@@ -1786,7 +1783,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
private boolean canShowSecondaryHandle() {
- return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null;
+ return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null
+ && mStartingQuickSwitchRotation != -1;
}
private final UserTracker.Callback mUserChangedCallback =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
new file mode 100644
index 000000000000..8e53723a5a6b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.qs.tiles.impl.location.domain
+
+import android.content.Context
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [LocationTileModel] to [QSTileState]. */
+class LocationTileMapper @Inject constructor(private val context: Context) :
+ QSTileDataToStateMapper<LocationTileModel> {
+
+ override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
+ QSTileState.build(context, config.uiConfig) {
+ val icon =
+ Icon.Loaded(
+ context.resources.getDrawable(
+ if (data.isEnabled) {
+ R.drawable.qs_location_icon_on
+ } else {
+ R.drawable.qs_location_icon_off
+ }
+ ),
+ contentDescription = null
+ )
+ this.icon = { icon }
+
+ this.label = context.resources.getString(R.string.quick_settings_location_label)
+
+ if (data.isEnabled) {
+ activationState = QSTileState.ActivationState.ACTIVE
+ secondaryLabel = context.resources.getStringArray(R.array.tile_states_location)[2]
+ } else {
+ activationState = QSTileState.ActivationState.INACTIVE
+ secondaryLabel = context.resources.getStringArray(R.array.tile_states_location)[1]
+ }
+ contentDescription = label
+ supportedActions =
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt
new file mode 100644
index 000000000000..d1c80309a1cc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.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.qs.tiles.impl.location.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
+import com.android.systemui.statusbar.policy.LocationController
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/** Observes location state changes providing the [LocationTileModel]. */
+class LocationTileDataInteractor
+@Inject
+constructor(
+ private val locationController: LocationController,
+) : QSTileDataInteractor<LocationTileModel> {
+
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>
+ ): Flow<LocationTileModel> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val initialValue = locationController.isLocationEnabled
+ trySend(LocationTileModel(initialValue))
+
+ val callback =
+ object : LocationController.LocationChangeCallback {
+ override fun onLocationSettingsChanged(locationEnabled: Boolean) {
+ trySend(LocationTileModel(locationEnabled))
+ }
+ }
+ locationController.addCallback(callback)
+ awaitClose { locationController.removeCallback(callback) }
+ }
+
+ override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
new file mode 100644
index 000000000000..66705ead3cf0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.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.qs.tiles.impl.location.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.LocationController
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/** Handles location tile clicks. */
+class LocationTileUserActionInteractor
+@Inject
+constructor(
+ @Background private val coroutineContext: CoroutineContext,
+ @Application private val applicationScope: CoroutineScope,
+ private val locationController: LocationController,
+ private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+ private val activityStarter: ActivityStarter,
+ private val keyguardController: KeyguardStateController,
+) : QSTileUserActionInteractor<LocationTileModel> {
+ override suspend fun handleInput(input: QSTileInput<LocationTileModel>): Unit =
+ with(input) {
+ when (action) {
+ is QSTileUserAction.Click -> {
+ val wasEnabled: Boolean = input.data.isEnabled
+ if (keyguardController.isMethodSecure() && keyguardController.isShowing()) {
+ activityStarter.postQSRunnableDismissingKeyguard {
+ CoroutineScope(applicationScope.coroutineContext).launch {
+ locationController.setLocationEnabled(!wasEnabled)
+ }
+ }
+ } else {
+ withContext(coroutineContext) {
+ locationController.setLocationEnabled(!wasEnabled)
+ }
+ }
+ }
+ is QSTileUserAction.LongClick -> {
+ qsTileIntentUserActionHandler.handle(
+ action.view,
+ Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/model/LocationTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/model/LocationTileModel.kt
new file mode 100644
index 000000000000..3095d7e77586
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/model/LocationTileModel.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.qs.tiles.impl.location.domain.model
+
+/**
+ * Location tile model.
+ *
+ * @param isEnabled is true when the location is enabled;
+ */
+@JvmInline value class LocationTileModel(val isEnabled: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c06e9a443a7b..c925010ef4d5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1309,7 +1309,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mQsController.updateResources();
mNotificationsQSContainerController.updateResources();
updateKeyguardStatusViewAlignment(/* animate= */false);
- mKeyguardMediaController.refreshMediaPosition();
+ mKeyguardMediaController.refreshMediaPosition(
+ "NotificationPanelViewController.updateResources");
if (splitShadeChanged) {
onSplitShadeEnabledChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
index 7c10663bbb50..abf09ae9844c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
@@ -17,14 +17,14 @@
package com.android.systemui.statusbar.notification.stack.data.repository
-import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
-/** A repository which holds state about and controlling the appearance of the NSSL */
+/** A repository which holds state about and controlling the appearance of the notification stack */
@SysUISingleton
class NotificationStackAppearanceRepository @Inject constructor() {
- /** The position of the notification stack in the current scene */
- val stackPosition = MutableStateFlow(SharedNotificationContainerPosition(0f, 0f))
+ /** The bounds of the notification stack in the current scene. */
+ val stackBounds = MutableStateFlow(NotificationContainerBounds(0f, 0f))
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 820fe0b1f11e..32e4e89c42c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.stack.domain.interactor
-import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.stack.data.repository.NotificationStackAppearanceRepository
import javax.inject.Inject
@@ -31,13 +31,12 @@ class NotificationStackAppearanceInteractor
constructor(
private val repository: NotificationStackAppearanceRepository,
) {
- /** The position of the notification stack in the current scene */
- val stackPosition: StateFlow<SharedNotificationContainerPosition>
- get() = repository.stackPosition.asStateFlow()
+ /** The bounds of the notification stack in the current scene. */
+ val stackBounds: StateFlow<NotificationContainerBounds> = repository.stackBounds.asStateFlow()
- /** Sets the position of the notification stack in the current scene */
- fun setStackPosition(position: SharedNotificationContainerPosition) {
- check(position.top <= position.bottom) { "Invalid position: $position" }
- repository.stackPosition.value = position
+ /** Sets the position of the notification stack in the current scene. */
+ fun setStackBounds(bounds: NotificationContainerBounds) {
+ check(bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" }
+ repository.stackBounds.value = bounds
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
index 4d6a6ee98b7d..fa7a8fdb7495 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
@@ -40,17 +40,17 @@ object NotificationStackAppearanceViewBinder {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
- viewModel.stackPosition.collect {
+ viewModel.stackBounds.collect { bounds ->
controller.updateTopPadding(
- it.top,
+ bounds.top,
controller.isAddOrRemoveAnimationPending
)
}
}
launch {
- viewModel.expandFraction.collect {
- ambientState.expansionFraction = it
- controller.expandedHeight = it * controller.view.height
+ viewModel.expandFraction.collect { expandFraction ->
+ ambientState.expansionFraction = expandFraction
+ controller.expandedHeight = expandFraction * controller.view.height
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 44006fc3fd4e..5e60b5f4c707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -73,8 +73,9 @@ object SharedNotificationContainerBinder {
if (!sceneContainerFlags.flexiNotifsEnabled()) {
launch {
- viewModel.position.collect {
- val animate = it.animate || controller.isAddOrRemoveAnimationPending
+ viewModel.bounds.collect {
+ val animate =
+ it.isAnimated || controller.isAddOrRemoveAnimationPending
controller.updateTopPadding(it.top, animate)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
index b86993486097..f4c0e92b0e87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
@@ -32,10 +32,9 @@ constructor(
stackAppearanceInteractor: NotificationStackAppearanceInteractor,
shadeInteractor: ShadeInteractor,
) {
- /** The expansion fraction from the top of the notification shade */
+ /** The expansion fraction from the top of the notification shade. */
val expandFraction: Flow<Float> = shadeInteractor.shadeExpansion
- /** The position of the notification stack in the current scene */
- val stackPosition: Flow<SharedNotificationContainerPosition> =
- stackAppearanceInteractor.stackPosition
+ /** The bounds of the notification stack in the current scene. */
+ val stackBounds: Flow<NotificationContainerBounds> = stackAppearanceInteractor.stackBounds
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 7def6feedb41..c6fd98ea2223 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -46,8 +46,18 @@ constructor(
/** DEBUG: whether the debug logging should be output. */
val isDebugLoggingEnabled: Boolean = flags.flexiNotifsEnabled()
- /** Sets the position of the notification stack in the current scene */
- fun setPlaceholderPositionInWindow(top: Float, bottom: Float) {
- interactor.setStackPosition(SharedNotificationContainerPosition(top, bottom))
+ /**
+ * Notifies that the bounds of the notification placeholder have changed.
+ *
+ * @param top The position of the top of the container in its window coordinate system, in
+ * pixels.
+ * @param bottom The position of the bottom of the container in its window coordinate system, in
+ * pixels.
+ */
+ fun onBoundsChanged(
+ top: Float,
+ bottom: Float,
+ ) {
+ interactor.setStackBounds(NotificationContainerBounds(top, bottom))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 09b4dfa31788..1febaf99b7b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -15,9 +15,11 @@
*
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -27,6 +29,7 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.Share
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -35,7 +38,6 @@ import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
@@ -109,18 +111,18 @@ constructor(
*
* When the shade is expanding, the position is controlled by... the shade.
*/
- val position: StateFlow<SharedNotificationContainerPosition> =
+ val bounds: StateFlow<NotificationContainerBounds> =
isOnLockscreenWithoutShade
.flatMapLatest { onLockscreen ->
if (onLockscreen) {
combine(
- keyguardInteractor.sharedNotificationContainerPosition,
+ keyguardInteractor.notificationContainerBounds,
configurationBasedDimensions
- ) { position, config ->
+ ) { bounds, config ->
if (config.useSplitShade) {
- position.copy(top = 0f)
+ bounds.copy(top = 0f)
} else {
- position
+ bounds
}
}
} else {
@@ -129,9 +131,9 @@ constructor(
// When QS expansion > 0, it should directly set the top padding so do not
// animate it
val animate = qsExpansion == 0f
- keyguardInteractor.sharedNotificationContainerPosition.value.copy(
+ keyguardInteractor.notificationContainerBounds.value.copy(
top = top,
- animate = animate
+ isAnimated = animate
)
}
}
@@ -139,7 +141,7 @@ constructor(
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = SharedNotificationContainerPosition(0f, 0f),
+ initialValue = NotificationContainerBounds(0f, 0f),
)
/**
@@ -169,7 +171,7 @@ constructor(
// when the notification stack has changed internally
val limitedNotifications =
combine(
- position,
+ bounds,
interactor.notificationStackChanged.onStart { emit(Unit) },
) { position, _ ->
calculateSpace(position.bottom - position.top)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
index 87df180353b1..bbba19d61b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
@@ -73,11 +73,7 @@ public interface DevicePostureController extends CallbackController<Callback> {
/** Callback to be notified about device posture changes. */
interface Callback {
- /**
- * Called when the posture changes. If there are multiple active displays ("concurrent"),
- * this will report the physical posture of the device (also known as the base device
- * state).
- */
+ /** Called when the posture changes. */
void onPostureChanged(@DevicePostureInt int posture);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
index 8365e0fd41b5..a32a5ab13748 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -36,11 +36,8 @@ import javax.inject.Inject;
/** Implementation of {@link DevicePostureController} using the DeviceStateManager. */
@SysUISingleton
public class DevicePostureControllerImpl implements DevicePostureController {
- /** From androidx.window.common.COMMON_STATE_USE_BASE_STATE */
- private static final int COMMON_STATE_USE_BASE_STATE = 1000;
private final List<Callback> mListeners = new ArrayList<>();
private int mCurrentDevicePosture = DEVICE_POSTURE_UNKNOWN;
- private int mCurrentBasePosture = DEVICE_POSTURE_UNKNOWN;
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
@@ -73,25 +70,12 @@ public class DevicePostureControllerImpl implements DevicePostureController {
mDeviceStateToPostureMap.put(deviceState, posture);
}
- deviceStateManager.registerCallback(executor, new DeviceStateManager.DeviceStateCallback() {
- @Override
- public void onStateChanged(int state) {
- Assert.isMainThread();
- mCurrentDevicePosture =
- mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+ deviceStateManager.registerCallback(executor, state -> {
+ Assert.isMainThread();
+ mCurrentDevicePosture =
+ mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
- mListeners.forEach(l -> l.onPostureChanged(getDevicePosture()));
- }
-
- @Override
- public void onBaseStateChanged(int state) {
- Assert.isMainThread();
- mCurrentBasePosture = mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
-
- if (useBaseState()) {
- mListeners.forEach(l -> l.onPostureChanged(getDevicePosture()));
- }
- }
+ mListeners.forEach(l -> l.onPostureChanged(mCurrentDevicePosture));
});
}
@@ -109,14 +93,6 @@ public class DevicePostureControllerImpl implements DevicePostureController {
@Override
public int getDevicePosture() {
- if (useBaseState()) {
- return mCurrentBasePosture;
- } else {
- return mCurrentDevicePosture;
- }
- }
-
- private boolean useBaseState() {
- return mCurrentDevicePosture == COMMON_STATE_USE_BASE_STATE;
+ return mCurrentDevicePosture;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
index 78f48bb511e5..75ae16ebab31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -30,6 +30,10 @@ import com.android.systemui.qs.tiles.impl.flashlight.domain.FlashlightMapper
import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileDataInteractor
import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
+import com.android.systemui.qs.tiles.impl.location.domain.LocationTileMapper
+import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileDataInteractor
+import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
@@ -54,8 +58,9 @@ interface PolicyModule {
companion object {
const val FLASHLIGHT_TILE_SPEC = "flashlight"
+ const val LOCATION_TILE_SPEC = "location"
- /** Inject config */
+ /** Inject flashlight config */
@Provides
@IntoMap
@StringKey(FLASHLIGHT_TILE_SPEC)
@@ -86,6 +91,38 @@ interface PolicyModule {
stateInteractor,
mapper,
)
+
+ /** Inject location config */
+ @Provides
+ @IntoMap
+ @StringKey(LOCATION_TILE_SPEC)
+ fun provideLocationTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(LOCATION_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.qs_location_icon_off,
+ labelRes = R.string.quick_settings_location_label,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ )
+
+ /** Inject LocationTile into tileViewModelMap in QSModule */
+ @Provides
+ @IntoMap
+ @StringKey(LOCATION_TILE_SPEC)
+ fun provideLocationTileViewModel(
+ factory: QSTileViewModelFactory.Static<LocationTileModel>,
+ mapper: LocationTileMapper,
+ stateInteractor: LocationTileDataInteractor,
+ userActionInteractor: LocationTileUserActionInteractor
+ ): QSTileViewModel =
+ factory.create(
+ TileSpec.create(LOCATION_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
}
/** Inject FlashlightTile into tileMap in QSModule */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index baac51385ba1..ba72b4c95a44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -14,83 +14,56 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestComponent
-import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.TestMocksModule
-import com.android.systemui.collectLastValue
-import com.android.systemui.collectValues
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.runCurrent
-import com.android.systemui.runTest
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.testKosmos
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
-import dagger.BindsInstance
-import dagger.Component
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
- @SysUISingleton
- @Component(
- modules =
- [
- SysUITestModule::class,
- UserDomainLayerModule::class,
- ]
- )
- interface TestComponent : SysUITestComponent<LockscreenToDreamingTransitionViewModel> {
- val repository: FakeKeyguardTransitionRepository
- val keyguardRepository: FakeKeyguardRepository
- val shadeRepository: FakeShadeRepository
-
- @Component.Factory
- interface Factory {
- fun create(
- @BindsInstance test: SysuiTestCase,
- featureFlags: FakeFeatureFlagsClassicModule,
- mocks: TestMocksModule,
- ): TestComponent
- }
- }
- private fun TestComponent.shadeExpanded(expanded: Boolean) {
- if (expanded) {
- shadeRepository.setQsExpansion(1f)
- } else {
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setQsExpansion(0f)
- shadeRepository.setLockscreenShadeExpansion(0f)
+ private val kosmos =
+ testKosmos().apply {
+ featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
- }
+ private val testScope = kosmos.testScope
+ private val repository = kosmos.fakeKeyguardTransitionRepository
+ private val shadeRepository = kosmos.shadeRepository
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
+ private val underTest =
+ LockscreenToDreamingTransitionViewModel(
+ interactor = kosmos.keyguardTransitionInteractor,
+ shadeDependentFlows = kosmos.shadeDependentFlows,
+ )
- private val testComponent: TestComponent =
- DaggerLockscreenToDreamingTransitionViewModelTest_TestComponent.factory()
- .create(
- test = this,
- featureFlags =
- FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
- mocks = TestMocksModule(),
- )
@Test
fun lockscreenFadeOut() =
- testComponent.runTest {
+ testScope.runTest {
val values by collectValues(underTest.lockscreenAlpha)
repository.sendTransitionSteps(
steps =
@@ -113,7 +86,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
@Test
fun lockscreenTranslationY() =
- testComponent.runTest {
+ testScope.runTest {
val pixels = 100
val values by collectValues(underTest.lockscreenTranslationY(pixels))
@@ -138,7 +111,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
@Test
fun deviceEntryParentViewAlpha_shadeExpanded() =
- testComponent.runTest {
+ testScope.runTest {
val values by collectValues(underTest.deviceEntryParentViewAlpha)
shadeExpanded(true)
runCurrent()
@@ -162,7 +135,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
@Test
fun deviceEntryParentViewAlpha_shadeNotExpanded() =
- testComponent.runTest {
+ testScope.runTest {
val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
shadeExpanded(false)
runCurrent()
@@ -194,4 +167,14 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
ownerName = "LockscreenToDreamingTransitionViewModelTest"
)
}
+
+ private fun shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 29d8f082795a..3536d5c77c93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -14,84 +14,56 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestComponent
-import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.TestMocksModule
-import com.android.systemui.collectLastValue
-import com.android.systemui.collectValues
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.runCurrent
-import com.android.systemui.runTest
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.testKosmos
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
-import dagger.BindsInstance
-import dagger.Component
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
- @SysUISingleton
- @Component(
- modules =
- [
- SysUITestModule::class,
- UserDomainLayerModule::class,
- ]
- )
- interface TestComponent : SysUITestComponent<LockscreenToOccludedTransitionViewModel> {
- val repository: FakeKeyguardTransitionRepository
- val keyguardRepository: FakeKeyguardRepository
- val shadeRepository: FakeShadeRepository
-
- @Component.Factory
- interface Factory {
- fun create(
- @BindsInstance test: SysuiTestCase,
- featureFlags: FakeFeatureFlagsClassicModule,
- mocks: TestMocksModule,
- ): TestComponent
- }
- }
- private fun TestComponent.shadeExpanded(expanded: Boolean) {
- if (expanded) {
- shadeRepository.setQsExpansion(1f)
- } else {
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setQsExpansion(0f)
- shadeRepository.setLockscreenShadeExpansion(0f)
+ private val kosmos =
+ testKosmos().apply {
+ featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
- }
-
- private val testComponent: TestComponent =
- DaggerLockscreenToOccludedTransitionViewModelTest_TestComponent.factory()
- .create(
- test = this,
- featureFlags =
- FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
- mocks = TestMocksModule(),
- )
+ private val testScope = kosmos.testScope
+ private val repository = kosmos.fakeKeyguardTransitionRepository
+ private val shadeRepository = kosmos.shadeRepository
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
+ private val underTest =
+ LockscreenToOccludedTransitionViewModel(
+ interactor = kosmos.keyguardTransitionInteractor,
+ shadeDependentFlows = kosmos.shadeDependentFlows,
+ )
@Test
fun lockscreenFadeOut() =
- testComponent.runTest {
+ testScope.runTest {
val values by collectValues(underTest.lockscreenAlpha)
repository.sendTransitionSteps(
steps =
@@ -113,7 +85,7 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
@Test
fun lockscreenTranslationY() =
- testComponent.runTest {
+ testScope.runTest {
val pixels = 100
val values by collectValues(underTest.lockscreenTranslationY(pixels))
repository.sendTransitionSteps(
@@ -133,7 +105,7 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
@Test
fun lockscreenTranslationYIsCanceled() =
- testComponent.runTest {
+ testScope.runTest {
val pixels = 100
val values by collectValues(underTest.lockscreenTranslationY(pixels))
repository.sendTransitionSteps(
@@ -155,7 +127,7 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
@Test
fun deviceEntryParentViewAlpha_shadeExpanded() =
- testComponent.runTest {
+ testScope.runTest {
val values by collectValues(underTest.deviceEntryParentViewAlpha)
shadeExpanded(true)
runCurrent()
@@ -176,7 +148,7 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
@Test
fun deviceEntryParentViewAlpha_shadeNotExpanded() =
- testComponent.runTest {
+ testScope.runTest {
val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
shadeExpanded(false)
runCurrent()
@@ -208,4 +180,14 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
ownerName = "LockscreenToOccludedTransitionViewModelTest"
)
}
+
+ private fun shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
index f4293f035cd1..50f0eb4eca2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
@@ -94,6 +94,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
fakeHandler,
configurationController,
ResourcesSplitShadeStateController(),
+ mock<KeyguardMediaControllerLogger>(),
mock<DumpManager>()
)
keyguardMediaController.attachSinglePaneContainer(mediaContainerView)
@@ -104,7 +105,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
fun testHiddenWhenHostIsHidden() {
whenever(mediaHost.visible).thenReturn(false)
- keyguardMediaController.refreshMediaPosition()
+ keyguardMediaController.refreshMediaPosition(TEST_REASON)
assertThat(mediaContainerView.visibility).isEqualTo(GONE)
}
@@ -118,7 +119,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
private fun testStateVisibility(state: Int, visibility: Int) {
whenever(statusBarStateController.state).thenReturn(state)
- keyguardMediaController.refreshMediaPosition()
+ keyguardMediaController.refreshMediaPosition(TEST_REASON)
assertThat(mediaContainerView.visibility).isEqualTo(visibility)
}
@@ -126,7 +127,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
fun testHiddenOnKeyguard_whenMediaOnLockScreenDisabled() {
settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 0)
- keyguardMediaController.refreshMediaPosition()
+ keyguardMediaController.refreshMediaPosition(TEST_REASON)
assertThat(mediaContainerView.visibility).isEqualTo(GONE)
}
@@ -135,7 +136,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
fun testAvailableOnKeyguard_whenMediaOnLockScreenEnabled() {
settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 1)
- keyguardMediaController.refreshMediaPosition()
+ keyguardMediaController.refreshMediaPosition(TEST_REASON)
assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
}
@@ -234,4 +235,8 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
whenever(statusBarStateController.isDozing).thenReturn(true)
statusBarStateListener.onDozingChanged(true)
}
+
+ private companion object {
+ private const val TEST_REASON = "test reason"
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 3d7cff44322b..ac7c2aa98065 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -15,36 +15,36 @@
*
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestComponent
-import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.TestMocksModule
-import com.android.systemui.collectLastValue
-import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.common.shared.model.NotificationContainerBounds
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
-import com.android.systemui.runCurrent
-import com.android.systemui.runTest
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
-import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import dagger.BindsInstance
-import dagger.Component
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,45 +52,29 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SharedNotificationContainerViewModelTest : SysuiTestCase() {
- @SysUISingleton
- @Component(
- modules =
- [
- SysUITestModule::class,
- UserDomainLayerModule::class,
- ]
- )
- interface TestComponent : SysUITestComponent<SharedNotificationContainerViewModel> {
-
- val configurationRepository: FakeConfigurationRepository
- val keyguardRepository: FakeKeyguardRepository
- val keyguardInteractor: KeyguardInteractor
- val keyguardTransitionRepository: FakeKeyguardTransitionRepository
- val shadeRepository: FakeShadeRepository
- val sharedNotificationContainerInteractor: SharedNotificationContainerInteractor
-
- @Component.Factory
- interface Factory {
- fun create(
- @BindsInstance test: SysuiTestCase,
- featureFlags: FakeFeatureFlagsClassicModule,
- mocks: TestMocksModule,
- ): TestComponent
+ val kosmos =
+ testKosmos().apply {
+ featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
}
+ val testScope = kosmos.testScope
+ val configurationRepository = kosmos.fakeConfigurationRepository
+ val keyguardRepository = kosmos.fakeKeyguardRepository
+ val keyguardInteractor = kosmos.keyguardInteractor
+ val keyguardRootViewModel = kosmos.keyguardRootViewModel
+ val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ val shadeRepository = kosmos.shadeRepository
+ val sharedNotificationContainerInteractor = kosmos.sharedNotificationContainerInteractor
+
+ val underTest = kosmos.sharedNotificationContainerViewModel
+
+ @Before
+ fun setUp() {
+ overrideResource(R.bool.config_use_split_notification_shade, false)
}
- private val testComponent: TestComponent =
- DaggerSharedNotificationContainerViewModelTest_TestComponent.factory()
- .create(
- test = this,
- featureFlags =
- FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
- mocks = TestMocksModule(),
- )
-
@Test
fun validateMarginStartInSplitShade() =
- testComponent.runTest {
+ testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, true)
overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
@@ -103,7 +87,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun validateMarginStart() =
- testComponent.runTest {
+ testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, false)
overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
@@ -116,7 +100,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun validateMarginEnd() =
- testComponent.runTest {
+ testScope.runTest {
overrideResource(R.dimen.notification_panel_margin_horizontal, 50)
val dimens by collectLastValue(underTest.configurationBasedDimensions)
@@ -128,7 +112,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun validateMarginBottom() =
- testComponent.runTest {
+ testScope.runTest {
overrideResource(R.dimen.notification_panel_margin_bottom, 50)
val dimens by collectLastValue(underTest.configurationBasedDimensions)
@@ -140,7 +124,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun validateMarginTopWithLargeScreenHeader() =
- testComponent.runTest {
+ testScope.runTest {
overrideResource(R.bool.config_use_large_screen_shade_header, true)
overrideResource(R.dimen.large_screen_shade_header_height, 50)
overrideResource(R.dimen.notification_panel_margin_top, 0)
@@ -154,7 +138,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun validateMarginTop() =
- testComponent.runTest {
+ testScope.runTest {
overrideResource(R.bool.config_use_large_screen_shade_header, false)
overrideResource(R.dimen.large_screen_shade_header_height, 50)
overrideResource(R.dimen.notification_panel_margin_top, 0)
@@ -168,7 +152,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun isOnLockscreen() =
- testComponent.runTest {
+ testScope.runTest {
val isOnLockscreen by collectLastValue(underTest.isOnLockscreen)
keyguardTransitionRepository.sendTransitionSteps(
@@ -206,7 +190,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun isOnLockscreenWithoutShade() =
- testComponent.runTest {
+ testScope.runTest {
val isOnLockscreenWithoutShade by collectLastValue(underTest.isOnLockscreenWithoutShade)
// First on AOD
@@ -242,8 +226,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun positionOnLockscreenNotInSplitShade() =
- testComponent.runTest {
- val position by collectLastValue(underTest.position)
+ testScope.runTest {
+ val position by collectLastValue(underTest.bounds)
// When not in split shade
overrideResource(R.bool.config_use_split_notification_shade, false)
@@ -253,18 +237,17 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
// Start on lockscreen
showLockscreen()
- keyguardInteractor.setSharedNotificationContainerPosition(
- SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 1f, bottom = 2f)
)
- assertThat(position)
- .isEqualTo(SharedNotificationContainerPosition(top = 1f, bottom = 2f))
+ assertThat(position).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f))
}
@Test
fun positionOnLockscreenInSplitShade() =
- testComponent.runTest {
- val position by collectLastValue(underTest.position)
+ testScope.runTest {
+ val position by collectLastValue(underTest.bounds)
// When in split shade
overrideResource(R.bool.config_use_split_notification_shade, true)
@@ -274,20 +257,19 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
// Start on lockscreen
showLockscreen()
- keyguardInteractor.setSharedNotificationContainerPosition(
- SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 1f, bottom = 2f)
)
runCurrent()
// Top should be overridden to 0f
- assertThat(position)
- .isEqualTo(SharedNotificationContainerPosition(top = 0f, bottom = 2f))
+ assertThat(position).isEqualTo(NotificationContainerBounds(top = 0f, bottom = 2f))
}
@Test
- fun positionOnShade() =
- testComponent.runTest {
- val position by collectLastValue(underTest.position)
+ fun boundsOnShade() =
+ testScope.runTest {
+ val bounds by collectLastValue(underTest.bounds)
// Start on lockscreen with shade expanded
showLockscreenWithShadeExpanded()
@@ -295,16 +277,14 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
// When not in split shade
sharedNotificationContainerInteractor.setTopPosition(10f)
- assertThat(position)
- .isEqualTo(
- SharedNotificationContainerPosition(top = 10f, bottom = 0f, animate = true)
- )
+ assertThat(bounds)
+ .isEqualTo(NotificationContainerBounds(top = 10f, bottom = 0f, isAnimated = true))
}
@Test
- fun positionOnQS() =
- testComponent.runTest {
- val position by collectLastValue(underTest.position)
+ fun boundsOnQS() =
+ testScope.runTest {
+ val bounds by collectLastValue(underTest.bounds)
// Start on lockscreen with shade expanded
showLockscreenWithQSExpanded()
@@ -312,15 +292,13 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
// When not in split shade
sharedNotificationContainerInteractor.setTopPosition(10f)
- assertThat(position)
- .isEqualTo(
- SharedNotificationContainerPosition(top = 10f, bottom = 0f, animate = false)
- )
+ assertThat(bounds)
+ .isEqualTo(NotificationContainerBounds(top = 10f, bottom = 0f, isAnimated = false))
}
@Test
fun maxNotificationsOnLockscreen() =
- testComponent.runTest {
+ testScope.runTest {
var notificationCount = 10
val maxNotifications by
collectLastValue(underTest.getMaxNotifications { notificationCount })
@@ -329,8 +307,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
overrideResource(R.bool.config_use_split_notification_shade, false)
configurationRepository.onAnyConfigurationChange()
- keyguardInteractor.setSharedNotificationContainerPosition(
- SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 1f, bottom = 2f)
)
assertThat(maxNotifications).isEqualTo(10)
@@ -343,7 +321,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun maxNotificationsOnLockscreen_DoesNotUpdateWhenUserInteracting() =
- testComponent.runTest {
+ testScope.runTest {
var notificationCount = 10
val maxNotifications by
collectLastValue(underTest.getMaxNotifications { notificationCount })
@@ -352,8 +330,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
overrideResource(R.bool.config_use_split_notification_shade, false)
configurationRepository.onAnyConfigurationChange()
- keyguardInteractor.setSharedNotificationContainerPosition(
- SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 1f, bottom = 2f)
)
assertThat(maxNotifications).isEqualTo(10)
@@ -379,7 +357,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun maxNotificationsOnShade() =
- testComponent.runTest {
+ testScope.runTest {
val maxNotifications by collectLastValue(underTest.getMaxNotifications { 10 })
// Show lockscreen with shade expanded
@@ -387,15 +365,26 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
overrideResource(R.bool.config_use_split_notification_shade, false)
configurationRepository.onAnyConfigurationChange()
- keyguardInteractor.setSharedNotificationContainerPosition(
- SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 1f, bottom = 2f)
)
// -1 means No Limit
assertThat(maxNotifications).isEqualTo(-1)
}
- private suspend fun TestComponent.showLockscreen() {
+ @Test
+ fun updateBounds_fromKeyguardRoot() =
+ testScope.runTest {
+ val bounds by collectLastValue(underTest.bounds)
+
+ val top = 123f
+ val bottom = 456f
+ keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom)
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top, bottom))
+ }
+
+ private suspend fun showLockscreen() {
shadeRepository.setLockscreenShadeExpansion(0f)
shadeRepository.setQsExpansion(0f)
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
@@ -406,7 +395,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
)
}
- private suspend fun TestComponent.showLockscreenWithShadeExpanded() {
+ private suspend fun showLockscreenWithShadeExpanded() {
shadeRepository.setLockscreenShadeExpansion(1f)
shadeRepository.setQsExpansion(0f)
keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
@@ -417,7 +406,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
)
}
- private suspend fun TestComponent.showLockscreenWithQSExpanded() {
+ private suspend fun showLockscreenWithQSExpanded() {
shadeRepository.setLockscreenShadeExpansion(0f)
shadeRepository.setQsExpansion(1f)
keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
deleted file mode 100644
index ce471705ed85..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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.policy
-
-import android.hardware.devicestate.DeviceStateManager
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.testing.TestableResources
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED
-import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_FLIPPED
-import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
-import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
-import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
-import com.android.systemui.statusbar.policy.DevicePostureController.SUPPORTED_POSTURES_SIZE
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class DevicePostureControllerImplTest : SysuiTestCase() {
- private val useBaseStateDeviceState = SUPPORTED_POSTURES_SIZE
- private val deviceStateToPostureMapping =
- arrayOf(
- "$DEVICE_POSTURE_UNKNOWN:$DEVICE_POSTURE_UNKNOWN",
- "$DEVICE_POSTURE_CLOSED:$DEVICE_POSTURE_CLOSED",
- "$DEVICE_POSTURE_HALF_OPENED:$DEVICE_POSTURE_HALF_OPENED",
- "$DEVICE_POSTURE_OPENED:$DEVICE_POSTURE_OPENED",
- "$DEVICE_POSTURE_FLIPPED:$DEVICE_POSTURE_FLIPPED",
- "$useBaseStateDeviceState:1000"
- )
- @Mock private lateinit var deviceStateManager: DeviceStateManager
- @Captor
- private lateinit var deviceStateCallback: ArgumentCaptor<DeviceStateManager.DeviceStateCallback>
-
- private lateinit var mainExecutor: FakeExecutor
- private lateinit var testableResources: TestableResources
- private lateinit var underTest: DevicePostureControllerImpl
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- mainExecutor = FakeExecutor(FakeSystemClock())
- testableResources = context.getOrCreateTestableResources()
- testableResources.addOverride(
- com.android.internal.R.array.config_device_state_postures,
- deviceStateToPostureMapping
- )
- underTest =
- DevicePostureControllerImpl(
- context,
- deviceStateManager,
- mainExecutor,
- )
- verifyRegistersForDeviceStateCallback()
- }
-
- @Test
- fun testPostureChanged_updates() {
- var posture = -1
- underTest.addCallback { posture = it }
-
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_UNKNOWN)
- assertThat(posture).isEqualTo(DEVICE_POSTURE_UNKNOWN)
-
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_CLOSED)
- assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
-
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
- assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
-
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_OPENED)
- assertThat(posture).isEqualTo(DEVICE_POSTURE_OPENED)
-
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_FLIPPED)
- assertThat(posture).isEqualTo(DEVICE_POSTURE_FLIPPED)
- }
-
- @Test
- fun testPostureChanged_useBaseUpdate() {
- var posture = -1
- underTest.addCallback { posture = it }
-
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
- assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
-
- // base state change doesn't change the posture
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
- assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
-
- // WHEN the display state maps to using the base state, then posture updates
- deviceStateCallback.value.onStateChanged(useBaseStateDeviceState)
- assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
- }
-
- @Test
- fun baseStateChanges_doesNotUpdatePosture() {
- var numPostureChanges = 0
- underTest.addCallback { numPostureChanges++ }
-
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
- assertThat(numPostureChanges).isEqualTo(1)
-
- // base state changes doesn't send another posture update since the device state isn't
- // useBaseStateDeviceState
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_HALF_OPENED)
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_FLIPPED)
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_OPENED)
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_UNKNOWN)
- assertThat(numPostureChanges).isEqualTo(1)
- }
-
- private fun verifyRegistersForDeviceStateCallback() {
- verify(deviceStateManager).registerCallback(eq(mainExecutor), deviceStateCallback.capture())
- }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt
new file mode 100644
index 000000000000..46259a6964cd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt
@@ -0,0 +1,22 @@
+/*
+ * 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
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+
+fun SysuiTestCase.testKosmos(): Kosmos = Kosmos().apply { testCase = this@testKosmos }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepositoryKosmos.kt
new file mode 100644
index 000000000000..8702e00b1e97
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.biometrics.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.fingerprintPropertyRepository by Fixture { FakeFingerprintPropertyRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorKosmos.kt
new file mode 100644
index 000000000000..b04161a7809f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.deviceEntryUdfpsInteractor by Fixture {
+ DeviceEntryUdfpsInteractor(
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ fingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
+ biometricSettingsRepository = biometricSettingsRepository,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/doze/util/BurnInHelperWrapperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/doze/util/BurnInHelperWrapperKosmos.kt
new file mode 100644
index 000000000000..0f7945fb9846
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/doze/util/BurnInHelperWrapperKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.doze.util
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.burnInHelperWrapper by Fixture { BurnInHelperWrapper() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryKosmos.kt
new file mode 100644
index 000000000000..45d39b0d4bc4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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 com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.biometricSettingsRepository by Fixture { FakeBiometricSettingsRepository() }
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryKosmos.kt
index 48d3fe000ff8..6437ef3e50f4 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryKosmos.kt
@@ -14,15 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.common.shared.model
+package com.android.systemui.keyguard.data.repository
-/** Positioning info for the shared notification container */
-data class SharedNotificationContainerPosition(
- val top: Float = 0f,
- val bottom: Float = 0f,
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
- /** Whether any modifications to top/bottom are smoothly animated */
- val animate: Boolean = false,
-) {
- val height: Float = bottom - top
+val Kosmos.deviceEntryFingerprintAuthRepository by Fixture {
+ FakeDeviceEntryFingerprintAuthRepository()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorKosmos.kt
new file mode 100644
index 000000000000..b0d941dc6c24
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorKosmos.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.content.applicationContext
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.doze.util.burnInHelperWrapper
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.burnInInteractor by Fixture {
+ BurnInInteractor(
+ context = applicationContext,
+ burnInHelperWrapper = burnInHelperWrapper,
+ scope = applicationCoroutineScope,
+ configurationRepository = configurationRepository,
+ keyguardInteractor = keyguardInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
new file mode 100644
index 000000000000..a31ab3ee1fc4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.aodToLockscreenTransitionViewModel by Fixture {
+ AodToLockscreenTransitionViewModel(
+ interactor = keyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
new file mode 100644
index 000000000000..5db95cf3ebc5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.goneToAodTransitionViewModel by Fixture {
+ GoneToAodTransitionViewModel(
+ interactor = keyguardTransitionInteractor,
+ deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
new file mode 100644
index 000000000000..663b8450e690
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.content.applicationContext
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.burnInInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
+import com.android.systemui.statusbar.phone.dozeParameters
+import com.android.systemui.statusbar.phone.screenOffAnimationController
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.keyguardRootViewModel by Fixture {
+ KeyguardRootViewModel(
+ context = applicationContext,
+ deviceEntryInteractor = deviceEntryInteractor,
+ dozeParameters = dozeParameters,
+ keyguardInteractor = keyguardInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ notificationsKeyguardInteractor = notificationsKeyguardInteractor,
+ burnInInteractor = burnInInteractor,
+ goneToAodTransitionViewModel = goneToAodTransitionViewModel,
+ aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
+ screenOffAnimationController = screenOffAnimationController,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlowsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlowsKosmos.kt
new file mode 100644
index 000000000000..f533bcad755e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlowsKosmos.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.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+
+val Kosmos.shadeDependentFlows by Fixture {
+ ShadeDependentFlows(
+ transitionInteractor = keyguardTransitionInteractor,
+ shadeInteractor = shadeInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/location/LocationTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/location/LocationTileKosmos.kt
new file mode 100644
index 000000000000..f4c7ca7a21f7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/location/LocationTileKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.qs.tiles.impl.location
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.statusbar.policy.PolicyModule
+
+val Kosmos.qsLocationTileConfig by
+ Kosmos.Fixture { PolicyModule.provideLocationTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
index a3ceef021c59..c2cdbed21abe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt
@@ -18,4 +18,4 @@ package com.android.systemui.scene.shared.flag
import com.android.systemui.kosmos.Kosmos
-val Kosmos.sceneContainerFlags by Kosmos.Fixture { FakeSceneContainerFlags() }
+var Kosmos.sceneContainerFlags by Kosmos.Fixture { FakeSceneContainerFlags() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepositoryKosmos.kt
new file mode 100644
index 000000000000..407390296a6c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepositoryKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.stack.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.notificationStackAppearanceRepository by Fixture {
+ NotificationStackAppearanceRepository()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
new file mode 100644
index 000000000000..546a1e019c6b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.stack.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.stack.data.repository.notificationStackAppearanceRepository
+
+val Kosmos.notificationStackAppearanceInteractor by Fixture {
+ NotificationStackAppearanceInteractor(
+ repository = notificationStackAppearanceRepository,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
new file mode 100644
index 000000000000..61a38b864c40
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.stack.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
+import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
+
+val Kosmos.notificationsKeyguardInteractor by Fixture {
+ NotificationsKeyguardInteractor(
+ repository = notificationsKeyguardViewStateRepository,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.kt
new file mode 100644
index 000000000000..f2f3a5a1ad72
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModelKosmos.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.statusbar.notification.stack.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
+
+val Kosmos.notificationStackAppearanceViewModel by Fixture {
+ NotificationStackAppearanceViewModel(
+ stackAppearanceInteractor = notificationStackAppearanceInteractor,
+ shadeInteractor = shadeInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
new file mode 100644
index 000000000000..0dbade76979c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.stack.ui.viewmodel
+
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.scene.shared.flag.sceneContainerFlags
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
+
+val Kosmos.notificationsPlaceholderViewModel by Fixture {
+ NotificationsPlaceholderViewModel(
+ interactor = notificationStackAppearanceInteractor,
+ flags = sceneContainerFlags,
+ featureFlags = featureFlagsClassic,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
new file mode 100644
index 000000000000..c17083c5fb1c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.stack.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+
+val Kosmos.sharedNotificationContainerViewModel by Fixture {
+ SharedNotificationContainerViewModel(
+ interactor = sharedNotificationContainerInteractor,
+ applicationScope = applicationCoroutineScope,
+ keyguardInteractor = keyguardInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ shadeInteractor = shadeInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeLocationController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeLocationController.java
index 838a2739f0b0..3c6327514f24 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeLocationController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeLocationController.java
@@ -19,8 +19,14 @@ import android.testing.LeakCheck;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
+import java.util.ArrayList;
+import java.util.List;
+
public class FakeLocationController extends BaseLeakChecker<LocationChangeCallback>
implements LocationController {
+
+ private final List<LocationChangeCallback> mCallbacks = new ArrayList<>();
+
public FakeLocationController(LeakCheck test) {
super(test, "location");
}
@@ -37,6 +43,19 @@ public class FakeLocationController extends BaseLeakChecker<LocationChangeCallba
@Override
public boolean setLocationEnabled(boolean enabled) {
+ mCallbacks.forEach(callback -> callback.onLocationSettingsChanged(enabled));
return false;
}
+
+ @Override
+ public void addCallback(LocationChangeCallback callback) {
+ super.addCallback(callback);
+ mCallbacks.add(callback);
+ }
+
+ @Override
+ public void removeCallback(LocationChangeCallback callback) {
+ super.removeCallback(callback);
+ mCallbacks.remove(callback);
+ }
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 49457fbb94f5..3323d0ba64dd 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -125,6 +125,9 @@ java_library_static {
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
"java/com/android/server/policy/EventLogTags.logtags",
+
+ // Java/AIDL sources to be moved out to CrashRecovery module
+ ":services-crashrecovery-sources",
],
libs: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 7e4cf4f35132..898b69310fe1 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -48,6 +48,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.KnownPackages;
+import com.android.server.pm.PackageArchiver;
import com.android.server.pm.PackageList;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.dex.DynamicCodeLogger;
@@ -1442,4 +1443,10 @@ public abstract class PackageManagerInternal {
*/
public abstract void sendPackageDataClearedBroadcast(@NonNull String packageName,
int uid, int userId, boolean isRestore, boolean isInstantApp);
+
+ /**
+ * Returns an instance of {@link PackageArchiver} to be used for archiving related operations.
+ */
+ @NonNull
+ public abstract PackageArchiver getPackageArchiver();
}
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index ce1a8756a849..0e7b4aa32b44 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -94,6 +94,7 @@ import sun.misc.Unsafe;
* <p>Files to pin are specified in the config_defaultPinnerServiceFiles
* overlay.</p>
* <p>Pin the default camera application if specified in config_pinnerCameraApp.</p>
+ * <p>(Optional) Pin experimental carveout regions based on DeviceConfig flags.</p>
*/
public final class PinnerService extends SystemService {
private static final boolean DEBUG = false;
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 80c4c58cea97..708da19232e1 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -71,6 +71,18 @@
"file_patterns": ["BinaryTransparencyService\\.java"]
},
{
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.PinnerServiceTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ],
+ "file_patterns": ["PinnerService\\.java"]
+ },
+ {
"name": "BinaryTransparencyHostTest",
"file_patterns": ["BinaryTransparencyService\\.java"]
},
@@ -139,6 +151,18 @@
},
{
"name": "CtsSuspendAppsTestCases"
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.PinnerServiceTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ],
+ "file_patterns": ["PinnerService\\.java"]
}
]
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index d46d55977d2f..3caff11e0a3a 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3349,9 +3349,7 @@ final class InstallPackageHelper {
if (disabledPs == null) {
logCriticalInfo(Log.WARN, "System package " + packageName
+ " no longer exists; its data will be wiped");
- mInjector.getHandler().post(
- () -> mRemovePackageHelper.removePackageData(ps, userIds));
- expectingBetter.put(ps.getPackageName(), ps.getPath());
+ mRemovePackageHelper.removePackageData(ps, userIds);
} else {
// we still have a disabled system package, but, it still might have
// been removed. check the code path still exists and check there's
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 3f4cc4a84ffd..b80c0094ffb9 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1531,7 +1531,8 @@ public class LauncherAppsService extends SystemService {
throw new ActivityNotFoundException("Activity could not be found");
}
- final Intent launchIntent = getMainActivityLaunchIntent(component, user);
+ final Intent launchIntent = getMainActivityLaunchIntent(component, user,
+ false /* includeArchivedApps */);
if (launchIntent == null) {
throw new SecurityException("Attempt to launch activity without "
+ " category Intent.CATEGORY_LAUNCHER " + component);
@@ -1577,7 +1578,8 @@ public class LauncherAppsService extends SystemService {
return;
}
- Intent launchIntent = getMainActivityLaunchIntent(component, user);
+ Intent launchIntent = getMainActivityLaunchIntent(component, user,
+ true /* includeArchivedApps */);
if (launchIntent == null) {
throw new SecurityException("Attempt to launch activity without "
+ " category Intent.CATEGORY_LAUNCHER " + component);
@@ -1593,7 +1595,8 @@ public class LauncherAppsService extends SystemService {
/**
* Returns the main activity launch intent for the given component package.
*/
- private Intent getMainActivityLaunchIntent(ComponentName component, UserHandle user) {
+ private Intent getMainActivityLaunchIntent(ComponentName component, UserHandle user,
+ boolean includeArchivedApps) {
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -1632,6 +1635,14 @@ public class LauncherAppsService extends SystemService {
break;
}
}
+ if (!canLaunch
+ && includeArchivedApps
+ && Flags.archiving()
+ && getMatchingArchivedAppActivityInfo(component, user) != null) {
+ launchIntent.setPackage(null);
+ launchIntent.setComponent(component);
+ canLaunch = true;
+ }
if (!canLaunch) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 968be5c2cf1c..c6e8a6463121 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -16,6 +16,10 @@
package com.android.server.pm;
+import static android.app.ActivityManager.START_ABORTED;
+import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.ActivityManager.START_PERMISSION_DENIED;
+import static android.app.ActivityManager.START_SUCCESS;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap;
@@ -34,7 +38,10 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
@@ -58,6 +65,7 @@ import android.graphics.drawable.LayerDrawable;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.IBinder;
import android.os.ParcelableException;
import android.os.Process;
import android.os.SELinux;
@@ -71,6 +79,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.ArchiveState.ArchiveActivityInfo;
+import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
@@ -185,6 +194,113 @@ public class PackageArchiver {
});
}
+ /**
+ * Starts unarchival for the package corresponding to the startActivity intent. Note that this
+ * will work only if the caller is the default/Home Launcher or if activity is started via Shell
+ * identity.
+ */
+ @NonNull
+ public int requestUnarchiveOnActivityStart(@Nullable Intent intent,
+ @Nullable String callerPackageName, int userId, int callingUid) {
+ String packageName = getPackageNameFromIntent(intent);
+ if (packageName == null) {
+ Slog.e(TAG, "packageName cannot be null for unarchival!");
+ return START_CLASS_NOT_FOUND;
+ }
+ if (callerPackageName == null) {
+ Slog.e(TAG, "callerPackageName cannot be null for unarchival!");
+ return START_CLASS_NOT_FOUND;
+ }
+ if (!isCallingPackageValid(callerPackageName, callingUid, userId)) {
+ // Return early as the calling UID does not match caller package's UID.
+ return START_CLASS_NOT_FOUND;
+ }
+ String currentLauncherPackageName = getCurrentLauncherPackageName(userId);
+ if ((currentLauncherPackageName == null || !callerPackageName.equals(
+ currentLauncherPackageName)) && callingUid != Process.SHELL_UID) {
+ // TODO(b/311619990): Remove dependency on SHELL_UID for testing
+ Slog.e(TAG, TextUtils.formatSimple(
+ "callerPackageName: %s does not qualify for archival of package: " + "%s!",
+ callerPackageName, packageName));
+ return START_PERMISSION_DENIED;
+ }
+ // TODO(b/302114464): Handle edge cases & also divert to a dialog based on
+ // permissions + compat options
+ Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName));
+ try {
+ final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType,
+ IBinder allowlistToken,
+ IIntentReceiver finishedReceiver, String requiredPermission,
+ Bundle options) {
+ // TODO(b/302114464): Handle intent sender status codes
+ }
+ };
+
+ requestUnarchive(packageName, callerPackageName,
+ new IntentSender((IIntentSender) mLocalSender), UserHandle.of(userId));
+ } catch (Throwable t) {
+ Slog.e(TAG, TextUtils.formatSimple(
+ "Unexpected error occurred while unarchiving package %s: %s.", packageName,
+ t.getLocalizedMessage()));
+ return START_ABORTED;
+ }
+ return START_SUCCESS;
+ }
+
+ /**
+ * Returns true if the componentName targeted by the intent corresponds to that of an archived
+ * app.
+ */
+ public boolean isIntentResolvedToArchivedApp(Intent intent, int userId) {
+ String packageName = getPackageNameFromIntent(intent);
+ if (packageName == null || intent.getComponent() == null) {
+ return false;
+ }
+ PackageState packageState = mPm.snapshotComputer().getPackageStateInternal(packageName);
+ if (packageState == null) {
+ return false;
+ }
+ PackageUserState userState = packageState.getUserStateOrDefault(userId);
+ if (!PackageArchiver.isArchived(userState)) {
+ return false;
+ }
+ List<ArchiveState.ArchiveActivityInfo> archiveActivityInfoList =
+ userState.getArchiveState().getActivityInfos();
+ for (int i = 0; i < archiveActivityInfoList.size(); i++) {
+ if (archiveActivityInfoList.get(i)
+ .getOriginalComponentName().equals(intent.getComponent())) {
+ return true;
+ }
+ }
+ Slog.e(TAG, TextUtils.formatSimple(
+ "Package: %s is archived but component to start main activity"
+ + " cannot be found!", packageName));
+ return false;
+ }
+
+ @Nullable
+ private String getCurrentLauncherPackageName(int userId) {
+ ComponentName defaultLauncherComponent = mPm.snapshotComputer().getDefaultHomeActivity(
+ userId);
+ if (defaultLauncherComponent != null) {
+ return defaultLauncherComponent.getPackageName();
+ }
+ return null;
+ }
+
+ private boolean isCallingPackageValid(String callingPackage, int callingUid, int userId) {
+ int packageUid;
+ packageUid = mPm.snapshotComputer().getPackageUid(callingPackage, 0L, userId);
+ if (packageUid != callingUid) {
+ Slog.w(TAG, TextUtils.formatSimple("Calling package: %s does not belong to uid: %d",
+ callingPackage, callingUid));
+ return false;
+ }
+ return true;
+ }
+
/** Creates archived state for the package and user. */
private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId)
throws PackageManager.NameNotFoundException {
@@ -403,7 +519,7 @@ public class PackageArchiver {
Computer snapshot = mPm.snapshotComputer();
int userId = userHandle.getIdentifier();
int binderUid = Binder.getCallingUid();
- if (!PackageManagerServiceUtils.isRootOrShell(binderUid)) {
+ if (!PackageManagerServiceUtils.isSystemOrRootOrShell(binderUid)) {
verifyCaller(snapshot.getPackageUid(callerPackageName, 0, userId), binderUid);
}
snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
@@ -780,6 +896,20 @@ public class PackageArchiver {
return bytesFromBitmap(BitmapFactory.decodeFile(path.toString()));
}
+ @Nullable
+ private static String getPackageNameFromIntent(@Nullable Intent intent) {
+ if (intent == null) {
+ return null;
+ }
+ if (intent.getPackage() != null) {
+ return intent.getPackage();
+ }
+ if (intent.getComponent() != null) {
+ return intent.getComponent().getPackageName();
+ }
+ return null;
+ }
+
/**
* Creates serializable archived activities from existing ArchiveState.
*/
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index c9663fc30ef5..882e05dd778b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1683,21 +1683,24 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
archivedPackageParcel);
// Create and commit install archived session.
- PackageInstallerSession session = null;
- try {
- var sessionId = createSessionInternal(params, installerPackageName,
- null /*installerAttributionTag*/, Binder.getCallingUid(), userId);
- session = openSessionInternal(sessionId);
- session.addFile(LOCATION_DATA_APP, "base", 0 /*lengthBytes*/, metadata.toByteArray(),
- null /*signature*/);
- session.commit(statusReceiver, false /*forTransfer*/);
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
- } finally {
- if (session != null) {
- session.close();
+ // Session belongs to the system_server and would not appear anywhere in the Public APIs.
+ Binder.withCleanCallingIdentity(() -> {
+ PackageInstallerSession session = null;
+ try {
+ var sessionId = createSessionInternal(params, installerPackageName, null
+ /*installerAttributionTag*/, Binder.getCallingUid(), userId);
+ session = openSessionInternal(sessionId);
+ session.addFile(LOCATION_DATA_APP, "base", 0 /*lengthBytes*/,
+ metadata.toByteArray(), null /*signature*/);
+ session.commit(statusReceiver, false /*forTransfer*/);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ } finally {
+ if (session != null) {
+ session.close();
+ }
}
- }
+ });
}
// TODO(b/307299702) Implement error dialog and propagate userActionIntent.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ec3823f365b6..e557f09467d4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6976,6 +6976,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
@Override
+ public PackageArchiver getPackageArchiver() {
+ return mInstallerService.mPackageArchiver;
+ }
+
+ @Override
public void sendPackageRestartedBroadcast(@NonNull String packageName,
int uid, @Intent.Flags int flags) {
final int userId = UserHandle.getUserId(uid);
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 639d6d78f953..80f69a4897c2 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -395,11 +395,13 @@ final class RemovePackageHelper {
mPm.mSettings.removeRenamedPackageLPw(deletedPs.getRealName());
}
if (changedUsers.size() > 0) {
- final PreferredActivityHelper preferredActivityHelper =
- new PreferredActivityHelper(mPm, mBroadcastHelper);
- preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(),
- changedUsers);
- mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
+ mPm.mInjector.getBackgroundHandler().post(() -> {
+ final PreferredActivityHelper preferredActivityHelper =
+ new PreferredActivityHelper(mPm, mBroadcastHelper);
+ preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(),
+ changedUsers);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
+ });
}
} else if (!deletedPs.isSystem() && outInfo != null && !outInfo.mIsUpdate
&& outInfo.mRemovedUsers != null && !outInfo.mIsExternal) {
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 926e0188c624..7910edcb5959 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -411,6 +411,9 @@ public class PackageInfoUtils {
ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
}
ai.isArchived = PackageArchiver.isArchived(state);
+ if (ai.isArchived) {
+ ai.nonLocalizedLabel = state.getArchiveState().getActivityInfos().get(0).getTitle();
+ }
}
@Nullable
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5e0a449ddd63..d6302e08fedb 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -103,6 +103,7 @@ import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
+import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
@@ -128,6 +129,7 @@ import com.android.internal.app.IVoiceInteractor;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
+import com.android.server.pm.PackageArchiver;
import com.android.server.power.ShutdownCheckPoints;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
@@ -958,6 +960,17 @@ class ActivityStarter {
}
}
+ if (Flags.archiving()) {
+ PackageArchiver packageArchiver = mService
+ .getPackageManagerInternalLocked()
+ .getPackageArchiver();
+ if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) {
+ return packageArchiver
+ .requestUnarchiveOnActivityStart(
+ intent, callingPackage, mRequest.userId, realCallingUid);
+ }
+ }
+
final int launchFlags = intent.getFlags();
if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
// Transfer the result target from the source activity to the new one being started,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index b77596391be1..7239ba9cd07e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -24,6 +24,7 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.content.ComponentName;
@@ -1565,6 +1566,33 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
+ @Override
+ public boolean setSandboxedDetectionTrainingDataOp(int opMode) {
+ synchronized (this) {
+ enforceIsCallerPreinstalledAssistant();
+
+ if (mImpl == null) {
+ Slog.w(TAG, "setSandboxedDetectionTrainingDataop without running"
+ + " voice interaction service");
+ return false;
+ }
+
+ int callingUid = Binder.getCallingUid();
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ AppOpsManager appOpsManager = (AppOpsManager)
+ mContext.getSystemService(Context.APP_OPS_SERVICE);
+ appOpsManager.setUidMode(
+ AppOpsManager.OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
+ callingUid, opMode);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+
+ return true;
+ }
+ }
+
//----------------- Model management APIs --------------------------------//
@Override
@@ -2219,6 +2247,13 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
+ private void enforceIsCallerPreinstalledAssistant() {
+ if (!isCallerPreinstalledAssistant()) {
+ throw new
+ SecurityException("Caller is not the pre-installed assistant.");
+ }
+ }
+
private void enforceCallerAllowedToEnrollVoiceModel() {
if (isCallerHoldingPermission(Manifest.permission.KEYPHRASE_ENROLLMENT_APPLICATION)) {
return;
@@ -2233,6 +2268,13 @@ public class VoiceInteractionManagerService extends SystemService {
&& mImpl.mInfo.getServiceInfo().applicationInfo.uid == Binder.getCallingUid();
}
+ private boolean isCallerPreinstalledAssistant() {
+ return mImpl != null
+ && mImpl.mInfo.getServiceInfo().applicationInfo.uid == Binder.getCallingUid()
+ && (mImpl.mInfo.getServiceInfo().applicationInfo.isSystemApp()
+ || mImpl.mInfo.getServiceInfo().applicationInfo.isUpdatedSystemApp());
+ }
+
private void setImplLocked(VoiceInteractionManagerServiceImpl impl) {
mImpl = impl;
mAtmInternal.notifyActiveVoiceInteractionServiceChanged(
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 13a045858ab1..bbd01d6bcd67 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -289,6 +289,11 @@ public abstract class InCallService extends Service {
switch (msg.what) {
case MSG_SET_IN_CALL_ADAPTER:
+ if (mPhone != null) {
+ Log.i(this, "mPhone is already instantiated, ignoring "
+ + "request to reset adapter.");
+ break;
+ }
String callingPackage = getApplicationContext().getOpPackageName();
mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage,
getApplicationContext().getApplicationInfo().targetSdkVersion);