summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/hid/jni/com_android_commands_hid_Device.cpp28
-rw-r--r--cmds/hid/jni/com_android_commands_hid_Device.h5
-rw-r--r--cmds/hid/src/com/android/commands/hid/Device.java10
-rw-r--r--cmds/hid/src/com/android/commands/hid/Event.java13
-rw-r--r--cmds/hid/src/com/android/commands/hid/Hid.java13
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/app/NotificationChannel.java6
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java10
-rw-r--r--core/java/android/view/ViewRootImpl.java15
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java19
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java33
-rw-r--r--data/etc/com.android.systemui.xml1
-rw-r--r--libs/input/SpriteController.cpp31
-rw-r--r--libs/input/SpriteController.h1
-rw-r--r--libs/input/SpriteIcon.h2
-rw-r--r--media/java/android/media/audiopolicy/AudioMix.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java19
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt37
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt49
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/NoOpInfoMediaManagerTest.java77
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt21
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt16
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt258
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt53
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt94
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt128
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt)135
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt2
-rw-r--r--packages/SystemUI/res/drawable/ic_head_tracking.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_spatial_audio.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_spatial_audio_off.xml26
-rw-r--r--packages/SystemUI/res/values/ids.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt100
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/wakelock/ClientTrackingWakeLock.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/model/SpatialAudioEnabledModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt126
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/data/repository/FakeSpatializerRepository.kt23
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java16
-rw-r--r--services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java10
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java45
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerControllerInterface.java5
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java27
-rw-r--r--services/core/java/com/android/server/display/brightness/StrategySelectionNotifyRequest.java53
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/BoostBrightnessStrategy.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategy.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/DozeBrightnessStrategy.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/InvalidBrightnessStrategy.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategy.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategy.java7
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java12
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java87
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java40
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java31
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING2
100 files changed, 1919 insertions, 644 deletions
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 8b8d361edbd4..a142450ac0c6 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -134,8 +134,9 @@ JNIEnv* DeviceCallback::getJNIEnv() {
return env;
}
-std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
- uint16_t bus, const std::vector<uint8_t>& descriptor,
+std::unique_ptr<Device> Device::open(int32_t id, const char* name, const char* uniq, int32_t vid,
+ int32_t pid, uint16_t bus,
+ const std::vector<uint8_t>& descriptor,
std::unique_ptr<DeviceCallback> callback) {
size_t size = descriptor.size();
if (size > HID_MAX_DESCRIPTOR_SIZE) {
@@ -152,8 +153,7 @@ std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid,
struct uhid_event ev = {};
ev.type = UHID_CREATE2;
strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
- std::string uniq = android::base::StringPrintf("Id: %d", id);
- strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq.c_str(), sizeof(ev.u.create2.uniq));
+ strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq, sizeof(ev.u.create2.uniq));
memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0]));
ev.u.create2.rd_size = size;
ev.u.create2.bus = bus;
@@ -314,19 +314,31 @@ std::vector<uint8_t> getData(JNIEnv* env, jbyteArray javaArray) {
return data;
}
-static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
- jint pid, jint bus, jbyteArray rawDescriptor, jobject callback) {
+static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jstring rawUniq, jint id,
+ jint vid, jint pid, jint bus, jbyteArray rawDescriptor, jobject callback) {
ScopedUtfChars name(env, rawName);
if (name.c_str() == nullptr) {
return 0;
}
+ std::string uniq;
+ if (rawUniq != nullptr) {
+ uniq = ScopedUtfChars(env, rawUniq);
+ } else {
+ uniq = android::base::StringPrintf("Id: %d", id);
+ }
+
+ if (uniq.c_str() == nullptr) {
+ return 0;
+ }
+
std::vector<uint8_t> desc = getData(env, rawDescriptor);
std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback));
std::unique_ptr<uhid::Device> d =
- uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()), vid, pid, bus, desc,
+ uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()),
+ reinterpret_cast<const char*>(uniq.c_str()), vid, pid, bus, desc,
std::move(cb));
return reinterpret_cast<jlong>(d.release());
}
@@ -370,7 +382,7 @@ static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
static JNINativeMethod sMethods[] = {
{"nativeOpenDevice",
- "(Ljava/lang/String;IIII[B"
+ "(Ljava/lang/String;Ljava/lang/String;IIII[B"
"Lcom/android/commands/hid/Device$DeviceCallback;)J",
reinterpret_cast<void*>(openDevice)},
{"nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport)},
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 9c6060d837e0..bc7a9092cc4e 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -42,8 +42,9 @@ private:
class Device {
public:
- static std::unique_ptr<Device> open(int32_t id, const char* name, int32_t vid, int32_t pid,
- uint16_t bus, const std::vector<uint8_t>& descriptor,
+ static std::unique_ptr<Device> open(int32_t id, const char* name, const char* uniq, int32_t vid,
+ int32_t pid, uint16_t bus,
+ const std::vector<uint8_t>& descriptor,
std::unique_ptr<DeviceCallback> callback);
~Device();
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 0415037cfc9f..4e8adc3af55c 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -71,6 +71,7 @@ public class Device {
private static native long nativeOpenDevice(
String name,
+ String uniq,
int id,
int vid,
int pid,
@@ -89,6 +90,7 @@ public class Device {
public Device(
int id,
String name,
+ String uniq,
int vid,
int pid,
int bus,
@@ -113,8 +115,9 @@ public class Device {
} else {
args.arg1 = id + ":" + vid + ":" + pid;
}
- args.arg2 = descriptor;
- args.arg3 = report;
+ args.arg2 = uniq;
+ args.arg3 = descriptor;
+ args.arg4 = report;
mHandler.obtainMessage(MSG_OPEN_DEVICE, args).sendToTarget();
mTimeToSend = SystemClock.uptimeMillis();
}
@@ -167,11 +170,12 @@ public class Device {
mPtr =
nativeOpenDevice(
(String) args.arg1,
+ (String) args.arg2,
args.argi1,
args.argi2,
args.argi3,
args.argi4,
- (byte[]) args.arg2,
+ (byte[]) args.arg3,
new DeviceCallback());
pauseEvents();
break;
diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java
index 3efb79766b94..09ed5281a83d 100644
--- a/cmds/hid/src/com/android/commands/hid/Event.java
+++ b/cmds/hid/src/com/android/commands/hid/Event.java
@@ -56,6 +56,7 @@ public class Event {
private int mId;
private String mCommand;
private String mName;
+ private String mUniq;
private byte[] mDescriptor;
private int mVid;
private int mPid;
@@ -78,6 +79,10 @@ public class Event {
return mName;
}
+ public String getUniq() {
+ return mUniq;
+ }
+
public byte[] getDescriptor() {
return mDescriptor;
}
@@ -118,6 +123,7 @@ public class Event {
return "Event{id=" + mId
+ ", command=" + String.valueOf(mCommand)
+ ", name=" + String.valueOf(mName)
+ + ", uniq=" + String.valueOf(mUniq)
+ ", descriptor=" + Arrays.toString(mDescriptor)
+ ", vid=" + mVid
+ ", pid=" + mPid
@@ -149,6 +155,10 @@ public class Event {
mEvent.mName = name;
}
+ public void setUniq(String uniq) {
+ mEvent.mUniq = uniq;
+ }
+
public void setDescriptor(byte[] descriptor) {
mEvent.mDescriptor = descriptor;
}
@@ -247,6 +257,9 @@ public class Event {
case "name":
eb.setName(mReader.nextString());
break;
+ case "uniq":
+ eb.setUniq(mReader.nextString());
+ break;
case "vid":
eb.setVid(readInt());
break;
diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java
index 2db791fe90bd..5ebfd959ef33 100644
--- a/cmds/hid/src/com/android/commands/hid/Hid.java
+++ b/cmds/hid/src/com/android/commands/hid/Hid.java
@@ -117,8 +117,17 @@ public class Hid {
"Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
}
int id = e.getId();
- Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
- e.getDescriptor(), e.getReport(), e.getFeatureReports(), e.getOutputs());
+ Device d = new Device(
+ id,
+ e.getName(),
+ e.getUniq(),
+ e.getVendorId(),
+ e.getProductId(),
+ e.getBus(),
+ e.getDescriptor(),
+ e.getReport(),
+ e.getFeatureReports(),
+ e.getOutputs());
mDevices.append(id, d);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 53d0c032dbaf..cfdabc88b073 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3930,6 +3930,7 @@ package android.view.inputmethod {
}
public final class InputMethodInfo implements android.os.Parcelable {
+ ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, boolean, @NonNull String);
ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, @NonNull String, boolean, @NonNull String);
ctor @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, @NonNull String, boolean, boolean, @NonNull String);
ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index ab5395e81aa3..9f2e4739e8e6 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -562,6 +562,12 @@ public final class NotificationChannel implements Parcelable {
* audio attributes. Notification channels with an {@link #getImportance() importance} of at
* least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound.
*
+ * Note: An app-specific sound can be provided in the Uri parameter, but because channels are
+ * persistent for the duration of the app install, and are backed up and restored, the Uri
+ * should be stable. For this reason it is not recommended to use a
+ * {@link ContentResolver#SCHEME_ANDROID_RESOURCE} uri, as resource ids can change on app
+ * upgrade.
+ *
* Only modifiable before the channel is submitted to
* {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
*/
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 6b2814ed2146..58aafbc648f7 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -778,6 +778,16 @@ public abstract class DisplayManagerInternal {
*/
float[] getAutoBrightnessLuxLevels(int mode);
+ /**
+ * @return The current brightness setting
+ */
+ float getBrightness();
+
+ /**
+ * @return The brightness value that is used when the device is in doze
+ */
+ float getDozeBrightness();
+
/** Returns whether displayoffload supports the given display state. */
static boolean isSupportedOffloadState(int displayState) {
return Display.isSuspendedState(displayState);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 42f64052d987..69f16572585d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -12360,7 +12360,9 @@ public final class ViewRootImpl implements ViewParent,
// For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction.
// FRAME_RATE_CATEGORY_HIGH is for boosting without user interaction
// (e.g., Window Initialization).
- if (mIsFrameRateBoosting || mInsetsAnimationRunning) {
+ if (mIsFrameRateBoosting || mInsetsAnimationRunning
+ || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE
+ && mPreferredFrameRate > 0)) {
frameRateCategory = FRAME_RATE_CATEGORY_HIGH;
}
@@ -12383,7 +12385,8 @@ public final class ViewRootImpl implements ViewParent,
}
private void setPreferredFrameRate(float preferredFrameRate) {
- if (!shouldSetFrameRate()) {
+ if (!shouldSetFrameRate() || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE
+ && preferredFrameRate > 0)) {
return;
}
@@ -12543,6 +12546,14 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Get the value of mLastPreferredFrameRate
+ */
+ @VisibleForTesting
+ public float getLastPreferredFrameRate() {
+ return mLastPreferredFrameRate;
+ }
+
+ /**
* Returns whether touch boost is currently enabled.
*/
@VisibleForTesting
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 16fecc17d426..8ddc1788a353 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -499,6 +499,25 @@ public final class InputMethodInfo implements Parcelable {
@TestApi
public InputMethodInfo(@NonNull String packageName, @NonNull String className,
@NonNull CharSequence label, @NonNull String settingsActivity,
+ boolean supportStylusHandwriting,
+ @NonNull String stylusHandwritingSettingsActivityAttr) {
+ this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
+ settingsActivity, null /* languageSettingsActivity */,
+ null /* subtypes */, 0 /* isDefaultResId */,
+ false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
+ false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
+ false /* isVirtualDeviceOnly */, 0 /* handledConfigChanges */,
+ supportStylusHandwriting, false /* supportConnectionlessStylusHandwriting */,
+ stylusHandwritingSettingsActivityAttr, false /* inlineSuggestionsEnabled */);
+ }
+
+ /**
+ * Test API for creating a built-in input method to verify stylus handwriting.
+ * @hide
+ */
+ @TestApi
+ public InputMethodInfo(@NonNull String packageName, @NonNull String className,
+ @NonNull CharSequence label, @NonNull String settingsActivity,
@NonNull String languageSettingsActivity, boolean supportStylusHandwriting,
@NonNull String stylusHandwritingSettingsActivityAttr) {
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9d902c94ffe4..a0cb70518866 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1608,7 +1608,10 @@
<fraction name="config_autoBrightnessAdjustmentMaxGamma">300%</fraction>
<!-- If we allow automatic adjustment of screen brightness while dozing, how many times we want
- to reduce it to preserve the battery. Value of 100% means no scaling. -->
+ to reduce it to preserve the battery. Value of 100% means no scaling. Not used if there is
+ a designated auto-brightness doze mapping defined in Display Device Config.
+ Also used to scale the brightness for the doze mode when auto-brightness is disabled if
+ there is an offload session present. -->
<fraction name="config_screenAutoBrightnessDozeScaleFactor">100%</fraction>
<!-- When the screen is turned on, the previous estimate of the ambient light level at the time
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 1a242eff73b1..2544fcb81692 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -19,6 +19,7 @@ package android.view;
import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY;
+import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
@@ -796,6 +797,38 @@ public class ViewRootImplTest {
}
/**
+ * When velocity of a View is not equal to 0, we call setFrameRateCategory with HIGH.
+ * Also, we shouldn't call setFrameRate.
+ */
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_VIEW_VELOCITY_API})
+ public void votePreferredFrameRate_voteFrameRateCategory_velocityToHigh() {
+ View view = new View(sContext);
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+ wmlp.width = 1;
+ wmlp.height = 1;
+
+ sInstrumentation.runOnMainSync(() -> {
+ WindowManager wm = sContext.getSystemService(WindowManager.class);
+ wm.addView(view, wmlp);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+
+ sInstrumentation.runOnMainSync(() -> {
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+ view.setFrameContentVelocity(100);
+ view.invalidate();
+ assertTrue(viewRootImpl.getPreferredFrameRate() > 0);
+ });
+ sInstrumentation.waitForIdleSync();
+ assertEquals(viewRootImpl.getLastPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ assertEquals(viewRootImpl.getLastPreferredFrameRate(), 0 , 0.1);
+ }
+
+ /**
* We should boost the frame rate if the value of mInsetsAnimationRunning is true.
*/
@Test
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index ce2543a47cf5..a621642f99df 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -88,5 +88,6 @@
<permission name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS" />
<permission name="android.permission.READ_SEARCH_INDEXABLES" />
<permission name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT"/>
+ <permission name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS" />
</privapp-permissions>
</permissions>
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 6dc45a6aebec..6a32c5a71999 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -202,11 +202,13 @@ void SpriteController::doUpdateSprites() {
&& update.state.surfaceDrawn;
bool becomingVisible = wantSurfaceVisibleAndDrawn && !update.state.surfaceVisible;
bool becomingHidden = !wantSurfaceVisibleAndDrawn && update.state.surfaceVisible;
- if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
- || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
- | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
- | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID
- | DIRTY_ICON_STYLE))))) {
+ if (update.state.surfaceControl != NULL &&
+ (becomingVisible || becomingHidden ||
+ (wantSurfaceVisibleAndDrawn &&
+ (update.state.dirty &
+ (DIRTY_ALPHA | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER |
+ DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID | DIRTY_ICON_STYLE |
+ DIRTY_DRAW_DROP_SHADOW))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -235,13 +237,15 @@ void SpriteController::doUpdateSprites() {
update.state.transformationMatrix.dtdy);
}
- if (wantSurfaceVisibleAndDrawn
- && (becomingVisible
- || (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) {
+ if (wantSurfaceVisibleAndDrawn &&
+ (becomingVisible ||
+ (update.state.dirty &
+ (DIRTY_HOTSPOT | DIRTY_ICON_STYLE | DIRTY_DRAW_DROP_SHADOW)))) {
Parcel p;
p.writeInt32(static_cast<int32_t>(update.state.icon.style));
p.writeFloat(update.state.icon.hotSpotX);
p.writeFloat(update.state.icon.hotSpotY);
+ p.writeBool(update.state.icon.drawNativeDropShadow);
// Pass cursor metadata in the sprite surface so that when Android is running as a
// client OS (e.g. ARC++) the host OS can get the requested cursor metadata and
@@ -388,12 +392,13 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
uint32_t dirty;
if (icon.isValid()) {
mLocked.state.icon.bitmap = icon.bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888);
- if (!mLocked.state.icon.isValid()
- || mLocked.state.icon.hotSpotX != icon.hotSpotX
- || mLocked.state.icon.hotSpotY != icon.hotSpotY) {
+ if (!mLocked.state.icon.isValid() || mLocked.state.icon.hotSpotX != icon.hotSpotX ||
+ mLocked.state.icon.hotSpotY != icon.hotSpotY ||
+ mLocked.state.icon.drawNativeDropShadow != icon.drawNativeDropShadow) {
mLocked.state.icon.hotSpotX = icon.hotSpotX;
mLocked.state.icon.hotSpotY = icon.hotSpotY;
- dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
+ mLocked.state.icon.drawNativeDropShadow = icon.drawNativeDropShadow;
+ dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_DRAW_DROP_SHADOW;
} else {
dirty = DIRTY_BITMAP;
}
@@ -404,7 +409,7 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
}
} else if (mLocked.state.icon.isValid()) {
mLocked.state.icon.bitmap.reset();
- dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE;
+ dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE | DIRTY_DRAW_DROP_SHADOW;
} else {
return; // setting to invalid icon and already invalid so nothing to do
}
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 04ecb3895aa2..35776e9961b3 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -151,6 +151,7 @@ private:
DIRTY_HOTSPOT = 1 << 6,
DIRTY_DISPLAY_ID = 1 << 7,
DIRTY_ICON_STYLE = 1 << 8,
+ DIRTY_DRAW_DROP_SHADOW = 1 << 9,
};
/* Describes the state of a sprite.
diff --git a/libs/input/SpriteIcon.h b/libs/input/SpriteIcon.h
index 0939af46c258..7d45d02b4a6f 100644
--- a/libs/input/SpriteIcon.h
+++ b/libs/input/SpriteIcon.h
@@ -40,7 +40,7 @@ struct SpriteIcon {
PointerIconStyle style{PointerIconStyle::TYPE_NULL};
float hotSpotX{};
float hotSpotY{};
- bool drawNativeDropShadow{false};
+ bool drawNativeDropShadow{};
inline bool isValid() const { return bitmap.isValid() && !bitmap.isEmpty(); }
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 60ab1a4e42ac..a53a8ce79354 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -275,17 +275,23 @@ public class AudioMix implements Parcelable {
if (o == null || getClass() != o.getClass()) return false;
final AudioMix that = (AudioMix) o;
+ boolean tokenMatch = android.media.audiopolicy.Flags.audioMixOwnership()
+ ? Objects.equals(this.mToken, that.mToken)
+ : true;
return Objects.equals(this.mRouteFlags, that.mRouteFlags)
&& Objects.equals(this.mRule, that.mRule)
&& Objects.equals(this.mMixType, that.mMixType)
&& Objects.equals(this.mFormat, that.mFormat)
- && Objects.equals(this.mToken, that.mToken);
+ && tokenMatch;
}
/** @hide */
@Override
public int hashCode() {
- return Objects.hash(mRouteFlags, mRule, mMixType, mFormat, mToken);
+ if (android.media.audiopolicy.Flags.audioMixOwnership()) {
+ return Objects.hash(mRouteFlags, mRule, mMixType, mFormat, mToken);
+ }
+ return Objects.hash(mRouteFlags, mRule, mMixType, mFormat);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
index ff4d4dd837d2..2b8c2dd0d0e3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
@@ -37,6 +37,23 @@ import java.util.List;
*/
// TODO - b/293578081: Remove once PackageNotAvailableException is propagated to library clients.
/* package */ final class NoOpInfoMediaManager extends InfoMediaManager {
+ /**
+ * Placeholder routing session to return as active session of {@link NoOpInfoMediaManager}.
+ *
+ * <p>Returning this routing session avoids crashes in {@link InfoMediaManager} and maintains
+ * the same client-facing behaviour as if no routing session was found for the target package
+ * name.
+ *
+ * <p>Volume and max volume are set to {@code -1} to emulate a non-existing routing session in
+ * {@link #getSessionVolume()} and {@link #getSessionVolumeMax()}.
+ */
+ private static final RoutingSessionInfo PLACEHOLDER_SESSION =
+ new RoutingSessionInfo.Builder(
+ /* id */ "FAKE_ROUTING_SESSION", /* clientPackageName */ "")
+ .addSelectedRoute(/* routeId */ "FAKE_SELECTED_ROUTE_ID")
+ .setVolumeMax(-1)
+ .setVolume(-1)
+ .build();
NoOpInfoMediaManager(
Context context,
@@ -118,7 +135,7 @@ import java.util.List;
@NonNull
@Override
protected List<RoutingSessionInfo> getRoutingSessionsForPackage() {
- return Collections.emptyList();
+ return List.of(PLACEHOLDER_SESSION);
}
@Nullable
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt b/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt
index a5c63be3c987..7e3f38b755d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt
@@ -18,23 +18,18 @@ package com.android.settingslib.media.data.repository
import android.media.AudioDeviceAttributes
import android.media.Spatializer
-import androidx.concurrent.futures.DirectExecutor
import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
interface SpatializerRepository {
- /** Returns true when head tracking is enabled and false the otherwise. */
- val isHeadTrackingAvailable: StateFlow<Boolean>
+ /**
+ * Returns true when head tracking is available for the [audioDeviceAttributes] and false the
+ * otherwise.
+ */
+ suspend fun isHeadTrackingAvailableForDevice(
+ audioDeviceAttributes: AudioDeviceAttributes
+ ): Boolean
/**
* Returns true when Spatial audio feature is supported for the [audioDeviceAttributes] and
@@ -65,22 +60,14 @@ interface SpatializerRepository {
class SpatializerRepositoryImpl(
private val spatializer: Spatializer,
- coroutineScope: CoroutineScope,
private val backgroundContext: CoroutineContext,
) : SpatializerRepository {
- override val isHeadTrackingAvailable: StateFlow<Boolean> =
- callbackFlow {
- val listener =
- Spatializer.OnHeadTrackerAvailableListener { _, available ->
- launch { send(available) }
- }
- spatializer.addOnHeadTrackerAvailableListener(DirectExecutor.INSTANCE, listener)
- awaitClose { spatializer.removeOnHeadTrackerAvailableListener(listener) }
- }
- .onStart { emit(spatializer.isHeadTrackerAvailable) }
- .flowOn(backgroundContext)
- .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), false)
+ override suspend fun isHeadTrackingAvailableForDevice(
+ audioDeviceAttributes: AudioDeviceAttributes
+ ): Boolean {
+ return withContext(backgroundContext) { spatializer.hasHeadTracker(audioDeviceAttributes) }
+ }
override suspend fun isSpatialAudioAvailableForDevice(
audioDeviceAttributes: AudioDeviceAttributes
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt
index 0347403cb385..5589733b606d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt
@@ -18,17 +18,17 @@ package com.android.settingslib.media.domain.interactor
import android.media.AudioDeviceAttributes
import com.android.settingslib.media.data.repository.SpatializerRepository
-import kotlinx.coroutines.flow.StateFlow
class SpatializerInteractor(private val repository: SpatializerRepository) {
- /** Checks if head tracking is available. */
- val isHeadTrackingAvailable: StateFlow<Boolean>
- get() = repository.isHeadTrackingAvailable
-
+ /** Checks if spatial audio is available. */
suspend fun isSpatialAudioAvailable(audioDeviceAttributes: AudioDeviceAttributes): Boolean =
repository.isSpatialAudioAvailableForDevice(audioDeviceAttributes)
+ /** Checks if head tracking is available. */
+ suspend fun isHeadTrackingAvailable(audioDeviceAttributes: AudioDeviceAttributes): Boolean =
+ repository.isHeadTrackingAvailableForDevice(audioDeviceAttributes)
+
/** Checks if spatial audio is enabled for the [audioDeviceAttributes]. */
suspend fun isSpatialAudioEnabled(audioDeviceAttributes: AudioDeviceAttributes): Boolean =
repository.getSpatialAudioCompatibleDevices().contains(audioDeviceAttributes)
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt
index 794cf832f48b..7719c4b75e9c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt
@@ -48,6 +48,9 @@ class NotificationsSoundPolicyInteractor(
/** Checks if [notificationPolicy] allows media. */
val isMediaAllowed: Flow<Boolean?> = notificationPolicy.map { it?.allowMedia() }
+ /** Checks if [notificationPolicy] allows system sounds. */
+ val isSystemAllowed: Flow<Boolean?> = notificationPolicy.map { it?.allowSystem() }
+
/** Checks if [notificationPolicy] allows ringer. */
val isRingerAllowed: Flow<Boolean?> =
notificationPolicy.map { policy ->
@@ -62,31 +65,29 @@ class NotificationsSoundPolicyInteractor(
areAlarmsAllowed.filterNotNull(),
isMediaAllowed.filterNotNull(),
isRingerAllowed.filterNotNull(),
- ) { zenMode, areAlarmsAllowed, isMediaAllowed, isRingerAllowed ->
- if (zenMode.zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS) {
- return@combine true
- }
-
- val isNotificationOrRing =
- stream.value == AudioManager.STREAM_RING ||
- stream.value == AudioManager.STREAM_NOTIFICATION
- if (isNotificationOrRing && zenMode.zenMode == Settings.Global.ZEN_MODE_ALARMS) {
- return@combine true
+ isSystemAllowed.filterNotNull(),
+ ) { zenMode, areAlarmsAllowed, isMediaAllowed, isRingerAllowed, isSystemAllowed ->
+ when (zenMode.zenMode) {
+ // Everything is muted
+ Settings.Global.ZEN_MODE_NO_INTERRUPTIONS -> return@combine true
+ Settings.Global.ZEN_MODE_ALARMS ->
+ return@combine stream.value == AudioManager.STREAM_RING ||
+ stream.value == AudioManager.STREAM_NOTIFICATION ||
+ stream.value == AudioManager.STREAM_SYSTEM
+ Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS -> {
+ when {
+ stream.value == AudioManager.STREAM_ALARM && !areAlarmsAllowed ->
+ return@combine true
+ stream.value == AudioManager.STREAM_MUSIC && !isMediaAllowed ->
+ return@combine true
+ stream.value == AudioManager.STREAM_SYSTEM && !isSystemAllowed ->
+ return@combine true
+ (stream.value == AudioManager.STREAM_RING ||
+ stream.value == AudioManager.STREAM_NOTIFICATION) && !isRingerAllowed ->
+ return@combine true
+ }
+ }
}
- if (zenMode.zenMode != Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
- return@combine false
- }
-
- if (stream.value == AudioManager.STREAM_ALARM && !areAlarmsAllowed) {
- return@combine true
- }
- if (stream.value == AudioManager.STREAM_MUSIC && !isMediaAllowed) {
- return@combine true
- }
- if (isNotificationOrRing && !isRingerAllowed) {
- return@combine true
- }
-
return@combine false
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/NoOpInfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/NoOpInfoMediaManagerTest.java
new file mode 100644
index 000000000000..d630301a083b
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/NoOpInfoMediaManagerTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.media;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/**
+ * Tests for {@link NoOpInfoMediaManager} to avoid exceptions in {@link InfoMediaManager}.
+ *
+ * <p>While {@link NoOpInfoMediaManager} should not perform any actions, it should still return
+ * placeholder information in certain cases to not change the behaviour of {@link InfoMediaManager}
+ * and prevent crashes.
+ */
+@RunWith(RobolectricTestRunner.class)
+public class NoOpInfoMediaManagerTest {
+ private InfoMediaManager mInfoMediaManager;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mInfoMediaManager =
+ new NoOpInfoMediaManager(
+ mContext,
+ /* packageName */ "FAKE_PACKAGE_NAME",
+ /* localBluetoothManager */ null);
+ }
+
+ @Test
+ public void getSessionVolumeMax_returnsNotFound() {
+ assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1);
+ }
+
+ @Test
+ public void getSessionVolume_returnsNotFound() {
+ assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
+ }
+
+ @Test
+ public void getSessionName_returnsNull() {
+ assertThat(mInfoMediaManager.getSessionName()).isNull();
+ }
+
+ @Test
+ public void getRoutingSessionForPackage_returnsPlaceholderSession() {
+ // Make sure we return a placeholder routing session so that we avoid OOB exceptions.
+ assertThat(mInfoMediaManager.getRoutingSessionsForPackage()).hasSize(1);
+ }
+
+ @Test
+ public void getSelectedMediaDevices_returnsEmptyList() {
+ assertThat(mInfoMediaManager.getSelectedMediaDevices()).isEmpty();
+ }
+}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 12e8f574e906..98591e9b76e9 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -273,6 +273,9 @@
<!-- to control accessibility volume -->
<uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" />
+ <!-- to change spatial audio -->
+ <uses-permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS" />
+
<!-- to access ResolverRankerServices -->
<uses-permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE" />
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
new file mode 100644
index 000000000000..a066b387d906
--- /dev/null
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.spatialaudio
+
+import dagger.Module
+
+@Module interface SpatialAudioModule
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
index 0728daf28c25..2a99039fa306 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
@@ -26,6 +26,7 @@ import androidx.compose.ui.layout.onPlaced
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.BurnInScaleViewModel
+import kotlinx.coroutines.flow.map
/**
* Modifies the composable to account for anti-burn in translation, alpha, and scaling.
@@ -38,9 +39,18 @@ fun Modifier.burnInAware(
params: BurnInParameters,
isClock: Boolean = false,
): Modifier {
- val translationX by viewModel.translationX(params).collectAsState(initial = 0f)
- val translationY by viewModel.translationY(params).collectAsState(initial = 0f)
- val scaleViewModel by viewModel.scale(params).collectAsState(initial = BurnInScaleViewModel())
+ val burnIn = viewModel.movement(params)
+ val translationX by burnIn.map { it.translationX.toFloat() }.collectAsState(initial = 0f)
+ val translationY by burnIn.map { it.translationY.toFloat() }.collectAsState(initial = 0f)
+ val scaleViewModel by
+ burnIn
+ .map {
+ BurnInScaleViewModel(
+ scale = it.scale,
+ scaleClockOnly = it.scaleClockOnly,
+ )
+ }
+ .collectAsState(initial = BurnInScaleViewModel())
return this.graphicsLayer {
val scale =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt
new file mode 100644
index 000000000000..ae267e2b002a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/selector/ui/composable/VolumePanelRadioButtons.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.selector.ui.composable
+
+import androidx.compose.animation.core.animateOffsetAsState
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
+
+/**
+ * Radio button group for the Volume Panel. It allows selecting a single item
+ *
+ * @param indicatorBackgroundPadding is the distance between the edge of the indicator and the
+ * indicator background
+ * @param labelIndicatorBackgroundSpacing is the distance between indicator background and labels
+ * row
+ */
+@Composable
+fun VolumePanelRadioButtonBar(
+ modifier: Modifier = Modifier,
+ indicatorBackgroundPadding: Dp =
+ VolumePanelRadioButtonBarDefaults.DefaultIndicatorBackgroundPadding,
+ spacing: Dp = VolumePanelRadioButtonBarDefaults.DefaultSpacing,
+ labelIndicatorBackgroundSpacing: Dp =
+ VolumePanelRadioButtonBarDefaults.DefaultLabelIndicatorBackgroundSpacing,
+ indicatorCornerRadius: CornerRadius =
+ VolumePanelRadioButtonBarDefaults.defaultIndicatorCornerRadius(),
+ indicatorBackgroundCornerSize: CornerSize =
+ CornerSize(VolumePanelRadioButtonBarDefaults.DefaultIndicatorBackgroundCornerRadius),
+ colors: VolumePanelRadioButtonBarColors = VolumePanelRadioButtonBarDefaults.defaultColors(),
+ content: VolumePanelRadioButtonBarScope.() -> Unit
+) {
+ val scope =
+ VolumePanelRadioButtonBarScopeImpl().apply(content).apply {
+ require(hasSelectedItem) { "At least one item should be selected" }
+ }
+
+ val items = scope.items
+
+ var selectedIndex by remember { mutableIntStateOf(items.indexOfFirst { it.isSelected }) }
+
+ var size by remember { mutableStateOf(IntSize(0, 0)) }
+ val spacingPx = with(LocalDensity.current) { spacing.toPx() }
+ val indicatorWidth = size.width / items.size - (spacingPx * (items.size - 1) / items.size)
+ val offset by
+ animateOffsetAsState(
+ targetValue =
+ Offset(
+ selectedIndex * indicatorWidth + (spacingPx * selectedIndex),
+ 0f,
+ ),
+ label = "VolumePanelRadioButtonOffsetAnimation",
+ finishedListener = {
+ for (itemIndex in items.indices) {
+ val item = items[itemIndex]
+ if (itemIndex == selectedIndex) {
+ item.onItemSelected()
+ break
+ }
+ }
+ }
+ )
+
+ Column(modifier = modifier) {
+ Box(modifier = Modifier.height(IntrinsicSize.Max)) {
+ Canvas(
+ modifier =
+ Modifier.fillMaxSize()
+ .background(
+ colors.indicatorBackgroundColor,
+ RoundedCornerShape(indicatorBackgroundCornerSize),
+ )
+ .padding(indicatorBackgroundPadding)
+ .onGloballyPositioned { size = it.size }
+ ) {
+ drawRoundRect(
+ color = colors.indicatorColor,
+ topLeft = offset,
+ size = Size(indicatorWidth, size.height.toFloat()),
+ cornerRadius = indicatorCornerRadius,
+ )
+ }
+ Row(
+ modifier = Modifier.padding(indicatorBackgroundPadding),
+ horizontalArrangement = Arrangement.spacedBy(spacing)
+ ) {
+ for (itemIndex in items.indices) {
+ TextButton(
+ modifier = Modifier.weight(1f),
+ onClick = { selectedIndex = itemIndex },
+ ) {
+ val item = items[itemIndex]
+ if (item.icon !== Empty) {
+ with(items[itemIndex]) { icon() }
+ }
+ }
+ }
+ }
+ }
+
+ Row(
+ modifier =
+ Modifier.padding(
+ start = indicatorBackgroundPadding,
+ top = labelIndicatorBackgroundSpacing,
+ end = indicatorBackgroundPadding
+ ),
+ horizontalArrangement = Arrangement.spacedBy(spacing),
+ ) {
+ for (itemIndex in items.indices) {
+ TextButton(
+ modifier = Modifier.weight(1f),
+ onClick = { selectedIndex = itemIndex },
+ ) {
+ val item = items[itemIndex]
+ if (item.icon !== Empty) {
+ with(items[itemIndex]) { label() }
+ }
+ }
+ }
+ }
+ }
+}
+
+data class VolumePanelRadioButtonBarColors(
+ /** Color of the indicator. */
+ val indicatorColor: Color,
+ /** Color of the indicator background. */
+ val indicatorBackgroundColor: Color,
+)
+
+object VolumePanelRadioButtonBarDefaults {
+
+ val DefaultIndicatorBackgroundPadding = 8.dp
+ val DefaultSpacing = 24.dp
+ val DefaultLabelIndicatorBackgroundSpacing = 12.dp
+ val DefaultIndicatorCornerRadius = 20.dp
+ val DefaultIndicatorBackgroundCornerRadius = 20.dp
+
+ @Composable
+ fun defaultIndicatorCornerRadius(
+ x: Dp = DefaultIndicatorCornerRadius,
+ y: Dp = DefaultIndicatorCornerRadius,
+ ): CornerRadius = with(LocalDensity.current) { CornerRadius(x.toPx(), y.toPx()) }
+
+ /**
+ * Returns the default VolumePanelRadioButtonBar colors.
+ *
+ * @param indicatorColor is the color of the indicator
+ * @param indicatorBackgroundColor is the color of the indicator background
+ */
+ @Composable
+ fun defaultColors(
+ indicatorColor: Color = MaterialTheme.colorScheme.primaryContainer,
+ indicatorBackgroundColor: Color = MaterialTheme.colorScheme.surface,
+ ): VolumePanelRadioButtonBarColors =
+ VolumePanelRadioButtonBarColors(
+ indicatorColor = indicatorColor,
+ indicatorBackgroundColor = indicatorBackgroundColor,
+ )
+}
+
+/** [VolumePanelRadioButtonBar] content scope. Use [item] to add more items. */
+interface VolumePanelRadioButtonBarScope {
+
+ /**
+ * Adds a single item to the radio button group.
+ *
+ * @param isSelected true when the item is selected and false the otherwise
+ * @param onItemSelected is called when the item is selected
+ * @param icon of the to show in the indicator bar
+ * @param label to show below the indicator bar for the corresponding [icon]
+ */
+ fun item(
+ isSelected: Boolean,
+ onItemSelected: () -> Unit,
+ icon: @Composable RowScope.() -> Unit = Empty,
+ label: @Composable RowScope.() -> Unit = Empty,
+ )
+}
+
+private val Empty: @Composable RowScope.() -> Unit = {}
+
+private class VolumePanelRadioButtonBarScopeImpl : VolumePanelRadioButtonBarScope {
+
+ var hasSelectedItem: Boolean = false
+ private set
+
+ private val mutableItems: MutableList<Item> = mutableListOf()
+ val items: List<Item> = mutableItems
+
+ override fun item(
+ isSelected: Boolean,
+ onItemSelected: () -> Unit,
+ icon: @Composable RowScope.() -> Unit,
+ label: @Composable RowScope.() -> Unit,
+ ) {
+ require(!isSelected || !hasSelectedItem) { "Only one item should be selected at a time" }
+ hasSelectedItem = hasSelectedItem || isSelected
+ mutableItems.add(
+ Item(
+ isSelected = isSelected,
+ onItemSelected = onItemSelected,
+ icon = icon,
+ label = label,
+ )
+ )
+ }
+}
+
+private class Item(
+ val isSelected: Boolean,
+ val onItemSelected: () -> Unit,
+ val icon: @Composable RowScope.() -> Unit,
+ val label: @Composable RowScope.() -> Unit,
+)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
new file mode 100644
index 000000000000..da29d5810974
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.spatialaudio
+
+import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
+import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
+import com.android.systemui.volume.panel.component.spatial.domain.SpatialAudioAvailabilityCriteria
+import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.SpatialAudioViewModel
+import com.android.systemui.volume.panel.component.spatialaudio.ui.composable.SpatialAudioPopup
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+/** Dagger module, that provides Spatial Audio Volume Panel UI functionality. */
+@Module
+interface SpatialAudioModule {
+
+ @Binds
+ @IntoMap
+ @StringKey(VolumePanelComponents.SPATIAL_AUDIO)
+ fun bindComponentAvailabilityCriteria(
+ criteria: SpatialAudioAvailabilityCriteria
+ ): ComponentAvailabilityCriteria
+
+ companion object {
+
+ @Provides
+ @IntoMap
+ @StringKey(VolumePanelComponents.SPATIAL_AUDIO)
+ fun provideVolumePanelUiComponent(
+ viewModel: SpatialAudioViewModel,
+ popup: SpatialAudioPopup,
+ ): VolumePanelUiComponent = ButtonComponent(viewModel.spatialAudioButton, popup::show)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
new file mode 100644
index 000000000000..bed0ae80e377
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.spatialaudio.ui.composable
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.common.ui.compose.toColor
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup
+import com.android.systemui.volume.panel.component.selector.ui.composable.VolumePanelRadioButtonBar
+import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.SpatialAudioViewModel
+import javax.inject.Inject
+
+class SpatialAudioPopup
+@Inject
+constructor(
+ private val viewModel: SpatialAudioViewModel,
+ private val volumePanelPopup: VolumePanelPopup,
+) {
+
+ /** Shows a popup with the [expandable] animation. */
+ fun show(expandable: Expandable) {
+ volumePanelPopup.show(expandable, { Title() }, { Content(it) })
+ }
+
+ @Composable
+ private fun Title() {
+ Text(
+ text = stringResource(R.string.volume_panel_spatial_audio_title),
+ style = MaterialTheme.typography.titleMedium,
+ textAlign = TextAlign.Center,
+ maxLines = 1,
+ )
+ }
+
+ @Composable
+ private fun Content(dialog: SystemUIDialog) {
+ val isAvailable by viewModel.isAvailable.collectAsState()
+
+ if (!isAvailable) {
+ SideEffect { dialog.dismiss() }
+ return
+ }
+
+ val enabledModelStates by viewModel.spatialAudioButtonByEnabled.collectAsState()
+ if (enabledModelStates.isEmpty()) {
+ return
+ }
+ VolumePanelRadioButtonBar {
+ for (buttonViewModel in enabledModelStates) {
+ item(
+ isSelected = buttonViewModel.button.isChecked,
+ onItemSelected = { viewModel.setEnabled(buttonViewModel.model) },
+ icon = {
+ Icon(
+ icon = buttonViewModel.button.icon,
+ tint = buttonViewModel.iconColor.toColor(),
+ )
+ },
+ label = {
+ Text(
+ text = buttonViewModel.button.label.toString(),
+ style = MaterialTheme.typography.labelMedium,
+ color = buttonViewModel.labelColor.toColor(),
+ )
+ }
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt
index 0a3aea767733..723f6a2bfff4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt
@@ -27,6 +27,8 @@ import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.wakelock.WakeLockFake
+import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -44,6 +46,9 @@ class HomeControlsDreamServiceTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
+ private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
+ private lateinit var fakeWakeLock: WakeLockFake
+
@Mock private lateinit var taskFragmentComponentFactory: TaskFragmentComponent.Factory
@Mock private lateinit var taskFragmentComponent: TaskFragmentComponent
@Mock private lateinit var activity: Activity
@@ -57,6 +62,10 @@ class HomeControlsDreamServiceTest : SysuiTestCase() {
whenever(taskFragmentComponentFactory.create(any(), any(), any(), any()))
.thenReturn(taskFragmentComponent)
+ fakeWakeLock = WakeLockFake()
+ fakeWakeLockBuilder = WakeLockFake.Builder(context)
+ fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
+
whenever(controlsComponent.getControlsListingController())
.thenReturn(Optional.of(controlsListingController))
@@ -87,12 +96,29 @@ class HomeControlsDreamServiceTest : SysuiTestCase() {
verify(taskFragmentComponentFactory, never()).create(any(), any(), any(), any())
}
+ @Test
+ fun testAttachWindow_wakeLockAcquired() =
+ testScope.runTest {
+ underTest.onAttachedToWindow()
+ assertThat(fakeWakeLock.isHeld).isTrue()
+ }
+ @Test
+ fun testDetachWindow_wakeLockCanBeReleased() =
+ testScope.runTest {
+ underTest.onAttachedToWindow()
+ assertThat(fakeWakeLock.isHeld).isTrue()
+
+ underTest.onDetachedFromWindow()
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
private fun buildService(activityProvider: DreamActivityProvider): HomeControlsDreamService =
with(kosmos) {
return HomeControlsDreamService(
controlsSettingsRepository = FakeControlsSettingsRepository(),
taskFragmentFactory = taskFragmentComponentFactory,
homeControlsComponentInteractor = homeControlsComponentInteractor,
+ fakeWakeLockBuilder,
dreamActivityProvider = activityProvider,
bgDispatcher = testDispatcher,
logBuffer = logcatLogBuffer("HomeControlsDreamServiceTest")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 128b46533a8b..19b80da62dc7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -25,7 +25,6 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
-import com.android.systemui.common.shared.model.Position
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
@@ -152,24 +151,6 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
}
@Test
- fun clockPosition() =
- testScope.runTest {
- assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 0))
-
- underTest.setClockPosition(0, 1)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 1))
-
- underTest.setClockPosition(1, 9)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 9))
-
- underTest.setClockPosition(1, 0)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 0))
-
- underTest.setClockPosition(3, 1)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1))
- }
-
- @Test
fun dozeTimeTick() =
testScope.runTest {
val lastDozeTimeTick by collectLastValue(underTest.dozeTimeTick)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index b0f59fe68f11..de659cf17c05 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -71,7 +71,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
MockitoAnnotations.initMocks(this)
- whenever(burnInInteractor.keyguardBurnIn).thenReturn(burnInFlow)
+ whenever(burnInInteractor.burnIn(anyInt(), anyInt())).thenReturn(burnInFlow)
kosmos.burnInInteractor = burnInInteractor
whenever(goneToAodTransitionViewModel.enterFromTopTranslationY(anyInt()))
.thenReturn(emptyFlow())
@@ -81,18 +81,18 @@ class AodBurnInViewModelTest : SysuiTestCase() {
}
@Test
- fun translationY_initializedToZero() =
+ fun movement_initializedToZero() =
testScope.runTest {
- val translationY by collectLastValue(underTest.translationY(burnInParameters))
- assertThat(translationY).isEqualTo(0)
+ val movement by collectLastValue(underTest.movement(burnInParameters))
+ assertThat(movement?.translationY).isEqualTo(0)
+ assertThat(movement?.translationX).isEqualTo(0)
+ assertThat(movement?.scale).isEqualTo(0f)
}
@Test
fun translationAndScale_whenNotDozing() =
testScope.runTest {
- val translationX by collectLastValue(underTest.translationX(burnInParameters))
- val translationY by collectLastValue(underTest.translationY(burnInParameters))
- val scale by collectLastValue(underTest.scale(burnInParameters))
+ val movement by collectLastValue(underTest.movement(burnInParameters))
// Set to not dozing (on lockscreen)
keyguardTransitionRepository.sendTransitionStep(
@@ -113,24 +113,17 @@ class AodBurnInViewModelTest : SysuiTestCase() {
scale = 0.5f,
)
- assertThat(translationX).isEqualTo(0)
- assertThat(translationY).isEqualTo(0)
- assertThat(scale)
- .isEqualTo(
- BurnInScaleViewModel(
- scale = 1f,
- scaleClockOnly = true,
- )
- )
+ assertThat(movement?.translationX).isEqualTo(0)
+ assertThat(movement?.translationY).isEqualTo(0)
+ assertThat(movement?.scale).isEqualTo(1f)
+ assertThat(movement?.scaleClockOnly).isEqualTo(true)
}
@Test
fun translationAndScale_whenFullyDozing() =
testScope.runTest {
burnInParameters = burnInParameters.copy(minViewY = 100)
- val translationX by collectLastValue(underTest.translationX(burnInParameters))
- val translationY by collectLastValue(underTest.translationY(burnInParameters))
- val scale by collectLastValue(underTest.scale(burnInParameters))
+ val movement by collectLastValue(underTest.movement(burnInParameters))
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -150,15 +143,10 @@ class AodBurnInViewModelTest : SysuiTestCase() {
scale = 0.5f,
)
- assertThat(translationX).isEqualTo(20)
- assertThat(translationY).isEqualTo(30)
- assertThat(scale)
- .isEqualTo(
- BurnInScaleViewModel(
- scale = 0.5f,
- scaleClockOnly = true,
- )
- )
+ assertThat(movement?.translationX).isEqualTo(20)
+ assertThat(movement?.translationY).isEqualTo(30)
+ assertThat(movement?.scale).isEqualTo(0.5f)
+ assertThat(movement?.scaleClockOnly).isEqualTo(true)
// Set to the beginning of GONE->AOD transition
keyguardTransitionRepository.sendTransitionStep(
@@ -170,15 +158,10 @@ class AodBurnInViewModelTest : SysuiTestCase() {
),
validateStep = false,
)
- assertThat(translationX).isEqualTo(0)
- assertThat(translationY).isEqualTo(0)
- assertThat(scale)
- .isEqualTo(
- BurnInScaleViewModel(
- scale = 1f,
- scaleClockOnly = true,
- )
- )
+ assertThat(movement?.translationX).isEqualTo(0)
+ assertThat(movement?.translationY).isEqualTo(0)
+ assertThat(movement?.scale).isEqualTo(1f)
+ assertThat(movement?.scaleClockOnly).isEqualTo(true)
}
@Test
@@ -191,9 +174,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
minViewY = 100,
topInset = 80,
)
- val translationX by collectLastValue(underTest.translationX(burnInParameters))
- val translationY by collectLastValue(underTest.translationY(burnInParameters))
- val scale by collectLastValue(underTest.scale(burnInParameters))
+ val movement by collectLastValue(underTest.movement(burnInParameters))
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -213,16 +194,11 @@ class AodBurnInViewModelTest : SysuiTestCase() {
translationY = -30,
scale = 0.5f,
)
- assertThat(translationX).isEqualTo(20)
+ assertThat(movement?.translationX).isEqualTo(20)
// -20 instead of -30, due to inset of 80
- assertThat(translationY).isEqualTo(-20)
- assertThat(scale)
- .isEqualTo(
- BurnInScaleViewModel(
- scale = 0.5f,
- scaleClockOnly = true,
- )
- )
+ assertThat(movement?.translationY).isEqualTo(-20)
+ assertThat(movement?.scale).isEqualTo(0.5f)
+ assertThat(movement?.scaleClockOnly).isEqualTo(true)
// Set to the beginning of GONE->AOD transition
keyguardTransitionRepository.sendTransitionStep(
@@ -234,15 +210,10 @@ class AodBurnInViewModelTest : SysuiTestCase() {
),
validateStep = false,
)
- assertThat(translationX).isEqualTo(0)
- assertThat(translationY).isEqualTo(0)
- assertThat(scale)
- .isEqualTo(
- BurnInScaleViewModel(
- scale = 1f,
- scaleClockOnly = true,
- )
- )
+ assertThat(movement?.translationX).isEqualTo(0)
+ assertThat(movement?.translationY).isEqualTo(0)
+ assertThat(movement?.scale).isEqualTo(1f)
+ assertThat(movement?.scaleClockOnly).isEqualTo(true)
}
@Test
@@ -255,9 +226,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
minViewY = 100,
topInset = 80,
)
- val translationX by collectLastValue(underTest.translationX(burnInParameters))
- val translationY by collectLastValue(underTest.translationY(burnInParameters))
- val scale by collectLastValue(underTest.scale(burnInParameters))
+ val movement by collectLastValue(underTest.movement(burnInParameters))
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -277,16 +246,11 @@ class AodBurnInViewModelTest : SysuiTestCase() {
translationY = -30,
scale = 0.5f,
)
- assertThat(translationX).isEqualTo(20)
+ assertThat(movement?.translationX).isEqualTo(20)
// -20 instead of -30, due to inset of 80
- assertThat(translationY).isEqualTo(-20)
- assertThat(scale)
- .isEqualTo(
- BurnInScaleViewModel(
- scale = 0.5f,
- scaleClockOnly = true,
- )
- )
+ assertThat(movement?.translationY).isEqualTo(-20)
+ assertThat(movement?.scale).isEqualTo(0.5f)
+ assertThat(movement?.scaleClockOnly).isEqualTo(true)
// Set to the beginning of GONE->AOD transition
keyguardTransitionRepository.sendTransitionStep(
@@ -298,15 +262,10 @@ class AodBurnInViewModelTest : SysuiTestCase() {
),
validateStep = false,
)
- assertThat(translationX).isEqualTo(0)
- assertThat(translationY).isEqualTo(0)
- assertThat(scale)
- .isEqualTo(
- BurnInScaleViewModel(
- scale = 1f,
- scaleClockOnly = true,
- )
- )
+ assertThat(movement?.translationX).isEqualTo(0)
+ assertThat(movement?.translationY).isEqualTo(0)
+ assertThat(movement?.scale).isEqualTo(1f)
+ assertThat(movement?.scaleClockOnly).isEqualTo(true)
}
@Test
@@ -314,9 +273,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
testScope.runTest {
whenever(clockController.config.useAlternateSmartspaceAODTransition).thenReturn(true)
- val translationX by collectLastValue(underTest.translationX(burnInParameters))
- val translationY by collectLastValue(underTest.translationY(burnInParameters))
- val scale by collectLastValue(underTest.scale(burnInParameters))
+ val movement by collectLastValue(underTest.movement(burnInParameters))
// Set to dozing (on AOD)
keyguardTransitionRepository.sendTransitionStep(
@@ -337,8 +294,9 @@ class AodBurnInViewModelTest : SysuiTestCase() {
scale = 0.5f,
)
- assertThat(translationX).isEqualTo(0)
- assertThat(translationY).isEqualTo(0)
- assertThat(scale).isEqualTo(BurnInScaleViewModel(scale = 0.5f, scaleClockOnly = false))
+ assertThat(movement?.translationX).isEqualTo(0)
+ assertThat(movement?.translationY).isEqualTo(0)
+ assertThat(movement?.scale).isEqualTo(0.5f)
+ assertThat(movement?.scaleClockOnly).isEqualTo(false)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
index 864acfb1ddb9..04c270d07b0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
@@ -24,9 +25,13 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.util.BurnInHelperWrapper
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -36,18 +41,23 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class KeyguardIndicationAreaViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
@Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
@Mock private lateinit var shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel
+ @Mock private lateinit var burnInInteractor: BurnInInteractor
+ private val burnInFlow = MutableStateFlow(BurnInModel())
+
+ private lateinit var bottomAreaInteractor: KeyguardBottomAreaInteractor
private lateinit var underTest: KeyguardIndicationAreaViewModel
private lateinit var repository: FakeKeyguardRepository
@@ -70,9 +80,11 @@ class KeyguardIndicationAreaViewModelTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
whenever(burnInHelperWrapper.burnInOffset(anyInt(), any()))
.thenReturn(RETURNED_BURN_IN_OFFSET)
+ whenever(burnInInteractor.burnIn(anyInt(), anyInt())).thenReturn(burnInFlow)
val withDeps = KeyguardInteractorFactory.create()
val keyguardInteractor = withDeps.keyguardInteractor
@@ -82,78 +94,85 @@ class KeyguardIndicationAreaViewModelTest : SysuiTestCase() {
whenever(bottomAreaViewModel.startButton).thenReturn(startButtonFlow)
whenever(bottomAreaViewModel.endButton).thenReturn(endButtonFlow)
whenever(bottomAreaViewModel.alpha).thenReturn(alphaFlow)
+ bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository)
underTest =
KeyguardIndicationAreaViewModel(
keyguardInteractor = keyguardInteractor,
- bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
+ bottomAreaInteractor = bottomAreaInteractor,
keyguardBottomAreaViewModel = bottomAreaViewModel,
burnInHelperWrapper = burnInHelperWrapper,
+ burnInInteractor = burnInInteractor,
shortcutsCombinedViewModel = shortcutsCombinedViewModel,
configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()),
)
}
@Test
- fun alpha() = runTest {
- val value = collectLastValue(underTest.alpha)
-
- assertThat(value()).isEqualTo(1f)
- alphaFlow.value = 0.1f
- assertThat(value()).isEqualTo(0.1f)
- alphaFlow.value = 0.5f
- assertThat(value()).isEqualTo(0.5f)
- alphaFlow.value = 0.2f
- assertThat(value()).isEqualTo(0.2f)
- alphaFlow.value = 0f
- assertThat(value()).isEqualTo(0f)
- }
+ fun alpha() =
+ testScope.runTest {
+ val value = collectLastValue(underTest.alpha)
+
+ assertThat(value()).isEqualTo(1f)
+ alphaFlow.value = 0.1f
+ assertThat(value()).isEqualTo(0.1f)
+ alphaFlow.value = 0.5f
+ assertThat(value()).isEqualTo(0.5f)
+ alphaFlow.value = 0.2f
+ assertThat(value()).isEqualTo(0.2f)
+ alphaFlow.value = 0f
+ assertThat(value()).isEqualTo(0f)
+ }
@Test
- fun isIndicationAreaPadded() = runTest {
- repository.setKeyguardShowing(true)
- val value = collectLastValue(underTest.isIndicationAreaPadded)
-
- assertThat(value()).isFalse()
- startButtonFlow.value = startButtonFlow.value.copy(isVisible = true)
- assertThat(value()).isTrue()
- endButtonFlow.value = endButtonFlow.value.copy(isVisible = true)
- assertThat(value()).isTrue()
- startButtonFlow.value = startButtonFlow.value.copy(isVisible = false)
- assertThat(value()).isTrue()
- endButtonFlow.value = endButtonFlow.value.copy(isVisible = false)
- assertThat(value()).isFalse()
- }
+ fun isIndicationAreaPadded() =
+ testScope.runTest {
+ repository.setKeyguardShowing(true)
+ val value = collectLastValue(underTest.isIndicationAreaPadded)
+
+ assertThat(value()).isFalse()
+ startButtonFlow.value = startButtonFlow.value.copy(isVisible = true)
+ assertThat(value()).isTrue()
+ endButtonFlow.value = endButtonFlow.value.copy(isVisible = true)
+ assertThat(value()).isTrue()
+ startButtonFlow.value = startButtonFlow.value.copy(isVisible = false)
+ assertThat(value()).isTrue()
+ endButtonFlow.value = endButtonFlow.value.copy(isVisible = false)
+ assertThat(value()).isFalse()
+ }
@Test
- fun indicationAreaTranslationX() = runTest {
- val value = collectLastValue(underTest.indicationAreaTranslationX)
-
- assertThat(value()).isEqualTo(0f)
- repository.setClockPosition(100, 100)
- assertThat(value()).isEqualTo(100f)
- repository.setClockPosition(200, 100)
- assertThat(value()).isEqualTo(200f)
- repository.setClockPosition(200, 200)
- assertThat(value()).isEqualTo(200f)
- repository.setClockPosition(300, 100)
- assertThat(value()).isEqualTo(300f)
- }
+ fun indicationAreaTranslationX() =
+ testScope.runTest {
+ val value = collectLastValue(underTest.indicationAreaTranslationX)
+
+ assertThat(value()).isEqualTo(0f)
+ bottomAreaInteractor.setClockPosition(100, 100)
+ assertThat(value()).isEqualTo(100f)
+ bottomAreaInteractor.setClockPosition(200, 100)
+ assertThat(value()).isEqualTo(200f)
+ bottomAreaInteractor.setClockPosition(200, 200)
+ assertThat(value()).isEqualTo(200f)
+ bottomAreaInteractor.setClockPosition(300, 100)
+ assertThat(value()).isEqualTo(300f)
+ }
@Test
- fun indicationAreaTranslationY() = runTest {
- val value = collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET))
-
- // Negative 0 - apparently there's a difference in floating point arithmetic - FML
- assertThat(value()).isEqualTo(-0f)
- val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f)
- assertThat(value()).isEqualTo(expected1)
- val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f)
- assertThat(value()).isEqualTo(expected2)
- val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f)
- assertThat(value()).isEqualTo(expected3)
- val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f)
- assertThat(value()).isEqualTo(expected4)
- }
+ fun indicationAreaTranslationY() =
+ testScope.runTest {
+ val value =
+ collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET))
+
+ // Negative 0 - apparently there's a difference in floating point arithmetic - FML
+ assertThat(value()).isEqualTo(-0f)
+ val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f)
+ assertThat(value()).isEqualTo(expected1)
+ val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f)
+ assertThat(value()).isEqualTo(expected2)
+ val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f)
+ assertThat(value()).isEqualTo(expected3)
+ val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f)
+ assertThat(value()).isEqualTo(expected4)
+ }
private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
repository.setDozeAmount(dozeAmount)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt
index e188f5bfc1c8..8e765f7bb689 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt
@@ -196,10 +196,14 @@ class NotificationsSoundPolicyInteractorTest : SysuiTestCase() {
}
@Test
- fun zenModeAlarms_ringAndNotifications_muted() {
+ fun zenModeAlarms_ringedStreams_muted() {
with(kosmos) {
val expectedToBeMuted =
- setOf(AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION)
+ setOf(
+ AudioManager.STREAM_RING,
+ AudioManager.STREAM_NOTIFICATION,
+ AudioManager.STREAM_SYSTEM,
+ )
testScope.runTest {
notificationsSoundPolicyRepository.updateNotificationPolicy()
notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_ALARMS))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 0de15b8db665..deb19769372c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -34,6 +34,7 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic
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.BurnInModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -66,7 +67,7 @@ import org.mockito.Mockito.mock
@RunWith(AndroidJUnit4::class)
class SharedNotificationContainerViewModelTest : SysuiTestCase() {
val aodBurnInViewModel = mock(AodBurnInViewModel::class.java)
- lateinit var translationYFlow: MutableStateFlow<Float>
+ lateinit var movementFlow: MutableStateFlow<BurnInModel>
val kosmos =
testKosmos().apply {
@@ -95,8 +96,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
overrideResource(R.bool.config_use_split_notification_shade, false)
- translationYFlow = MutableStateFlow(0f)
- whenever(aodBurnInViewModel.translationY(any())).thenReturn(translationYFlow)
+ movementFlow = MutableStateFlow(BurnInModel())
+ whenever(aodBurnInViewModel.movement(any())).thenReturn(movementFlow)
underTest = kosmos.sharedNotificationContainerViewModel
}
@@ -608,7 +609,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
showLockscreen()
assertThat(translationY).isEqualTo(0)
- translationYFlow.value = 150f
+ movementFlow.value = BurnInModel(translationY = 150)
assertThat(translationY).isEqualTo(150f)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
index 36be90ecbf7e..449e8bf6998f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
@@ -78,7 +78,7 @@ class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice)
- spatializerRepository.setIsHeadTrackingAvailable(false)
+ spatializerRepository.defaultHeadTrackingAvailable = false
spatializerRepository.defaultSpatialAudioAvailable = false
val isAvailable by collectLastValue(underTest.isAvailable())
@@ -94,7 +94,7 @@ class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice)
- spatializerRepository.setIsHeadTrackingAvailable(false)
+ spatializerRepository.defaultHeadTrackingAvailable = false
spatializerRepository.defaultSpatialAudioAvailable = true
val isAvailable by collectLastValue(underTest.isAvailable())
@@ -110,7 +110,7 @@ class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
with(kosmos) {
testScope.runTest {
localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice)
- spatializerRepository.setIsHeadTrackingAvailable(true)
+ spatializerRepository.defaultHeadTrackingAvailable = true
spatializerRepository.defaultSpatialAudioAvailable = true
val isAvailable by collectLastValue(underTest.isAvailable())
@@ -125,7 +125,7 @@ class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
fun spatialAudio_headTracking_noDevice_unavailable() {
with(kosmos) {
testScope.runTest {
- spatializerRepository.setIsHeadTrackingAvailable(true)
+ spatializerRepository.defaultHeadTrackingAvailable = true
spatializerRepository.defaultSpatialAudioAvailable = true
val isAvailable by collectLastValue(underTest.isAvailable())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
index eb6f0b2e32b3..06ae220876d6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
@@ -82,7 +82,7 @@ class SpatialAudioComponentInteractorTest : SysuiTestCase() {
),
true
)
- spatializerRepository.setIsHeadTrackingAvailable(true)
+ spatializerRepository.defaultHeadTrackingAvailable = true
underTest =
SpatialAudioComponentInteractor(
diff --git a/packages/SystemUI/res/drawable/ic_head_tracking.xml b/packages/SystemUI/res/drawable/ic_head_tracking.xml
new file mode 100644
index 000000000000..d4a44fd9858e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_head_tracking.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M480,520Q414,520 367,473Q320,426 320,360Q320,294 367,247Q414,200 480,200Q546,200 593,247Q640,294 640,360Q640,426 593,473Q546,520 480,520ZM160,840L160,728Q160,695 177,666Q194,637 224,622Q275,596 339,578Q403,560 480,560Q557,560 621,578Q685,596 736,622Q766,637 783,666Q800,695 800,728L800,840L160,840ZM240,760L720,760L720,728Q720,717 714.5,708Q709,699 700,694Q664,676 607.5,658Q551,640 480,640Q409,640 352.5,658Q296,676 260,694Q251,699 245.5,708Q240,717 240,728L240,760ZM480,440Q513,440 536.5,416.5Q560,393 560,360Q560,327 536.5,303.5Q513,280 480,280Q447,280 423.5,303.5Q400,327 400,360Q400,393 423.5,416.5Q447,440 480,440ZM39,200L39,120Q56,120 70,113.5Q84,107 95,96Q106,85 112,71Q118,57 118,40L199,40Q199,73 186.5,102Q174,131 152,153Q130,175 101,187.5Q72,200 39,200ZM39,361L39,281Q90,281 133.5,262Q177,243 209,210Q241,177 260,133.5Q279,90 279,40L360,40Q360,106 335,164.5Q310,223 266,267Q222,311 164,336Q106,361 39,361ZM920,361Q854,361 795.5,336Q737,311 693,267Q649,223 624,164.5Q599,106 599,40L679,40Q679,90 698,133.5Q717,177 750,210Q783,243 826.5,262Q870,281 920,281L920,361ZM920,200Q887,200 858,187.5Q829,175 807,153Q785,131 772.5,102Q760,73 760,40L840,40Q840,57 846.5,71Q853,85 864,96Q875,107 889,113.5Q903,120 920,120L920,200ZM480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360ZM480,760L480,760Q480,760 480,760Q480,760 480,760Q480,760 480,760Q480,760 480,760Q480,760 480,760Q480,760 480,760Q480,760 480,760Q480,760 480,760L480,760L480,760Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_spatial_audio.xml b/packages/SystemUI/res/drawable/ic_spatial_audio.xml
new file mode 100644
index 000000000000..0ee609ab79f2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_spatial_audio.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M920,401Q848,401 782,373.5Q716,346 665,295Q614,244 586.5,178Q559,112 559,40L639,40Q639,97 660,148Q681,199 721,239Q761,279 812,300.5Q863,322 920,322L920,401ZM920,242Q879,242 842.5,227Q806,212 777,183Q748,154 733,117.5Q718,81 718,40L797,40Q797,65 806.5,87.5Q816,110 833,127Q850,144 872.5,153Q895,162 920,162L920,242ZM400,520Q334,520 287,473Q240,426 240,360Q240,294 287,247Q334,200 400,200Q466,200 513,247Q560,294 560,360Q560,426 513,473Q466,520 400,520ZM80,840L80,728Q80,695 97,666Q114,637 144,622Q195,596 259,578Q323,560 400,560Q477,560 541,578Q605,596 656,622Q686,637 703,666Q720,695 720,728L720,840L80,840ZM160,760L640,760L640,728Q640,717 634.5,708Q629,699 620,694Q584,676 527.5,658Q471,640 400,640Q329,640 272.5,658Q216,676 180,694Q171,699 165.5,708Q160,717 160,728L160,760ZM400,440Q433,440 456.5,416.5Q480,393 480,360Q480,327 456.5,303.5Q433,280 400,280Q367,280 343.5,303.5Q320,327 320,360Q320,393 343.5,416.5Q367,440 400,440ZM400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360ZM400,760L400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760L400,760L400,760Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_spatial_audio_off.xml b/packages/SystemUI/res/drawable/ic_spatial_audio_off.xml
new file mode 100644
index 000000000000..c7d3272b380d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_spatial_audio_off.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M750,550L806,494Q766,454 743.5,402.5Q721,351 721,294Q721,237 743.5,186Q766,135 806,95L750,37Q699,88 670,155Q641,222 641,294Q641,366 670,432.5Q699,499 750,550ZM862,436L918,380Q901,363 891,341Q881,319 881,294Q881,269 891,247Q901,225 918,208L862,151Q833,180 817,216Q801,252 801,293Q801,334 817,371Q833,408 862,436ZM400,520Q334,520 287,473Q240,426 240,360Q240,294 287,247Q334,200 400,200Q466,200 513,247Q560,294 560,360Q560,426 513,473Q466,520 400,520ZM80,840L80,728Q80,695 97,666Q114,637 144,622Q195,596 259,578Q323,560 400,560Q477,560 541,578Q605,596 656,622Q686,637 703,666Q720,695 720,728L720,840L80,840ZM160,760L640,760L640,728Q640,717 634.5,708Q629,699 620,694Q584,676 527.5,658Q471,640 400,640Q329,640 272.5,658Q216,676 180,694Q171,699 165.5,708Q160,717 160,728L160,760ZM400,440Q433,440 456.5,416.5Q480,393 480,360Q480,327 456.5,303.5Q433,280 400,280Q367,280 343.5,303.5Q320,327 320,360Q320,393 343.5,416.5Q367,440 400,440ZM400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360Q400,360 400,360ZM400,760L400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760Q400,760 400,760L400,760L400,760Z" />
+</vector>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 71ae0d716429..035cfdc492d0 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -223,6 +223,7 @@
<item type="id" name="lock_icon" />
<item type="id" name="lock_icon_bg" />
<item type="id" name="burn_in_layer" />
+ <item type="id" name="burn_in_layer_empty_view" />
<item type="id" name="communal_tutorial_indicator" />
<item type="id" name="nssl_placeholder_barrier_bottom" />
<item type="id" name="ambient_indication_container" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5dbdd18bc81b..f71c4155771b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1538,8 +1538,16 @@
<string name="volume_stream_content_description_vibrate_a11y">%1$s. Tap to set to vibrate.</string>
<string name="volume_stream_content_description_mute_a11y">%1$s. Tap to mute.</string>
- <!-- Label for button to enabled/disable live caption [CHAR_LIMIT=30] -->
+ <!-- Label for button to enabled/disable active noise cancellation [CHAR_LIMIT=30] -->
<string name="volume_panel_noise_control_title">Noise Control</string>
+ <!-- Label for button to enabled/disable spatial audio [CHAR_LIMIT=30] -->
+ <string name="volume_panel_spatial_audio_title">Spatial Audio</string>
+ <!-- Label for button to disable spatial audio [CHAR_LIMIT=20] -->
+ <string name="volume_panel_spatial_audio_off">Off</string>
+ <!-- Label for button to enabled spatial audio [CHAR_LIMIT=20] -->
+ <string name="volume_panel_spatial_audio_fixed">Fixed</string>
+ <!-- Label for button to enabled head tracking [CHAR_LIMIT=20] -->
+ <string name="volume_panel_spatial_audio_tracking">Head Tracking</string>
<string name="volume_ringer_change">Tap to change ringer mode</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 9421f150a38a..c0ae4a1f4036 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -53,9 +53,6 @@ import com.android.systemui.Dumpable;
import com.android.systemui.animation.ViewHierarchyAnimator;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.shared.model.TransitionState;
-import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.plugins.clocks.ClockController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.power.shared.model.ScreenPowerState;
@@ -104,7 +101,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
private final Rect mClipBounds = new Rect();
private final KeyguardInteractor mKeyguardInteractor;
private final PowerInteractor mPowerInteractor;
- private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final DozeParameters mDozeParameters;
private View mStatusArea = null;
@@ -112,7 +108,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
private Boolean mSplitShadeEnabled = false;
private Boolean mStatusViewCentered = true;
- private boolean mGoneToAodTransitionRunning = false;
private DumpManager mDumpManager;
private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener =
@@ -181,7 +176,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
KeyguardLogger logger,
InteractionJankMonitor interactionJankMonitor,
KeyguardInteractor keyguardInteractor,
- KeyguardTransitionInteractor keyguardTransitionInteractor,
DumpManager dumpManager,
PowerInteractor powerInteractor) {
super(keyguardStatusView);
@@ -197,7 +191,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mDumpManager = dumpManager;
mKeyguardInteractor = keyguardInteractor;
mPowerInteractor = powerInteractor;
- mKeyguardTransitionInteractor = keyguardTransitionInteractor;
}
@Override
@@ -232,6 +225,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mDumpManager.registerDumpable(getInstanceName(), this);
if (migrateClocksToBlueprint()) {
startCoroutines(EmptyCoroutineContext.INSTANCE);
+ mView.setVisibility(View.GONE);
}
}
@@ -247,15 +241,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
dozeTimeTick();
}
}, context);
-
- collectFlow(mView, mKeyguardTransitionInteractor.getGoneToAodTransition(),
- (TransitionStep step) -> {
- if (step.getTransitionState() == TransitionState.RUNNING) {
- mGoneToAodTransitionRunning = true;
- } else {
- mGoneToAodTransitionRunning = false;
- }
- }, context);
}
public KeyguardStatusView getView() {
@@ -326,7 +311,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
* Set keyguard status view alpha.
*/
public void setAlpha(float alpha) {
- if (!mKeyguardVisibilityHelper.isVisibilityAnimating() && !mGoneToAodTransitionRunning) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
mView.setAlpha(alpha);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 2000028dff41..f5a6cb35b545 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -88,6 +88,10 @@ public class KeyguardVisibilityHelper {
boolean keyguardFadingAway,
boolean goingToFullShade,
int oldStatusBarState) {
+ if (migrateClocksToBlueprint()) {
+ log("Ignoring KeyguardVisibilityelper, migrateClocksToBlueprint flag on");
+ return;
+ }
Assert.isMainThread();
PropertyAnimator.cancelAnimation(mView, AnimatableProperty.ALPHA);
boolean isOccluded = mKeyguardStateController.isOccluded();
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
index e74814adb1b0..376d3129d8c3 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
@@ -17,6 +17,7 @@
package com.android.systemui.dreams.homecontrols
import android.content.Intent
+import android.os.PowerManager
import android.service.controls.ControlsProviderService
import android.service.dreams.DreamService
import android.window.TaskFragmentInfo
@@ -27,6 +28,8 @@ import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsCo
import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor.Companion.MAX_UPDATE_CORRELATION_DELAY
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.dagger.DreamLog
+import com.android.systemui.util.wakelock.WakeLock
+import com.android.systemui.util.wakelock.WakeLock.Builder.NO_TIMEOUT
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
@@ -42,14 +45,23 @@ constructor(
private val controlsSettingsRepository: ControlsSettingsRepository,
private val taskFragmentFactory: TaskFragmentComponent.Factory,
private val homeControlsComponentInteractor: HomeControlsComponentInteractor,
+ private val wakeLockBuilder: WakeLock.Builder,
private val dreamActivityProvider: DreamActivityProvider,
@Background private val bgDispatcher: CoroutineDispatcher,
@DreamLog logBuffer: LogBuffer
) : DreamService() {
+
private val serviceJob = SupervisorJob()
private val serviceScope = CoroutineScope(bgDispatcher + serviceJob)
- private val logger = DreamLogger(logBuffer, "HomeControlsDreamService")
+ private val logger = DreamLogger(logBuffer, TAG)
private lateinit var taskFragmentComponent: TaskFragmentComponent
+ private val wakeLock: WakeLock by lazy {
+ wakeLockBuilder
+ .setMaxTimeout(NO_TIMEOUT)
+ .setTag(TAG)
+ .setLevelsAndFlags(PowerManager.SCREEN_BRIGHT_WAKE_LOCK)
+ .build()
+ }
override fun onAttachedToWindow() {
super.onAttachedToWindow()
@@ -72,6 +84,8 @@ constructor(
hide = { finish() }
)
.apply { createTaskFragment() }
+
+ wakeLock.acquire(TAG)
}
private fun onTaskFragmentInfoChanged(taskFragmentInfo: TaskFragmentInfo) {
@@ -100,6 +114,7 @@ constructor(
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
+ wakeLock.release(TAG)
taskFragmentComponent.destroy()
serviceScope.launch {
delay(CANCELLATION_DELAY_AFTER_DETACHED)
@@ -115,5 +130,6 @@ constructor(
* complete.
*/
val CANCELLATION_DELAY_AFTER_DETACHED = 5.seconds
+ const val TAG = "HomeControlsDreamService"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 3a6423d680c6..1298fa5af033 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -24,7 +24,6 @@ import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -80,12 +79,6 @@ interface KeyguardRepository {
val keyguardAlpha: StateFlow<Float>
/**
- * Observable of the relative offset of the lock-screen clock from its natural position on the
- * screen.
- */
- val clockPosition: StateFlow<Position>
-
- /**
* Observable for whether the keyguard is showing.
*
* Note: this is also `true` when the lock-screen is occluded with an `Activity` "above" it in
@@ -241,11 +234,6 @@ interface KeyguardRepository {
fun setKeyguardAlpha(alpha: Float)
/**
- * Sets the relative offset of the lock-screen clock from its natural position on the screen.
- */
- fun setClockPosition(x: Int, y: Int)
-
- /**
* Returns whether the keyguard bottom area should be constrained to the top of the lock icon
*/
fun isUdfpsSupported(): Boolean
@@ -324,9 +312,6 @@ constructor(
private val _keyguardAlpha = MutableStateFlow(1f)
override val keyguardAlpha = _keyguardAlpha.asStateFlow()
- private val _clockPosition = MutableStateFlow(Position(0, 0))
- override val clockPosition = _clockPosition.asStateFlow()
-
private val _clockShouldBeCentered = MutableStateFlow(true)
override val clockShouldBeCentered: Flow<Boolean> = _clockShouldBeCentered.asStateFlow()
@@ -678,10 +663,6 @@ constructor(
_keyguardAlpha.value = alpha
}
- override fun setClockPosition(x: Int, y: Int) {
- _clockPosition.value = Position(x, y)
- }
-
override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported
override fun setQuickSettingsVisible(isVisible: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index 7ae70a9a3e7c..ca862896efaa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor
import android.content.Context
import androidx.annotation.DimenRes
-import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.doze.util.BurnInHelperWrapper
@@ -47,13 +47,15 @@ constructor(
private val context: Context,
private val burnInHelperWrapper: BurnInHelperWrapper,
@Application private val scope: CoroutineScope,
- private val configurationRepository: ConfigurationRepository,
+ private val configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
) {
val deviceEntryIconXOffset: StateFlow<Int> =
burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_x, isXAxis = true)
+ .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
val deviceEntryIconYOffset: StateFlow<Int> =
burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_y, isXAxis = false)
+ .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
val udfpsProgress: StateFlow<Float> =
keyguardInteractor.dozeTimeTick
.mapLatest { burnInHelperWrapper.burnInProgressOffset() }
@@ -63,18 +65,18 @@ constructor(
burnInHelperWrapper.burnInProgressOffset()
)
- val keyguardBurnIn: Flow<BurnInModel> =
- combine(
- burnInOffset(R.dimen.burn_in_prevention_offset_x, isXAxis = true),
- burnInOffset(R.dimen.burn_in_prevention_offset_y, isXAxis = false).map {
- it * 2 -
- context.resources.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y)
+ /** Given the max x,y dimens, determine the current translation shifts. */
+ fun burnIn(xDimenResourceId: Int, yDimenResourceId: Int): Flow<BurnInModel> {
+ return combine(
+ burnInOffset(xDimenResourceId, isXAxis = true),
+ burnInOffset(yDimenResourceId, isXAxis = false).map {
+ it * 2 - context.resources.getDimensionPixelSize(yDimenResourceId)
}
) { translationX, translationY ->
BurnInModel(translationX, translationY, burnInHelperWrapper.burnInScale())
}
.distinctUntilChanged()
- .stateIn(scope, SharingStarted.Lazily, BurnInModel())
+ }
/**
* Use for max burn-in offsets that are NOT specified in pixels. This flow will recalculate the
@@ -84,23 +86,14 @@ constructor(
private fun burnInOffset(
@DimenRes maxBurnInOffsetResourceId: Int,
isXAxis: Boolean,
- ): StateFlow<Int> {
- return configurationRepository.onAnyConfigurationChange
- .flatMapLatest {
- val maxBurnInOffsetPixels =
- context.resources.getDimensionPixelSize(maxBurnInOffsetResourceId)
- keyguardInteractor.dozeTimeTick.mapLatest {
- calculateOffset(maxBurnInOffsetPixels, isXAxis)
- }
+ ): Flow<Int> {
+ return configurationInteractor.onAnyConfigurationChange.flatMapLatest {
+ val maxBurnInOffsetPixels =
+ context.resources.getDimensionPixelSize(maxBurnInOffsetResourceId)
+ keyguardInteractor.dozeTimeTick.mapLatest {
+ calculateOffset(maxBurnInOffsetPixels, isXAxis)
}
- .stateIn(
- scope,
- SharingStarted.Lazily,
- calculateOffset(
- context.resources.getDimensionPixelSize(maxBurnInOffsetResourceId),
- isXAxis,
- )
- )
+ }
}
/**
@@ -111,24 +104,14 @@ constructor(
private fun burnInOffsetDefinedInPixels(
@DimenRes maxBurnInOffsetResourceId: Int,
isXAxis: Boolean,
- ): StateFlow<Int> {
- return configurationRepository.scaleForResolution
- .flatMapLatest { scale ->
- val maxBurnInOffsetPixels =
- context.resources.getDimensionPixelSize(maxBurnInOffsetResourceId)
- keyguardInteractor.dozeTimeTick.mapLatest {
- calculateOffset(maxBurnInOffsetPixels, isXAxis, scale)
- }
+ ): Flow<Int> {
+ return configurationInteractor.scaleForResolution.flatMapLatest { scale ->
+ val maxBurnInOffsetPixels =
+ context.resources.getDimensionPixelSize(maxBurnInOffsetResourceId)
+ keyguardInteractor.dozeTimeTick.mapLatest {
+ calculateOffset(maxBurnInOffsetPixels, isXAxis, scale)
}
- .stateIn(
- scope,
- SharingStarted.WhileSubscribed(),
- calculateOffset(
- context.resources.getDimensionPixelSize(maxBurnInOffsetResourceId),
- isXAxis,
- configurationRepository.getResolutionScale(),
- )
- )
+ }
}
private fun calculateOffset(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
index d2a7486eed0b..b9ec58ccb925 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
@@ -22,6 +22,8 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
/** Encapsulates business-logic specifically related to the keyguard bottom area. */
@SysUISingleton
@@ -35,10 +37,13 @@ constructor(
/** The amount of alpha for the UI components of the bottom area. */
val alpha: Flow<Float> = repository.bottomAreaAlpha
/** The position of the keyguard clock. */
- val clockPosition: Flow<Position> = repository.clockPosition
+ private val _clockPosition = MutableStateFlow(Position(0, 0))
+ /** See [ClockSection] */
+ @Deprecated("with migrateClocksToBlueprint()")
+ val clockPosition: Flow<Position> = _clockPosition.asStateFlow()
fun setClockPosition(x: Int, y: Int) {
- repository.setClockPosition(x, y)
+ _clockPosition.value = Position(x, y)
}
fun setAlpha(alpha: Float) {
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 f321bd7e13a0..143edf972cb0 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,7 +27,6 @@ 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.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -37,6 +36,7 @@ import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -235,9 +235,6 @@ constructor(
/** The approximate location on the screen of the face unlock sensor, if one is available. */
val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation
- /** The position of the keyguard clock. */
- val clockPosition: Flow<Position> = repository.clockPosition
-
@Deprecated("Use the relevant TransitionViewModel")
val keyguardAlpha: Flow<Float> = repository.keyguardAlpha
@@ -272,8 +269,11 @@ constructor(
configurationInteractor
.dimensionPixelSize(R.dimen.keyguard_translate_distance_on_swipe_up)
.flatMapLatest { translationDistance ->
- shadeRepository.legacyShadeExpansion.map {
- if (it == 0f) {
+ combine(
+ shadeRepository.legacyShadeExpansion.onStart { emit(0f) },
+ keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) },
+ ) { legacyShadeExpansion, goneValue ->
+ if (goneValue == 1f || legacyShadeExpansion == 0f) {
// Reset the translation value
0f
} else {
@@ -281,11 +281,12 @@ constructor(
MathUtils.lerp(
translationDistance,
0,
- Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it)
+ Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(legacyShadeExpansion)
)
}
}
}
+ .distinctUntilChanged()
val clockShouldBeCentered: Flow<Boolean> = repository.clockShouldBeCentered
@@ -345,10 +346,6 @@ constructor(
repository.setQuickSettingsVisible(isVisible)
}
- fun setClockPosition(x: Int, y: Int) {
- repository.setClockPosition(x, y)
- }
-
fun setAlpha(alpha: Float) {
repository.setKeyguardAlpha(alpha)
}
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 dc1f33d53853..fc95ec927a4c 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
@@ -73,7 +73,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@@ -148,6 +147,7 @@ object KeyguardRootViewBinder {
viewModel.alpha(viewState).collect { alpha ->
view.alpha = alpha
childViews[statusViewId]?.alpha = alpha
+ childViews[burnInLayerId]?.alpha = alpha
}
}
}
@@ -195,66 +195,68 @@ object KeyguardRootViewBinder {
// large clock isn't added to burnInLayer due to its scale transition
// so we also need to add translation to it here
// same as translationX
- burnInParams
- .flatMapLatest { params -> viewModel.translationY(params) }
- .collect { y ->
- childViews[burnInLayerId]?.translationY = y
- childViews[largeClockId]?.translationY = y
- childViews[aodNotificationIconContainerId]?.translationY = y
- }
+ viewModel.translationY.collect { y ->
+ childViews[burnInLayerId]?.translationY = y
+ childViews[largeClockId]?.translationY = y
+ childViews[aodNotificationIconContainerId]?.translationY = y
+ }
}
launch {
- burnInParams
- .flatMapLatest { params -> viewModel.translationX(params) }
- .collect { state ->
- val px = state.value ?: return@collect
- when {
- state.isToOrFrom(KeyguardState.AOD) -> {
- childViews[largeClockId]?.translationX = px
- childViews[burnInLayerId]?.translationX = px
- childViews[aodNotificationIconContainerId]
- ?.translationX = px
- }
- state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
- for ((key, childView) in childViews.entries) {
- when (key) {
- indicationArea,
- startButton,
- endButton,
- lockIcon -> {
- // Do not move these views
- }
- else -> childView.translationX = px
+ viewModel.translationX.collect { state ->
+ val px = state.value ?: return@collect
+ when {
+ state.isToOrFrom(KeyguardState.AOD) -> {
+ childViews[largeClockId]?.translationX = px
+ childViews[burnInLayerId]?.translationX = px
+ childViews[aodNotificationIconContainerId]?.translationX =
+ px
+ }
+ state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
+ for ((key, childView) in childViews.entries) {
+ when (key) {
+ indicationArea,
+ startButton,
+ endButton,
+ lockIcon -> {
+ // Do not move these views
}
+ else -> childView.translationX = px
}
}
}
}
+ }
}
launch {
- burnInParams
- .flatMapLatest { params -> viewModel.scale(params) }
- .collect { scaleViewModel ->
- if (scaleViewModel.scaleClockOnly) {
- // For clocks except weather clock, we have scale transition
- // besides translate
- childViews[largeClockId]?.let {
- it.scaleX = scaleViewModel.scale
- it.scaleY = scaleViewModel.scale
- }
- } else {
- // For weather clock, large clock should have only scale
- // transition with other parts in burnInLayer
- childViews[burnInLayerId]?.scaleX = scaleViewModel.scale
- childViews[burnInLayerId]?.scaleY = scaleViewModel.scale
- childViews[aodNotificationIconContainerId]?.scaleX =
- scaleViewModel.scale
- childViews[aodNotificationIconContainerId]?.scaleY =
- scaleViewModel.scale
+ viewModel.scale.collect { scaleViewModel ->
+ if (scaleViewModel.scaleClockOnly) {
+ // For clocks except weather clock, we have scale transition
+ // besides translate
+ childViews[largeClockId]?.let {
+ it.scaleX = scaleViewModel.scale
+ it.scaleY = scaleViewModel.scale
+ }
+ // Make sure to reset these views, or they will be invisible
+ if (childViews[burnInLayerId]?.scaleX != 1f) {
+ childViews[burnInLayerId]?.scaleX = 1f
+ childViews[burnInLayerId]?.scaleY = 1f
+ childViews[aodNotificationIconContainerId]?.scaleX = 1f
+ childViews[aodNotificationIconContainerId]?.scaleY = 1f
+ view.requestLayout()
}
+ } else {
+ // For weather clock, large clock should have only scale
+ // transition with other parts in burnInLayer
+ childViews[burnInLayerId]?.scaleX = scaleViewModel.scale
+ childViews[burnInLayerId]?.scaleY = scaleViewModel.scale
+ childViews[aodNotificationIconContainerId]?.scaleX =
+ scaleViewModel.scale
+ childViews[aodNotificationIconContainerId]?.scaleY =
+ scaleViewModel.scale
}
+ }
}
if (NotificationIconContainerRefactor.isEnabled) {
@@ -311,6 +313,8 @@ object KeyguardRootViewBinder {
}
}
+ launch { burnInParams.collect { viewModel.updateBurnInParams(it) } }
+
if (deviceEntryHapticsInteractor != null && vibratorHelper != null) {
launch {
deviceEntryHapticsInteractor.playSuccessHaptic.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
index 98bebd091f1a..88ce9dc88a7b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
@@ -21,6 +21,8 @@ import android.content.Context
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.KeyguardRootView
@@ -37,24 +39,24 @@ constructor(
private val clockViewModel: KeyguardClockViewModel,
) : KeyguardSection() {
private lateinit var burnInLayer: AodBurnInLayer
+ // The burn-in layer requires at least 1 view at all times
+ private val emptyView: View by lazy {
+ View(context, null).apply {
+ id = R.id.burn_in_layer_empty_view
+ visibility = View.GONE
+ }
+ }
override fun addViews(constraintLayout: ConstraintLayout) {
if (!migrateClocksToBlueprint()) {
return
}
- // The burn-in layer requires at least 1 view at all times
- val emptyView = View(context, null).apply { id = View.generateViewId() }
constraintLayout.addView(emptyView)
burnInLayer =
AodBurnInLayer(context).apply {
id = R.id.burn_in_layer
registerListener(rootView)
addView(emptyView)
- if (!migrateClocksToBlueprint()) {
- val statusView =
- constraintLayout.requireViewById<View>(R.id.keyguard_status_view)
- addView(statusView)
- }
}
constraintLayout.addView(burnInLayer)
}
@@ -70,6 +72,13 @@ constructor(
if (!migrateClocksToBlueprint()) {
return
}
+
+ constraintSet.apply {
+ // The empty view should not occupy any space
+ constrainHeight(R.id.burn_in_layer_empty_view, 1)
+ constrainWidth(R.id.burn_in_layer_empty_view, 0)
+ connect(R.id.burn_in_layer_empty_view, BOTTOM, PARENT_ID, BOTTOM)
+ }
}
override fun removeViews(constraintLayout: ConstraintLayout) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 7be390a4526f..f961e083e64f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.Log
import android.util.MathUtils
import com.android.app.animation.Interpolators
import com.android.keyguard.KeyguardClockSwitch
@@ -62,23 +63,26 @@ constructor(
private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
private val keyguardClockViewModel: KeyguardClockViewModel,
) {
- /** Horizontal translation for elements that need to apply anti-burn-in tactics. */
- fun translationX(
- params: BurnInParameters,
- ): Flow<Float> {
- return burnIn(params).map { it.translationX.toFloat() }
- }
+ private val TAG = "AodBurnInViewModel"
- /** Vertical translation for elements that need to apply anti-burn-in tactics. */
- fun translationY(
- params: BurnInParameters,
- ): Flow<Float> {
+ /** All burn-in movement: x,y,scale, to shift items and prevent burn-in */
+ fun movement(
+ burnInParams: BurnInParameters,
+ ): Flow<BurnInModel> {
+ val params =
+ if (burnInParams.minViewY < burnInParams.topInset) {
+ // minViewY should never be below the inset. Correct it if needed
+ Log.w(TAG, "minViewY is below topInset: $burnInParams")
+ burnInParams.copy(minViewY = burnInParams.topInset)
+ } else {
+ burnInParams
+ }
return configurationInteractor
.dimensionPixelSize(R.dimen.keyguard_enter_from_top_translation_y)
.flatMapLatest { enterFromTopAmount ->
combine(
keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
- burnIn(params).map { it.translationY.toFloat() }.onStart { emit(0f) },
+ burnIn(params).onStart { emit(BurnInModel()) },
goneToAodTransitionViewModel
.enterFromTopTranslationY(enterFromTopAmount)
.onStart { emit(StateToValue()) },
@@ -88,32 +92,26 @@ constructor(
aodToLockscreenTransitionViewModel.translationY(params.translationY).onStart {
emit(StateToValue())
},
- ) { keyguardTranslationY, burnInY, goneToAod, occludedToLockscreen, aodToLockscreen
- ->
- if (isInTransition(aodToLockscreen.transitionState)) {
- aodToLockscreen.value ?: 0f
- } else if (isInTransition(goneToAod.transitionState)) {
- (goneToAod.value ?: 0f) + burnInY
- } else {
- burnInY + occludedToLockscreen + keyguardTranslationY
- }
+ ) {
+ keyguardTranslationY,
+ burnInModel,
+ goneToAod,
+ occludedToLockscreen,
+ aodToLockscreen ->
+ val translationY =
+ if (isInTransition(aodToLockscreen.transitionState)) {
+ aodToLockscreen.value ?: 0f
+ } else if (isInTransition(goneToAod.transitionState)) {
+ (goneToAod.value ?: 0f) + burnInModel.translationY
+ } else {
+ burnInModel.translationY + occludedToLockscreen + keyguardTranslationY
+ }
+ burnInModel.copy(translationY = translationY.toInt())
}
}
.distinctUntilChanged()
}
- /** Scale for elements that need to apply anti-burn-in tactics. */
- fun scale(
- params: BurnInParameters,
- ): Flow<BurnInScaleViewModel> {
- return burnIn(params).map {
- BurnInScaleViewModel(
- scale = it.scale,
- scaleClockOnly = it.scaleClockOnly,
- )
- }
- }
-
private fun isInTransition(state: TransitionState): Boolean {
return state == STARTED || state == RUNNING
}
@@ -125,7 +123,10 @@ constructor(
keyguardTransitionInteractor.dozeAmountTransition.map {
Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value)
},
- burnInInteractor.keyguardBurnIn,
+ burnInInteractor.burnIn(
+ xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
+ yDimenResourceId = R.dimen.burn_in_prevention_offset_y
+ ),
) { interpolated, burnIn ->
val useScaleOnly =
(clockController(params.clockControllerProvider)
@@ -149,7 +150,6 @@ constructor(
} else {
max(params.topInset, params.minViewY + burnInY) - params.minViewY
}
-
BurnInModel(
translationX = MathUtils.lerp(0, burnIn.translationX, interpolated).toInt(),
translationY = translationY,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index 6458edaecd9e..e35e06533f8c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -17,10 +17,14 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.Flags.keyguardBottomAreaRefactor
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -32,9 +36,10 @@ class KeyguardIndicationAreaViewModel
@Inject
constructor(
private val keyguardInteractor: KeyguardInteractor,
- bottomAreaInteractor: KeyguardBottomAreaInteractor,
+ private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
private val burnInHelperWrapper: BurnInHelperWrapper,
+ private val burnInInteractor: BurnInInteractor,
private val shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
configurationInteractor: ConfigurationInteractor,
) {
@@ -63,24 +68,37 @@ constructor(
}
.distinctUntilChanged()
}
+
+ private val burnIn: Flow<BurnInModel> =
+ burnInInteractor
+ .burnIn(
+ xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
+ yDimenResourceId = R.dimen.default_burn_in_prevention_offset,
+ )
+ .distinctUntilChanged()
+
/** An observable for the x-offset by which the indication area should be translated. */
val indicationAreaTranslationX: Flow<Float> =
- if (keyguardBottomAreaRefactor()) {
- keyguardInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
+ if (migrateClocksToBlueprint() || keyguardBottomAreaRefactor()) {
+ burnIn.map { it.translationX.toFloat() }
} else {
bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
}
/** Returns an observable for the y-offset by which the indication area should be translated. */
fun indicationAreaTranslationY(defaultBurnInOffset: Int): Flow<Float> {
- return keyguardInteractor.dozeAmount
- .map { dozeAmount ->
- dozeAmount *
- (burnInHelperWrapper.burnInOffset(
- /* amplitude = */ defaultBurnInOffset * 2,
- /* xAxis= */ false,
- ) - defaultBurnInOffset)
- }
- .distinctUntilChanged()
+ return if (migrateClocksToBlueprint()) {
+ burnIn.map { it.translationY.toFloat() }
+ } else {
+ keyguardInteractor.dozeAmount
+ .map { dozeAmount ->
+ dozeAmount *
+ (burnInHelperWrapper.burnInOffset(
+ /* amplitude = */ defaultBurnInOffset * 2,
+ /* xAxis= */ false,
+ ) - defaultBurnInOffset)
+ }
+ .distinctUntilChanged()
+ }
}
}
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 f848717c2170..5ca9215ce199 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
@@ -24,9 +24,11 @@ import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -47,8 +49,11 @@ import com.android.systemui.util.ui.toAnimatedValueFlow
import com.android.systemui.util.ui.zip
import javax.inject.Inject
import kotlin.math.max
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -57,12 +62,14 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class KeyguardRootViewModel
@Inject
constructor(
+ @Application private val scope: CoroutineScope,
private val deviceEntryInteractor: DeviceEntryInteractor,
private val dozeParameters: DozeParameters,
private val keyguardInteractor: KeyguardInteractor,
@@ -102,6 +109,8 @@ constructor(
private val aodAlphaViewModel: AodAlphaViewModel,
private val shadeInteractor: ShadeInteractor,
) {
+ private var burnInJob: Job? = null
+ private val burnInModel = MutableStateFlow(BurnInModel())
val burnInLayerVisibility: Flow<Int> =
keyguardTransitionInteractor.startedKeyguardState
@@ -213,22 +222,34 @@ constructor(
/** For elements that appear and move during the animation -> AOD */
val burnInLayerAlpha: Flow<Float> = aodAlphaViewModel.alpha
- fun translationY(params: BurnInParameters): Flow<Float> {
- return aodBurnInViewModel.translationY(params)
- }
+ val translationY: Flow<Float> = burnInModel.map { it.translationY.toFloat() }
- fun translationX(params: BurnInParameters): Flow<StateToValue> {
- return merge(
- aodBurnInViewModel.translationX(params).map { StateToValue(to = AOD, value = it) },
+ val translationX: Flow<StateToValue> =
+ merge(
+ burnInModel.map { StateToValue(to = AOD, value = it.translationX.toFloat()) },
lockscreenToGlanceableHubTransitionViewModel.keyguardTranslationX,
glanceableHubToLockscreenTransitionViewModel.keyguardTranslationX,
)
- }
- fun scale(params: BurnInParameters): Flow<BurnInScaleViewModel> {
- return aodBurnInViewModel.scale(params)
+ fun updateBurnInParams(params: BurnInParameters) {
+ burnInJob?.cancel()
+
+ burnInJob =
+ scope.launch {
+ aodBurnInViewModel.movement(params).collect {
+ burnInModel.value = it
+ }
+ }
}
+ val scale: Flow<BurnInScaleViewModel> =
+ burnInModel.map {
+ BurnInScaleViewModel(
+ scale = it.scale,
+ scaleClockOnly = it.scaleClockOnly,
+ )
+ }
+
/** Is the notification icon container visible? */
val isNotifIconContainerVisible: Flow<AnimatedValue<Boolean>> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
index 2294fc0be520..d8c38503dbaf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
@@ -22,12 +22,14 @@ import android.content.Context
import android.graphics.Bitmap
import android.graphics.Rect
import android.graphics.drawable.Drawable
+import android.util.Log
import android.view.Display
import android.view.LayoutInflater
import android.view.ScrollCaptureResponse
import android.view.View
import android.view.ViewTreeObserver
import android.view.WindowInsets
+import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import com.android.internal.logging.UiEventLogger
import com.android.systemui.flags.FeatureFlags
@@ -40,7 +42,6 @@ import com.android.systemui.res.R
class LegacyScreenshotViewProxy(context: Context) : ScreenshotViewProxy {
override val view: ScreenshotView =
LayoutInflater.from(context).inflate(R.layout.screenshot, null) as ScreenshotView
- override val internalInsetsListener: ViewTreeObserver.OnComputeInternalInsetsListener
override val screenshotPreview: View
override var defaultDisplay: Int = Display.DEFAULT_DISPLAY
@@ -51,6 +52,9 @@ class LegacyScreenshotViewProxy(context: Context) : ScreenshotViewProxy {
set(value) {
view.setDefaultTimeoutMillis(value)
}
+ override var onBackInvokedCallback: OnBackInvokedCallback = OnBackInvokedCallback {
+ Log.wtf(TAG, "OnBackInvoked called before being set!")
+ }
override var onKeyListener: View.OnKeyListener? = null
set(value) {
view.setOnKeyListener(value)
@@ -84,7 +88,35 @@ class LegacyScreenshotViewProxy(context: Context) : ScreenshotViewProxy {
get() = view.isPendingSharedTransition
init {
- internalInsetsListener = view
+
+ view.addOnAttachStateChangeListener(
+ object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(view: View) {
+ if (LogConfig.DEBUG_INPUT) {
+ Log.d(TAG, "Registering Predictive Back callback")
+ }
+ view
+ .findOnBackInvokedDispatcher()
+ ?.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ onBackInvokedCallback
+ )
+ }
+
+ override fun onViewDetachedFromWindow(view: View) {
+ if (LogConfig.DEBUG_INPUT) {
+ Log.d(TAG, "Unregistering Predictive Back callback")
+ }
+ view
+ .findOnBackInvokedDispatcher()
+ ?.unregisterOnBackInvokedCallback(onBackInvokedCallback)
+ }
+ }
+ )
+ if (LogConfig.DEBUG_WINDOW) {
+ Log.d(TAG, "adding OnComputeInternalInsetsListener")
+ }
+ view.viewTreeObserver.addOnComputeInternalInsetsListener(view)
screenshotPreview = view.screenshotPreview
}
@@ -139,12 +171,6 @@ class LegacyScreenshotViewProxy(context: Context) : ScreenshotViewProxy {
override fun announceForAccessibility(string: String) = view.announceForAccessibility(string)
- override fun addOnAttachStateChangeListener(listener: View.OnAttachStateChangeListener) =
- view.addOnAttachStateChangeListener(listener)
-
- override fun findOnBackInvokedDispatcher(): OnBackInvokedDispatcher? =
- view.findOnBackInvokedDispatcher()
-
override fun getViewTreeObserver(): ViewTreeObserver = view.viewTreeObserver
override fun post(runnable: Runnable) {
@@ -156,4 +182,8 @@ class LegacyScreenshotViewProxy(context: Context) : ScreenshotViewProxy {
return LegacyScreenshotViewProxy(context)
}
}
+
+ companion object {
+ private const val TAG = "LegacyScreenshotViewProxy"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 13448d258a2c..1ca9b985b090 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -76,8 +76,6 @@ import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
-import android.window.OnBackInvokedCallback;
-import android.window.OnBackInvokedDispatcher;
import android.window.WindowContext;
import com.android.internal.app.ChooserActivity;
@@ -265,13 +263,6 @@ public class ScreenshotController {
private final UserManager mUserManager;
private final AssistContentRequester mAssistContentRequester;
- private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
- if (DEBUG_INPUT) {
- Log.d(TAG, "Predictive Back callback dispatched");
- }
- respondToKeyDismissal();
- };
-
private final MessageContainerController mMessageContainerController;
private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
@@ -594,27 +585,13 @@ public class ScreenshotController {
}
mMessageContainerController.setView(mViewProxy.getView());
- mViewProxy.addOnAttachStateChangeListener(
- new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(@NonNull View v) {
- if (DEBUG_INPUT) {
- Log.d(TAG, "Registering Predictive Back callback");
- }
- mViewProxy.findOnBackInvokedDispatcher().registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback);
- }
-
- @Override
- public void onViewDetachedFromWindow(@NonNull View v) {
- if (DEBUG_INPUT) {
- Log.d(TAG, "Unregistering Predictive Back callback");
- }
- mViewProxy.findOnBackInvokedDispatcher()
- .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
- }
- });
mViewProxy.setLogger(mUiEventLogger);
+ mViewProxy.setOnBackInvokedCallback(() -> {
+ if (DEBUG_INPUT) {
+ Log.d(TAG, "Predictive Back callback dispatched");
+ }
+ respondToKeyDismissal();
+ });
mViewProxy.setCallbacks(new ScreenshotView.ScreenshotViewCallback() {
@Override
public void onUserInteraction() {
@@ -657,11 +634,6 @@ public class ScreenshotController {
});
if (DEBUG_WINDOW) {
- Log.d(TAG, "adding OnComputeInternalInsetsListener");
- }
- mViewProxy.getViewTreeObserver().addOnComputeInternalInsetsListener(
- mViewProxy.getInternalInsetsListener());
- if (DEBUG_WINDOW) {
Log.d(TAG, "setContentView: " + mViewProxy.getView());
}
setContentView(mViewProxy.getView());
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
index 0064521bd3a4..381404a85587 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
@@ -28,18 +28,18 @@ import android.view.View.OnKeyListener
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.WindowInsets
-import android.window.OnBackInvokedDispatcher
+import android.window.OnBackInvokedCallback
import com.android.internal.logging.UiEventLogger
import com.android.systemui.flags.FeatureFlags
/** Abstraction of the surface between ScreenshotController and ScreenshotView */
interface ScreenshotViewProxy {
val view: ViewGroup
- val internalInsetsListener: ViewTreeObserver.OnComputeInternalInsetsListener
val screenshotPreview: View
var defaultDisplay: Int
var defaultTimeoutMillis: Long
+ var onBackInvokedCallback: OnBackInvokedCallback
var onKeyListener: OnKeyListener?
var flags: FeatureFlags?
var packageName: String
@@ -78,8 +78,6 @@ interface ScreenshotViewProxy {
fun stopInputListening()
fun requestFocus()
fun announceForAccessibility(string: String)
- fun addOnAttachStateChangeListener(listener: View.OnAttachStateChangeListener)
- fun findOnBackInvokedDispatcher(): OnBackInvokedDispatcher?
fun getViewTreeObserver(): ViewTreeObserver
fun post(runnable: Runnable)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 2fd438be9610..a1644b219063 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1288,10 +1288,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mView.getContext().getDisplay());
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
- }
- mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
- mKeyguardStatusViewController.getView().addOnLayoutChangeListener(
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
+ mKeyguardStatusViewController.getView().addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
int oldHeight = oldBottom - oldTop;
if (v.getHeight() != oldHeight) {
@@ -1299,7 +1298,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
});
- updateClockAppearance();
+ updateClockAppearance();
+ }
}
@Override
@@ -1326,7 +1326,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private void onSplitShadeEnabledChanged() {
mShadeLog.logSplitShadeChanged(mSplitShadeEnabled);
- mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
+ if (!migrateClocksToBlueprint()) {
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
+ }
// Reset any left over overscroll state. It is a rare corner case but can happen.
mQsController.setOverScrollAmount(0);
mScrimController.setNotificationsOverScrollAmount(0);
@@ -1441,11 +1443,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
mStatusBarStateController.getInterpolatedDozeAmount());
- mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
- mBarState,
- false,
- false,
- mBarState);
+ if (!migrateClocksToBlueprint()) {
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ mBarState,
+ false,
+ false,
+ mBarState);
+ }
if (mKeyguardQsUserSwitchController != null) {
mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
mBarState,
@@ -1665,13 +1669,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardStatusViewController.setLockscreenClockY(
mClockPositionAlgorithm.getExpandedPreferredClockY());
}
- if (keyguardBottomAreaRefactor()) {
- mKeyguardInteractor.setClockPosition(
- mClockPositionResult.clockX, mClockPositionResult.clockY);
- } else {
+ if (!(migrateClocksToBlueprint() || keyguardBottomAreaRefactor())) {
mKeyguardBottomAreaInteractor.setClockPosition(
mClockPositionResult.clockX, mClockPositionResult.clockY);
}
+
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
@@ -1749,13 +1751,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private void updateKeyguardStatusViewAlignment(boolean animate) {
- boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
- ConstraintLayout layout;
if (migrateClocksToBlueprint()) {
- layout = mKeyguardViewConfigurator.getKeyguardRootView();
- } else {
- layout = mNotificationContainerParent;
+ return;
}
+ boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
+ ConstraintLayout layout = mNotificationContainerParent;
mKeyguardStatusViewController.updateAlignment(
layout, mSplitShadeEnabled, shouldBeCentered, animate);
mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered));
@@ -3316,6 +3316,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
/** Updates the views to the initial state for the fold to AOD animation. */
@Override
public void prepareFoldToAodAnimation() {
+ if (migrateClocksToBlueprint()) {
+ return;
+ }
// Force show AOD UI even if we are not locked
showAodUi();
@@ -3337,6 +3340,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@Override
public void startFoldToAodAnimation(Runnable startAction, Runnable endAction,
Runnable cancelAction) {
+ if (migrateClocksToBlueprint()) {
+ return;
+ }
final ViewPropertyAnimator viewAnimator = mView.animate();
viewAnimator.cancel();
viewAnimator
@@ -3372,6 +3378,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
/** Cancels fold to AOD transition and resets view state. */
@Override
public void cancelFoldToAodAnimation() {
+ if (migrateClocksToBlueprint()) {
+ return;
+ }
cancelAnimation();
resetAlpha();
resetTranslation();
@@ -4446,11 +4455,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
}
- mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
- statusBarState,
- keyguardFadingAway,
- goingToFullShade,
- mBarState);
+ if (!migrateClocksToBlueprint()) {
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ statusBarState,
+ keyguardFadingAway,
+ goingToFullShade,
+ mBarState);
+ }
if (!keyguardBottomAreaRefactor()) {
setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
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 78fc1471053d..3a9cdd2d852e 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
@@ -474,7 +474,10 @@ constructor(
*/
fun translationY(params: BurnInParameters): Flow<Float> {
return combine(
- aodBurnInViewModel.translationY(params).onStart { emit(0f) },
+ aodBurnInViewModel
+ .movement(params)
+ .map { it.translationY.toFloat() }
+ .onStart { emit(0f) },
isOnLockscreenWithoutShade,
merge(
keyguardInteractor.keyguardTranslationY,
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/ClientTrackingWakeLock.kt b/packages/SystemUI/src/com/android/systemui/util/wakelock/ClientTrackingWakeLock.kt
index db300ebe6cae..2157fafa78ff 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/ClientTrackingWakeLock.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/ClientTrackingWakeLock.kt
@@ -18,6 +18,7 @@ package com.android.systemui.util.wakelock
import android.os.PowerManager
import android.util.Log
+import com.android.systemui.util.wakelock.WakeLock.Builder.NO_TIMEOUT
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
@@ -36,7 +37,11 @@ class ClientTrackingWakeLock(
override fun acquire(why: String) {
val count = activeClients.computeIfAbsent(why) { _ -> AtomicInteger(0) }.incrementAndGet()
logger?.logAcquire(pmWakeLock, why, count)
- pmWakeLock.acquire(maxTimeout)
+ if (maxTimeout == NO_TIMEOUT) {
+ pmWakeLock.acquire()
+ } else {
+ pmWakeLock.acquire(maxTimeout)
+ }
}
override fun release(why: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
index 707751a58d84..f763ee46666a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
@@ -130,7 +130,11 @@ public interface WakeLock {
if (logger != null) {
logger.logAcquire(inner, why, count);
}
- inner.acquire(maxTimeout);
+ if (maxTimeout == Builder.NO_TIMEOUT) {
+ inner.acquire();
+ } else {
+ inner.acquire(maxTimeout);
+ }
}
/** @see PowerManager.WakeLock#release() */
@@ -169,6 +173,7 @@ public interface WakeLock {
* An injectable Builder that wraps {@link #createPartial(Context, String, long)}.
*/
class Builder {
+ public static final long NO_TIMEOUT = -1;
private final Context mContext;
private final WakeLockLogger mLogger;
private String mTag;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt
index 593b90aa3c68..4ba7cbb1588c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt
@@ -21,12 +21,10 @@ import android.media.Spatializer
import com.android.settingslib.media.data.repository.SpatializerRepository
import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl
import com.android.settingslib.media.domain.interactor.SpatializerInteractor
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import dagger.Module
import dagger.Provides
import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
/** Spatializer module. */
@Module
@@ -42,9 +40,8 @@ interface SpatializerModule {
@Provides
fun provdieSpatializerRepository(
spatializer: Spatializer,
- @Application scope: CoroutineScope,
@Background backgroundContext: CoroutineContext,
- ): SpatializerRepository = SpatializerRepositoryImpl(spatializer, scope, backgroundContext)
+ ): SpatializerRepository = SpatializerRepositoryImpl(spatializer, backgroundContext)
@Provides
fun provideSpatializerInetractor(repository: SpatializerRepository): SpatializerInteractor =
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt
index 8ab563a94299..6c47aec563df 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt
@@ -23,3 +23,6 @@ data class ToggleButtonViewModel(
val icon: Icon,
val label: CharSequence,
)
+
+fun ToggleButtonViewModel.toButtonViewModel(): ButtonViewModel =
+ ButtonViewModel(icon = icon, label = label)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt
index 9d801fc9bfa1..9ef07fa3f11f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt
@@ -24,5 +24,6 @@ object VolumePanelComponents {
const val BOTTOM_BAR: VolumePanelComponentKey = "bottom_bar"
const val VOLUME_SLIDERS: VolumePanelComponentKey = "volume_sliders"
const val CAPTIONING: VolumePanelComponentKey = "captioning"
+ const val SPATIAL_AUDIO: VolumePanelComponentKey = "spatial_audio"
const val ANC: VolumePanelComponentKey = "anc"
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
index 4358611694b2..6032bfe3b50a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
@@ -71,10 +71,10 @@ constructor(
combine(
currentAudioDeviceAttributes,
changes.onStart { emit(Unit) },
- spatializerInteractor.isHeadTrackingAvailable,
- ) { attributes, _, isHeadTrackingAvailable ->
+ ) { attributes, _,
+ ->
attributes ?: return@combine SpatialAudioAvailabilityModel.Unavailable
- if (isHeadTrackingAvailable) {
+ if (spatializerInteractor.isHeadTrackingAvailable(attributes)) {
return@combine SpatialAudioAvailabilityModel.HeadTracking
}
if (spatializerInteractor.isSpatialAudioAvailable(attributes)) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/model/SpatialAudioEnabledModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/model/SpatialAudioEnabledModel.kt
index 4e65f60aa0e1..9735e5cbd9c9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/model/SpatialAudioEnabledModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/model/SpatialAudioEnabledModel.kt
@@ -19,6 +19,16 @@ package com.android.systemui.volume.panel.component.spatial.domain.model
/** Models spatial audio and head tracking enabled/disabled state. */
interface SpatialAudioEnabledModel {
+ companion object {
+ /** All possible SpatialAudioEnabledModel implementations. */
+ val values =
+ listOf(
+ Disabled,
+ SpatialAudioEnabled,
+ HeadTrackingEnabled,
+ )
+ }
+
/** Spatial audio is disabled. */
data object Disabled : SpatialAudioEnabledModel
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt
new file mode 100644
index 000000000000..9f9275baf4f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.spatial.ui.viewmodel
+
+import com.android.systemui.common.shared.model.Color
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
+import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
+
+data class SpatialAudioButtonViewModel(
+ val model: SpatialAudioEnabledModel,
+ val button: ToggleButtonViewModel,
+ val iconColor: Color,
+ val labelColor: Color,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
new file mode 100644
index 000000000000..30715d167c25
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.spatial.ui.viewmodel
+
+import android.content.Context
+import com.android.systemui.common.shared.model.Color
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.toButtonViewModel
+import com.android.systemui.volume.panel.component.spatial.domain.SpatialAudioAvailabilityCriteria
+import com.android.systemui.volume.panel.component.spatial.domain.interactor.SpatialAudioComponentInteractor
+import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
+import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+@VolumePanelScope
+class SpatialAudioViewModel
+@Inject
+constructor(
+ @Application private val context: Context,
+ @VolumePanelScope private val scope: CoroutineScope,
+ availabilityCriteria: SpatialAudioAvailabilityCriteria,
+ private val interactor: SpatialAudioComponentInteractor,
+) {
+
+ val spatialAudioButton: StateFlow<ButtonViewModel?> =
+ interactor.isEnabled
+ .map { it.toViewModel(true).toButtonViewModel() }
+ .stateIn(scope, SharingStarted.Eagerly, null)
+
+ val isAvailable: StateFlow<Boolean> =
+ availabilityCriteria.isAvailable().stateIn(scope, SharingStarted.Eagerly, true)
+
+ val spatialAudioButtonByEnabled: StateFlow<List<SpatialAudioButtonViewModel>> =
+ combine(interactor.isEnabled, interactor.isAvailable) { currentIsEnabled, isAvailable ->
+ SpatialAudioEnabledModel.values
+ .filter {
+ if (it is SpatialAudioEnabledModel.HeadTrackingEnabled) {
+ // Spatial audio control can be visible when there is spatial audio
+ // setting available but not the head tracking.
+ isAvailable is SpatialAudioAvailabilityModel.HeadTracking
+ } else {
+ true
+ }
+ }
+ .map { isEnabled ->
+ val isChecked = isEnabled == currentIsEnabled
+ val buttonViewModel: ToggleButtonViewModel =
+ isEnabled.toViewModel(isChecked)
+ SpatialAudioButtonViewModel(
+ button = buttonViewModel,
+ model = isEnabled,
+ iconColor =
+ Color.Attribute(
+ if (isChecked)
+ com.android.internal.R.attr.materialColorOnPrimaryContainer
+ else com.android.internal.R.attr.materialColorOnSurfaceVariant
+ ),
+ labelColor =
+ Color.Attribute(
+ if (isChecked)
+ com.android.internal.R.attr.materialColorOnSurface
+ else com.android.internal.R.attr.materialColorOutline
+ ),
+ )
+ }
+ }
+ .stateIn(scope, SharingStarted.Eagerly, emptyList())
+
+ fun setEnabled(model: SpatialAudioEnabledModel) {
+ scope.launch { interactor.setEnabled(model) }
+ }
+
+ private fun SpatialAudioEnabledModel.toViewModel(isChecked: Boolean): ToggleButtonViewModel {
+ if (this is SpatialAudioEnabledModel.HeadTrackingEnabled) {
+ return ToggleButtonViewModel(
+ isChecked = isChecked,
+ icon = Icon.Resource(R.drawable.ic_head_tracking, contentDescription = null),
+ label = context.getString(R.string.volume_panel_spatial_audio_tracking)
+ )
+ }
+
+ if (this is SpatialAudioEnabledModel.SpatialAudioEnabled) {
+ return ToggleButtonViewModel(
+ isChecked = isChecked,
+ icon = Icon.Resource(R.drawable.ic_spatial_audio, contentDescription = null),
+ label = context.getString(R.string.volume_panel_spatial_audio_fixed)
+ )
+ }
+
+ if (this is SpatialAudioEnabledModel.Disabled) {
+ return ToggleButtonViewModel(
+ isChecked = isChecked,
+ icon = Icon.Resource(R.drawable.ic_spatial_audio_off, contentDescription = null),
+ label = context.getString(R.string.volume_panel_spatial_audio_off)
+ )
+ }
+
+ error("Unsupported model: $this")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
index f31ee865eaac..d868c33d0887 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
@@ -20,6 +20,7 @@ import com.android.systemui.volume.panel.component.anc.AncModule
import com.android.systemui.volume.panel.component.bottombar.BottomBarModule
import com.android.systemui.volume.panel.component.captioning.CaptioningModule
import com.android.systemui.volume.panel.component.mediaoutput.MediaOutputModule
+import com.android.systemui.volume.panel.component.spatialaudio.SpatialAudioModule
import com.android.systemui.volume.panel.component.volume.VolumeSlidersModule
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
@@ -49,6 +50,7 @@ import kotlinx.coroutines.CoroutineScope
// Components modules
BottomBarModule::class,
AncModule::class,
+ SpatialAudioModule::class,
VolumeSlidersModule::class,
CaptioningModule::class,
MediaOutputModule::class,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
index 57ea9972012f..999f4c161633 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
@@ -51,6 +51,7 @@ interface DomainModule {
fun provideEnabledComponents(): Collection<VolumePanelComponentKey> {
return setOf(
VolumePanelComponents.ANC,
+ VolumePanelComponents.SPATIAL_AUDIO,
VolumePanelComponents.CAPTIONING,
VolumePanelComponents.VOLUME_SLIDERS,
VolumePanelComponents.MEDIA_OUTPUT,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
index ec4da0692841..8ba06e10fcf8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
@@ -49,6 +49,7 @@ interface UiModule {
fun provideFooterComponents(): Collection<VolumePanelComponentKey> {
return listOf(
VolumePanelComponents.ANC,
+ VolumePanelComponents.SPATIAL_AUDIO,
VolumePanelComponents.CAPTIONING,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index 13fb42ce8c3e..90587d7386ce 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -16,8 +16,6 @@
package com.android.keyguard;
-import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,7 +30,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.power.data.repository.FakePowerRepository;
import com.android.systemui.power.domain.interactor.PowerInteractorFactory;
import com.android.systemui.res.R;
@@ -62,7 +59,6 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
@Mock protected KeyguardStatusViewController mControllerMock;
@Mock protected InteractionJankMonitor mInteractionJankMonitor;
@Mock protected ViewTreeObserver mViewTreeObserver;
- @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock protected DumpManager mDumpManager;
protected FakeKeyguardRepository mFakeKeyguardRepository;
protected FakePowerRepository mFakePowerRepository;
@@ -93,7 +89,6 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
mKeyguardLogger,
mInteractionJankMonitor,
deps.getKeyguardInteractor(),
- mKeyguardTransitionInteractor,
mDumpManager,
PowerInteractorFactory.create(
mFakePowerRepository
@@ -110,7 +105,6 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
when(mKeyguardClockSwitchController.getView()).thenReturn(mKeyguardClockSwitch);
- when(mKeyguardTransitionInteractor.getGoneToAodTransition()).thenReturn(emptyFlow());
when(mKeyguardStatusView.findViewById(R.id.keyguard_status_area))
.thenReturn(mKeyguardStatusAreaView);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
index df52265384fa..0bd541c7a704 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
@@ -20,16 +20,19 @@ package com.android.systemui.keyguard.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -43,41 +46,35 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class BurnInInteractorTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+ val testScope = kosmos.testScope
+ val configurationRepository = kosmos.fakeConfigurationRepository
+ val fakeKeyguardRepository = kosmos.fakeKeyguardRepository
+
private val burnInOffset = 7
private var burnInProgress = 0f
@Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
- private lateinit var configurationRepository: FakeConfigurationRepository
- private lateinit var keyguardInteractor: KeyguardInteractor
- private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
- private lateinit var testScope: TestScope
private lateinit var underTest: BurnInInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- configurationRepository = FakeConfigurationRepository()
context
.getOrCreateTestableResources()
.addOverride(R.dimen.burn_in_prevention_offset_y, burnInOffset)
-
- KeyguardInteractorFactory.create().let {
- keyguardInteractor = it.keyguardInteractor
- fakeKeyguardRepository = it.repository
- }
whenever(burnInHelperWrapper.burnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset)
setBurnInProgress(.65f)
- testScope = TestScope()
underTest =
BurnInInteractor(
context,
burnInHelperWrapper,
- testScope.backgroundScope,
- configurationRepository,
- keyguardInteractor,
+ kosmos.applicationCoroutineScope,
+ kosmos.configurationInteractor,
+ kosmos.keyguardInteractor,
)
}
@@ -122,7 +119,13 @@ class BurnInInteractorTest : SysuiTestCase() {
testScope.runTest {
whenever(burnInHelperWrapper.burnInScale()).thenReturn(0.5f)
- val burnInModel by collectLastValue(underTest.keyguardBurnIn)
+ val burnInModel by
+ collectLastValue(
+ underTest.burnIn(
+ xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
+ yDimenResourceId = R.dimen.burn_in_prevention_offset_y
+ )
+ )
// After time tick, returns the configured values
fakeKeyguardRepository.dozeTimeTick(10)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 87eee1a8d817..0a29821c0660 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -22,23 +22,25 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.util.BurnInHelperWrapper
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -53,18 +55,19 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class UdfpsKeyguardInteractorTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+ val testScope = kosmos.testScope
+ val configRepository = kosmos.fakeConfigurationRepository
+ val keyguardRepository = kosmos.fakeKeyguardRepository
+
private val burnInProgress = 1f
private val burnInYOffset = 20
private val burnInXOffset = 10
- private lateinit var testScope: TestScope
- private lateinit var configRepository: FakeConfigurationRepository
private lateinit var bouncerRepository: KeyguardBouncerRepository
- private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var fakeCommandQueue: FakeCommandQueue
private lateinit var burnInInteractor: BurnInInteractor
private lateinit var shadeRepository: FakeShadeRepository
- private lateinit var keyguardInteractor: KeyguardInteractor
private lateinit var powerInteractor: PowerInteractor
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
@@ -75,12 +78,6 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- testScope = TestScope()
- configRepository = FakeConfigurationRepository()
- KeyguardInteractorFactory.create().let {
- keyguardInteractor = it.keyguardInteractor
- keyguardRepository = it.repository
- }
bouncerRepository = FakeKeyguardBouncerRepository()
shadeRepository = FakeShadeRepository()
fakeCommandQueue = FakeCommandQueue()
@@ -89,8 +86,8 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
context,
burnInHelper,
testScope.backgroundScope,
- configRepository,
- keyguardInteractor
+ kosmos.configurationInteractor,
+ kosmos.keyguardInteractor
)
powerInteractor = PowerInteractorFactory.create().powerInteractor
@@ -98,7 +95,7 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
UdfpsKeyguardInteractor(
configRepository,
burnInInteractor,
- keyguardInteractor,
+ kosmos.keyguardInteractor,
shadeRepository,
dialogManager,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index ca403e0addec..695d3b2c6c78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -19,7 +19,6 @@ package com.android.systemui.media.dialog;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -48,7 +47,6 @@ import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
-import com.android.settingslib.media.LocalMediaManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
@@ -129,12 +127,6 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
- // Using a fake package will cause routing operations to fail, so we intercept
- // scanning-related operations.
- mMediaOutputController.mLocalMediaManager = mock(LocalMediaManager.class);
- doNothing().when(mMediaOutputController.mLocalMediaManager).startScan();
- doNothing().when(mMediaOutputController.mLocalMediaManager).stopScan();
-
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mBroadcastSender,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index acbf9976873a..cdff4d1c6561 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -461,7 +461,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mKeyguardLogger,
mInteractionJankMonitor,
mKeyguardInteractor,
- mKeyguardTransitionInteractor,
mDumpManager,
mPowerInteractor));
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 793e2d7efcda..1e305d67d40d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.data.repository
import android.graphics.Point
-import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
@@ -58,9 +57,6 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
private val _bottomAreaAlpha = MutableStateFlow(1f)
override val bottomAreaAlpha: StateFlow<Float> = _bottomAreaAlpha
- private val _clockPosition = MutableStateFlow(Position(0, 0))
- override val clockPosition: StateFlow<Position> = _clockPosition
-
private val _isKeyguardShowing = MutableStateFlow(false)
override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
@@ -149,10 +145,6 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
_bottomAreaAlpha.value = alpha
}
- override fun setClockPosition(x: Int, y: Int) {
- _clockPosition.value = Position(x, y)
- }
-
fun setKeyguardShowing(isShowing: Boolean) {
_isKeyguardShowing.value = isShowing
}
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
index a9d89a37c542..40131c772de7 100644
--- 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
@@ -19,7 +19,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.content.applicationContext
-import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.doze.util.burnInHelperWrapper
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -31,7 +31,7 @@ var Kosmos.burnInInteractor by Fixture {
context = applicationContext,
burnInHelperWrapper = burnInHelperWrapper,
scope = applicationCoroutineScope,
- configurationRepository = configurationRepository,
+ configurationInteractor = configurationInteractor,
keyguardInteractor = keyguardInteractor,
)
}
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
index 75e3ac24e381..a863edfc5198 100644
--- 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
@@ -23,6 +23,7 @@ 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.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
@@ -31,6 +32,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.keyguardRootViewModel by Fixture {
KeyguardRootViewModel(
+ scope = applicationCoroutineScope,
deviceEntryInteractor = deviceEntryInteractor,
dozeParameters = dozeParameters,
keyguardInteractor = keyguardInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/data/repository/FakeSpatializerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/data/repository/FakeSpatializerRepository.kt
index 0183b97090dc..63e2d4b5ed24 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/data/repository/FakeSpatializerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/data/repository/FakeSpatializerRepository.kt
@@ -18,23 +18,27 @@ package com.android.systemui.media.data.repository
import android.media.AudioDeviceAttributes
import com.android.settingslib.media.data.repository.SpatializerRepository
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
class FakeSpatializerRepository : SpatializerRepository {
var defaultSpatialAudioAvailable: Boolean = false
+ var defaultHeadTrackingAvailable: Boolean = false
private val spatialAudioAvailabilityByDevice: MutableMap<AudioDeviceAttributes, Boolean> =
mutableMapOf()
+ private val headTrackingAvailabilityByDevice: MutableMap<AudioDeviceAttributes, Boolean> =
+ mutableMapOf()
private val spatialAudioCompatibleDevices: MutableList<AudioDeviceAttributes> = mutableListOf()
- private val mutableHeadTrackingAvailable = MutableStateFlow(false)
private val headTrackingEnabledByDevice = mutableMapOf<AudioDeviceAttributes, Boolean>()
- override val isHeadTrackingAvailable: StateFlow<Boolean> =
- mutableHeadTrackingAvailable.asStateFlow()
+ override suspend fun isHeadTrackingAvailableForDevice(
+ audioDeviceAttributes: AudioDeviceAttributes
+ ): Boolean =
+ headTrackingAvailabilityByDevice.getOrDefault(
+ audioDeviceAttributes,
+ defaultHeadTrackingAvailable
+ )
override suspend fun isSpatialAudioAvailableForDevice(
audioDeviceAttributes: AudioDeviceAttributes
@@ -77,7 +81,10 @@ class FakeSpatializerRepository : SpatializerRepository {
spatialAudioAvailabilityByDevice[audioDeviceAttributes] = isAvailable
}
- fun setIsHeadTrackingAvailable(isAvailable: Boolean) {
- mutableHeadTrackingAvailable.value = isAvailable
+ fun setIsHeadTrackingAvailable(
+ audioDeviceAttributes: AudioDeviceAttributes,
+ isAvailable: Boolean,
+ ) {
+ headTrackingAvailabilityByDevice[audioDeviceAttributes] = isAvailable
}
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index b2a738fd352b..3d95fee84f2a 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -403,15 +403,14 @@ public class AutomaticBrightnessController {
brightnessEvent.setRecommendedBrightness(mScreenAutoBrightness);
brightnessEvent.setFlags(brightnessEvent.getFlags()
| (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0)
- | (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE
- ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
+ | (shouldApplyDozeScaleFactor() ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
brightnessEvent.setAutoBrightnessMode(getMode());
}
if (!mAmbientLuxValid) {
return PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
- if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) {
+ if (shouldApplyDozeScaleFactor()) {
return mScreenAutoBrightness * mDozeScaleFactor;
}
return mScreenAutoBrightness;
@@ -434,7 +433,7 @@ public class AutomaticBrightnessController {
float brightness = mCurrentBrightnessMapper.getBrightness(mLastObservedLux,
mForegroundAppPackageName, mForegroundAppCategory);
- if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) {
+ if (shouldApplyDozeScaleFactor()) {
brightness *= mDozeScaleFactor;
}
@@ -443,8 +442,7 @@ public class AutomaticBrightnessController {
brightnessEvent.setRecommendedBrightness(brightness);
brightnessEvent.setFlags(brightnessEvent.getFlags()
| (mLastObservedLux == INVALID_LUX ? BrightnessEvent.FLAG_INVALID_LUX : 0)
- | (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE
- ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
+ | (shouldApplyDozeScaleFactor() ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
brightnessEvent.setAutoBrightnessMode(getMode());
}
return brightness;
@@ -1263,6 +1261,12 @@ public class AutomaticBrightnessController {
}
}
+ private boolean shouldApplyDozeScaleFactor() {
+ // Don't apply the doze scale factor if we have a designated brightness curve for doze
+ return mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE
+ && getMode() != AUTO_BRIGHTNESS_MODE_DOZE;
+ }
+
private class ShortTermModel {
// When the short term model is invalidated, we don't necessarily reset it (i.e. clear the
// user's adjustment) immediately, but wait for a drastic enough change in the ambient
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index 91e560e19f0d..b1defe94436a 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -113,4 +113,14 @@ public class DisplayOffloadSessionImpl implements DisplayManagerInternal.Display
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
+
+ @Override
+ public float getBrightness() {
+ return mDisplayPowerController.getScreenBrightnessSetting();
+ }
+
+ @Override
+ public float getDozeBrightness() {
+ return mDisplayPowerController.getDozeBrightnessForOffload();
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 7f014f6dae28..77a43d08506b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -487,6 +487,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private DisplayOffloadSession mDisplayOffloadSession;
+ // Used to scale the brightness in doze mode
+ private float mDozeScaleFactor;
+
/**
* Creates the display power controller.
*/
@@ -547,6 +550,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
loadBrightnessRampRates();
mSkipScreenOnBrightnessRamp = resources.getBoolean(
R.bool.config_skipScreenOnBrightnessRamp);
+ mDozeScaleFactor = context.getResources().getFraction(
+ R.fraction.config_screenAutoBrightnessDozeScaleFactor,
+ 1, 1);
Runnable modeChangeCallback = () -> {
sendUpdatePowerState();
@@ -1042,10 +1048,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
if (defaultModeBrightnessMapper != null) {
- final float dozeScaleFactor = context.getResources().getFraction(
- R.fraction.config_screenAutoBrightnessDozeScaleFactor,
- 1, 1);
-
// Ambient Lux - Active Mode Brightness Thresholds
float[] ambientBrighteningThresholds =
mDisplayDeviceConfig.getAmbientBrighteningPercentages();
@@ -1156,7 +1158,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mAutomaticBrightnessController = mInjector.getAutomaticBrightnessController(
this, handler.getLooper(), mSensorManager, mLightSensor,
brightnessMappers, lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate,
+ PowerManager.BRIGHTNESS_MAX, mDozeScaleFactor, lightSensorRate,
initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
brighteningLightDebounceIdle, darkeningLightDebounceIdle,
autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
@@ -1473,17 +1475,22 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false);
}
- // If there's an offload session and auto-brightness is on, we need to set the initial doze
- // brightness using the doze auto-brightness curve before the offload session starts
- // controlling the brightness.
- if (Float.isNaN(brightnessState) && mFlags.areAutoBrightnessModesEnabled()
- && mFlags.isDisplayOffloadEnabled()
- && mPowerRequest.policy == POLICY_DOZE
- && mDisplayOffloadSession != null
- && mAutomaticBrightnessController != null
- && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
- rawBrightnessState = mAutomaticBrightnessController
- .getAutomaticScreenBrightnessBasedOnLastObservedLux(mTempBrightnessEvent);
+ // If there's an offload session, we need to set the initial doze brightness before
+ // the offload session starts controlling the brightness.
+ if (Float.isNaN(brightnessState) && mFlags.isDisplayOffloadEnabled()
+ && mPowerRequest.policy == POLICY_DOZE && mDisplayOffloadSession != null) {
+ if (mAutomaticBrightnessController != null
+ && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
+ // Use the auto-brightness curve and the last observed lux
+ rawBrightnessState = mAutomaticBrightnessController
+ .getAutomaticScreenBrightnessBasedOnLastObservedLux(
+ mTempBrightnessEvent);
+ } else {
+ rawBrightnessState = getDozeBrightnessForOffload();
+ mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
+ | BrightnessEvent.FLAG_DOZE_SCALE);
+ }
+
if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) {
brightnessState = clampScreenBrightness(rawBrightnessState);
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL);
@@ -2444,6 +2451,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
@Override
+ public float getDozeBrightnessForOffload() {
+ return mDisplayBrightnessController.getCurrentBrightness() * mDozeScaleFactor;
+ }
+
+ @Override
public void setBrightness(float brightness) {
mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness));
}
@@ -2596,6 +2608,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
+ pw.println(" mDozeScaleFactor=" + mDozeScaleFactor);
mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index ecf1635a0cd2..408d610dea91 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -161,6 +161,11 @@ public interface DisplayPowerControllerInterface {
float getScreenBrightnessSetting();
/**
+ * Gets the brightness value used when the device is in doze
+ */
+ float getDozeBrightnessForOffload();
+
+ /**
* Sets up the temporary brightness for the associated display
*/
void setTemporaryBrightness(float brightness);
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 71cc8725f85b..03fb1478022e 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -71,8 +71,14 @@ public class DisplayBrightnessStrategySelector {
@Nullable
private final OffloadBrightnessStrategy mOffloadBrightnessStrategy;
+ // A collective representation of all the strategies that the selector is aware of. This is
+ // non null, but the strategies this is tracking can be null
+ @NonNull
private final DisplayBrightnessStrategy[] mDisplayBrightnessStrategies;
+ @NonNull
+ private final DisplayManagerFlags mDisplayManagerFlags;
+
// We take note of the old brightness strategy so that we can know when the strategy changes.
private String mOldBrightnessStrategyName;
@@ -86,6 +92,7 @@ public class DisplayBrightnessStrategySelector {
if (injector == null) {
injector = new Injector();
}
+ mDisplayManagerFlags = flags;
mDisplayId = displayId;
mDozeBrightnessStrategy = injector.getDozeBrightnessStrategy();
mScreenOffBrightnessStrategy = injector.getScreenOffBrightnessStrategy();
@@ -139,6 +146,10 @@ public class DisplayBrightnessStrategySelector {
displayBrightnessStrategy = mOffloadBrightnessStrategy;
}
+ if (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) {
+ postProcess(constructStrategySelectionNotifyRequest(displayBrightnessStrategy));
+ }
+
if (!mOldBrightnessStrategyName.equals(displayBrightnessStrategy.getName())) {
Slog.i(TAG,
"Changing the DisplayBrightnessStrategy from " + mOldBrightnessStrategyName
@@ -186,13 +197,27 @@ public class DisplayBrightnessStrategySelector {
" mAllowAutoBrightnessWhileDozingConfig= "
+ mAllowAutoBrightnessWhileDozingConfig);
IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
- for (DisplayBrightnessStrategy displayBrightnessStrategy: mDisplayBrightnessStrategies) {
+ for (DisplayBrightnessStrategy displayBrightnessStrategy : mDisplayBrightnessStrategies) {
if (displayBrightnessStrategy != null) {
displayBrightnessStrategy.dump(ipw);
}
}
}
+ private StrategySelectionNotifyRequest constructStrategySelectionNotifyRequest(
+ DisplayBrightnessStrategy selectedDisplayBrightnessStrategy) {
+ return new StrategySelectionNotifyRequest(selectedDisplayBrightnessStrategy);
+ }
+
+ private void postProcess(StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+ for (DisplayBrightnessStrategy displayBrightnessStrategy : mDisplayBrightnessStrategies) {
+ if (displayBrightnessStrategy != null) {
+ displayBrightnessStrategy.strategySelectionPostProcessor(
+ strategySelectionNotifyRequest);
+ }
+ }
+ }
+
/**
* Validates if the conditions are met to qualify for the DozeBrightnessStrategy.
*/
diff --git a/services/core/java/com/android/server/display/brightness/StrategySelectionNotifyRequest.java b/services/core/java/com/android/server/display/brightness/StrategySelectionNotifyRequest.java
new file mode 100644
index 000000000000..d8bd2e459730
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/StrategySelectionNotifyRequest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness;
+
+import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
+
+import java.util.Objects;
+
+/**
+ * A wrapper class to encapsulate the request to notify the strategies about the selection of a
+ * DisplayBrightnessStrategy
+ */
+public final class StrategySelectionNotifyRequest {
+ // The strategy that was selected with the current request
+ private final DisplayBrightnessStrategy mSelectedDisplayBrightnessStrategy;
+
+ public StrategySelectionNotifyRequest(DisplayBrightnessStrategy displayBrightnessStrategy) {
+ mSelectedDisplayBrightnessStrategy = displayBrightnessStrategy;
+ }
+
+ public DisplayBrightnessStrategy getSelectedDisplayBrightnessStrategy() {
+ return mSelectedDisplayBrightnessStrategy;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof StrategySelectionNotifyRequest)) {
+ return false;
+ }
+ StrategySelectionNotifyRequest other = (StrategySelectionNotifyRequest) obj;
+ return other.getSelectedDisplayBrightnessStrategy()
+ == getSelectedDisplayBrightnessStrategy();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSelectedDisplayBrightnessStrategy);
+ }
+}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/BoostBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/BoostBrightnessStrategy.java
index 9ee1d73726bd..11edde9caaf7 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/BoostBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/BoostBrightnessStrategy.java
@@ -22,6 +22,7 @@ import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.BrightnessUtils;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
import java.io.PrintWriter;
@@ -53,4 +54,10 @@ public class BoostBrightnessStrategy implements DisplayBrightnessStrategy {
@Override
public void dump(PrintWriter writer) {}
+
+ @Override
+ public void strategySelectionPostProcessor(
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+ // DO NOTHING
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategy.java
index 1f28eb4fa113..7b49957a7e31 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategy.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.hardware.display.DisplayManagerInternal;
import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
import java.io.PrintWriter;
@@ -48,4 +49,10 @@ public interface DisplayBrightnessStrategy {
* @param writer
*/
void dump(PrintWriter writer);
+
+ /**
+ * Notifies this strategy about the selection of a DisplayBrightnessStrategy
+ */
+ void strategySelectionPostProcessor(
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest);
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/DozeBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/DozeBrightnessStrategy.java
index 2be74438f87a..5afdc42b9eee 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/DozeBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/DozeBrightnessStrategy.java
@@ -21,6 +21,7 @@ import android.hardware.display.DisplayManagerInternal;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.BrightnessUtils;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
import java.io.PrintWriter;
@@ -46,4 +47,10 @@ public class DozeBrightnessStrategy implements DisplayBrightnessStrategy {
@Override
public void dump(PrintWriter writer) {}
+
+ @Override
+ public void strategySelectionPostProcessor(
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+ // DO NOTHING
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
index 54f9afcbdd94..0650c1c9dc62 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
@@ -22,6 +22,7 @@ import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.BrightnessUtils;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
import java.io.PrintWriter;
@@ -82,4 +83,10 @@ public class FollowerBrightnessStrategy implements DisplayBrightnessStrategy {
writer.println(" mBrightnessToFollow:" + mBrightnessToFollow);
writer.println(" mBrightnessToFollowSlowChange:" + mBrightnessToFollowSlowChange);
}
+
+ @Override
+ public void strategySelectionPostProcessor(
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+ // DO NOTHING
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/InvalidBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/InvalidBrightnessStrategy.java
index 49c3e03c8742..bf37ee0a9666 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/InvalidBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/InvalidBrightnessStrategy.java
@@ -22,6 +22,7 @@ import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.BrightnessUtils;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
import java.io.PrintWriter;
@@ -44,4 +45,10 @@ public class InvalidBrightnessStrategy implements DisplayBrightnessStrategy {
@Override
public void dump(PrintWriter writer) {}
+
+ @Override
+ public void strategySelectionPostProcessor(
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+ // DO NOTHING
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java
index 4ffb16be5789..d2bb1e284256 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/OffloadBrightnessStrategy.java
@@ -21,6 +21,7 @@ import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
import java.io.PrintWriter;
@@ -72,4 +73,10 @@ public class OffloadBrightnessStrategy implements DisplayBrightnessStrategy {
writer.println("OffloadBrightnessStrategy:");
writer.println(" mOffloadScreenBrightness:" + mOffloadScreenBrightness);
}
+
+ @Override
+ public void strategySelectionPostProcessor(
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+ // DO NOTHING
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
index 7b651d8ced8e..653170c886ea 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/OverrideBrightnessStrategy.java
@@ -21,6 +21,7 @@ import android.hardware.display.DisplayManagerInternal;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.BrightnessUtils;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
import java.io.PrintWriter;
@@ -45,4 +46,10 @@ public class OverrideBrightnessStrategy implements DisplayBrightnessStrategy {
@Override
public void dump(PrintWriter writer) {}
+
+ @Override
+ public void strategySelectionPostProcessor(
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+ // DO NOTHING
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategy.java
index 201ef4118854..f0cce23c6f45 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategy.java
@@ -22,6 +22,7 @@ import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.BrightnessUtils;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
import java.io.PrintWriter;
@@ -46,4 +47,10 @@ public class ScreenOffBrightnessStrategy implements DisplayBrightnessStrategy {
@Override
public void dump(PrintWriter writer) {}
+
+ @Override
+ public void strategySelectionPostProcessor(
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+ // DO NOTHING
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategy.java
index bbd0c009debb..91e1d091ade3 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategy.java
@@ -22,6 +22,7 @@ import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.BrightnessUtils;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
import java.io.PrintWriter;
@@ -73,4 +74,10 @@ public class TemporaryBrightnessStrategy implements DisplayBrightnessStrategy {
writer.println("TemporaryBrightnessStrategy:");
writer.println(" mTemporaryScreenBrightness:" + mTemporaryScreenBrightness);
}
+
+ @Override
+ public void strategySelectionPostProcessor(
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+ // DO NOTHING
+ }
}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 516d4b1d4125..3c98ee453913 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -126,6 +126,13 @@ public class DisplayManagerFlags {
Flags::sensorBasedBrightnessThrottling
);
+
+ private final FlagState mRefactorDisplayPowerController = new FlagState(
+ Flags.FLAG_REFACTOR_DISPLAY_POWER_CONTROLLER,
+ Flags::refactorDisplayPowerController
+ );
+
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -256,6 +263,10 @@ public class DisplayManagerFlags {
return mSensorBasedBrightnessThrottling.isEnabled();
}
+ public boolean isRefactorDisplayPowerControllerEnabled() {
+ return mRefactorDisplayPowerController.isEnabled();
+ }
+
/**
* dumps all flagstates
* @param pw printWriter
@@ -280,6 +291,7 @@ public class DisplayManagerFlags {
pw.println(" " + mFastHdrTransitions);
pw.println(" " + mRefreshRateVotingTelemetry);
pw.println(" " + mSensorBasedBrightnessThrottling);
+ pw.println(" " + mRefactorDisplayPowerController);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 63ab3a95822f..34045273c93a 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -192,3 +192,11 @@ flag {
bug: "294900859"
is_fixed_read_only: true
}
+
+flag {
+ name: "refactor_display_power_controller"
+ namespace: "display_manager"
+ description: "Feature flag for refactoring the DisplayPowerController and associated components"
+ bug: "294444204"
+ is_fixed_read_only: true
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 9c9aeeaadb71..705dac54fdd6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -19,6 +19,7 @@ package com.android.server.display;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
import static org.junit.Assert.assertArrayEquals;
@@ -72,7 +73,7 @@ public class AutomaticBrightnessControllerTest {
private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 4000;
private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 1000;
private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 2000;
- private static final float DOZE_SCALE_FACTOR = 0.0f;
+ private static final float DOZE_SCALE_FACTOR = 0.54f;
private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
private static final int AMBIENT_LIGHT_HORIZON_SHORT = 1000;
@@ -87,6 +88,7 @@ public class AutomaticBrightnessControllerTest {
@Mock SensorManager mSensorManager;
@Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
@Mock BrightnessMappingStrategy mIdleBrightnessMappingStrategy;
+ @Mock BrightnessMappingStrategy mDozeBrightnessMappingStrategy;
@Mock HysteresisLevels mAmbientBrightnessThresholds;
@Mock HysteresisLevels mScreenBrightnessThresholds;
@Mock HysteresisLevels mAmbientBrightnessThresholdsIdle;
@@ -124,12 +126,15 @@ public class AutomaticBrightnessControllerTest {
when(mBrightnessMappingStrategy.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_DEFAULT);
when(mIdleBrightnessMappingStrategy.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_IDLE);
+ when(mDozeBrightnessMappingStrategy.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_DOZE);
SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap = new SparseArray<>();
brightnessMappingStrategyMap.append(AUTO_BRIGHTNESS_MODE_DEFAULT,
mBrightnessMappingStrategy);
brightnessMappingStrategyMap.append(AUTO_BRIGHTNESS_MODE_IDLE,
mIdleBrightnessMappingStrategy);
+ brightnessMappingStrategyMap.append(AUTO_BRIGHTNESS_MODE_DOZE,
+ mDozeBrightnessMappingStrategy);
mController = new AutomaticBrightnessController(
new AutomaticBrightnessController.Injector() {
@Override
@@ -1032,10 +1037,9 @@ public class AutomaticBrightnessControllerTest {
float normalizedBrightness = 0.3f;
when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
- when(mBrightnessMappingStrategy.getBrightness(eq(lux), eq(null), anyInt()))
- .thenReturn(normalizedBrightness);
+ when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
+ /* category= */ anyInt())).thenReturn(normalizedBrightness);
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
- when(mBrightnessThrottler.isThrottled()).thenReturn(true);
// Send a new sensor value, disable the sensor and verify
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
@@ -1047,4 +1051,79 @@ public class AutomaticBrightnessControllerTest {
mController.getAutomaticScreenBrightnessBasedOnLastObservedLux(
/* brightnessEvent= */ null), EPSILON);
}
+
+ @Test
+ public void testAutoBrightnessInDoze_ShouldScaleIfNotUsingDozeCurve() throws Exception {
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Set up system to return 0.3f as a brightness value
+ float lux = 100.0f;
+ // Brightness as float (from 0.0f to 1.0f)
+ float normalizedBrightness = 0.3f;
+ when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
+ when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
+ when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
+ /* category= */ anyInt())).thenReturn(normalizedBrightness);
+ when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+
+ // Set policy to DOZE
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
+ /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
+ /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE,
+ /* shouldResetShortTermModel= */ true);
+
+ // Send a new sensor value
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+
+ // The brightness should be scaled by the doze factor
+ assertEquals(normalizedBrightness * DOZE_SCALE_FACTOR,
+ mController.getAutomaticScreenBrightness(
+ /* brightnessEvent= */ null), EPSILON);
+ assertEquals(normalizedBrightness * DOZE_SCALE_FACTOR,
+ mController.getAutomaticScreenBrightnessBasedOnLastObservedLux(
+ /* brightnessEvent= */ null), EPSILON);
+ }
+
+ @Test
+ public void testAutoBrightnessInDoze_ShouldNotScaleIfUsingDozeCurve() throws Exception {
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Set up system to return 0.3f as a brightness value
+ float lux = 100.0f;
+ // Brightness as float (from 0.0f to 1.0f)
+ float normalizedBrightness = 0.3f;
+ when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
+ when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
+ when(mDozeBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
+ /* category= */ anyInt())).thenReturn(normalizedBrightness);
+ when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+
+ // Switch mode to DOZE
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+
+ // Set policy to DOZE
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
+ /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
+ /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE,
+ /* shouldResetShortTermModel= */ true);
+
+ // Send a new sensor value
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+
+ // The brightness should not be scaled by the doze factor
+ assertEquals(normalizedBrightness,
+ mController.getAutomaticScreenBrightness(
+ /* brightnessEvent= */ null), EPSILON);
+ assertEquals(normalizedBrightness,
+ mController.getAutomaticScreenBrightnessBasedOnLastObservedLux(
+ /* brightnessEvent= */ null), EPSILON);
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 14d8a9c5f0f2..76b77808b880 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -114,6 +114,8 @@ public final class DisplayPowerControllerTest {
private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1;
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
+ private static final float DOZE_SCALE_FACTOR = 0.34f;
+
private static final float BRIGHTNESS_RAMP_RATE_MINIMUM = 0.0f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f;
private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f;
@@ -193,6 +195,9 @@ public final class DisplayPowerControllerTest {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_displayColorFadeDisabled, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
+ DOZE_SCALE_FACTOR);
doAnswer((Answer<Void>) invocationOnMock -> null).when(() ->
SystemProperties.set(anyString(), any()));
@@ -1705,7 +1710,7 @@ public final class DisplayPowerControllerTest {
}
@Test
- public void testInitialDozeBrightness() {
+ public void testInitialDozeBrightness_AutoBrightnessEnabled() {
when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
@@ -1734,6 +1739,39 @@ public final class DisplayPowerControllerTest {
}
@Test
+ public void testInitialDozeBrightness_AutoBrightnessDisabled() {
+ when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+ float brightness = 0.277f;
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
+ when(mHolder.hbmController.getCurrentBrightnessMax())
+ .thenReturn(PowerManager.BRIGHTNESS_MAX);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState, initialize
+
+ ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+ ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+ listener.onBrightnessChanged(brightness);
+ advanceTime(1); // Send messages, run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(brightness * DOZE_SCALE_FACTOR),
+ /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
+ /* ignoreAnimationLimits= */ anyBoolean());
+ assertEquals(brightness * DOZE_SCALE_FACTOR, mHolder.dpc.getDozeBrightnessForOffload(),
+ /* delta= */ 0);
+ }
+
+ @Test
public void testInitialDozeBrightness_AbcIsNull() {
when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index 0e89d8383a8f..a3728c868a4e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -18,8 +18,10 @@ package com.android.server.display.brightness;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
@@ -268,4 +270,33 @@ public final class DisplayBrightnessStrategySelectorTest {
mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest,
Display.STATE_ON));
}
+
+ @Test
+ public void selectStrategyCallsPostProcessorForAllStrategies() {
+ when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
+ mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+ mInjector, DISPLAY_ID, mDisplayManagerFlags);
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+ DisplayManagerInternal.DisplayPowerRequest.class);
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(0.3f);
+
+ mDisplayBrightnessStrategySelector.selectStrategy(displayPowerRequest, Display.STATE_ON);
+
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest =
+ new StrategySelectionNotifyRequest(mFollowerBrightnessStrategy);
+ verify(mInvalidBrightnessStrategy).strategySelectionPostProcessor(
+ eq(strategySelectionNotifyRequest));
+ verify(mScreenOffBrightnessModeStrategy).strategySelectionPostProcessor(
+ eq(strategySelectionNotifyRequest));
+ verify(mDozeBrightnessModeStrategy).strategySelectionPostProcessor(
+ eq(strategySelectionNotifyRequest));
+ verify(mFollowerBrightnessStrategy).strategySelectionPostProcessor(
+ eq(strategySelectionNotifyRequest));
+ verify(mBoostBrightnessStrategy).strategySelectionPostProcessor(
+ eq(strategySelectionNotifyRequest));
+ verify(mOverrideBrightnessStrategy).strategySelectionPostProcessor(
+ eq(strategySelectionNotifyRequest));
+ verify(mTemporaryBrightnessStrategy).strategySelectionPostProcessor(
+ eq(strategySelectionNotifyRequest));
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
index 6ac56bfc254a..4ac4484956fc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "RollbackPackageHealthObserverTests",
"options": [