summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/biometrics/BiometricFingerprintConstants.java17
-rw-r--r--core/java/android/os/VibrationEffect.java32
-rw-r--r--core/java/android/view/ViewRootImpl.java6
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java94
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java6
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java77
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt99
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java60
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java13
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java73
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java74
-rw-r--r--packages/SystemUI/compose/gallery/res/drawable/kitten1.jpegbin0 -> 4097 bytes
-rw-r--r--packages/SystemUI/compose/gallery/res/drawable/kitten2.jpegbin0 -> 4875 bytes
-rw-r--r--packages/SystemUI/compose/gallery/res/drawable/kitten3.jpegbin0 -> 6268 bytes
-rw-r--r--packages/SystemUI/compose/gallery/res/drawable/kitten4.jpegbin0 -> 5212 bytes
-rw-r--r--packages/SystemUI/compose/gallery/res/drawable/kitten5.jpegbin0 -> 5339 bytes
-rw-r--r--packages/SystemUI/compose/gallery/res/drawable/kitten6.jpegbin0 -> 6109 bytes
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt65
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/UserSwitcherScreen.kt35
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt21
-rw-r--r--packages/SystemUI/ktfmt_includes.txt1
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_off.xml109
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_on.xml109
-rw-r--r--packages/SystemUI/res/drawable/qs_airplane_icon_off.xml66
-rw-r--r--packages/SystemUI/res/drawable/qs_airplane_icon_on.xml253
-rw-r--r--packages/SystemUI/res/drawable/qs_data_saver_icon_off.xml84
-rw-r--r--packages/SystemUI/res/drawable/qs_data_saver_icon_on.xml110
-rw-r--r--packages/SystemUI/res/drawable/qs_extra_dim_icon_off.xml197
-rw-r--r--packages/SystemUI/res/drawable/qs_extra_dim_icon_on.xml197
-rw-r--r--packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml84
-rw-r--r--packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml84
-rw-r--r--packages/SystemUI/res/drawable/qs_nightlight_icon_off.xml94
-rw-r--r--packages/SystemUI/res/drawable/qs_nightlight_icon_on.xml94
-rw-r--r--packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml218
-rw-r--r--packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml286
-rw-r--r--packages/SystemUI/res/values/colors.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/drawable/CircularDrawable.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextHelper.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextView.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java137
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt132
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java3
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java10
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java6
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java45
-rw-r--r--services/core/java/com/android/server/wm/AsyncRotationController.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java7
-rw-r--r--services/core/jni/gnss/Utils.cpp2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java101
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java2
-rw-r--r--telephony/java/android/telephony/AnomalyReporter.java10
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java14
137 files changed, 4510 insertions, 836 deletions
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index c59d7571ee6d..257ad7162e9e 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -315,17 +315,17 @@ public interface BiometricFingerprintConstants {
int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
/**
- * Whether the FingerprintAcquired message is a signal to turn off HBM
+ * Whether the FingerprintAcquired message is a signal to disable the UDFPS display mode.
+ * We want to disable the UDFPS mode as soon as possible to conserve power and provide better
+ * UX. For example, prolonged high-brightness illumination of optical sensors can be unpleasant
+ * to the user, can cause long term display burn-in, and can drain the battery faster.
*/
- static boolean shouldTurnOffHbm(@FingerprintAcquired int acquiredInfo) {
+ static boolean shouldDisableUdfpsDisplayMode(@FingerprintAcquired int acquiredInfo) {
switch (acquiredInfo) {
case FINGERPRINT_ACQUIRED_START:
- // Authentication just began
+ // Keep the UDFPS mode because the authentication just began.
return false;
case FINGERPRINT_ACQUIRED_GOOD:
- // Good image captured. Turn off HBM. Success/Reject comes after, which is when
- // hideUdfpsOverlay will be called.
- return true;
case FINGERPRINT_ACQUIRED_PARTIAL:
case FINGERPRINT_ACQUIRED_INSUFFICIENT:
case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
@@ -334,11 +334,12 @@ public interface BiometricFingerprintConstants {
case FINGERPRINT_ACQUIRED_IMMOBILE:
case FINGERPRINT_ACQUIRED_TOO_BRIGHT:
case FINGERPRINT_ACQUIRED_VENDOR:
- // Bad image captured. Turn off HBM. Matcher will not run, so there's no need to
- // keep HBM on.
+ // Disable the UDFPS mode because the image capture has finished. The overlay
+ // can be hidden later, once the authentication result arrives.
return true;
case FINGERPRINT_ACQUIRED_UNKNOWN:
default:
+ // Keep the UDFPS mode in case of an unknown message.
return false;
}
}
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index ec1c57d675c1..f7d4be3960f8 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -830,29 +830,8 @@ public abstract class VibrationEffect implements Parcelable {
* .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1.0f, 100)
* .compose();}</pre>
*
- * <p>Composition elements can also be {@link VibrationEffect} instances, including other
- * compositions, and off durations, which are periods of time when the vibrator will be
- * turned off. Here is an example of a composition that "warms up" with a light tap,
- * a stronger double tap, then repeats a vibration pattern indefinitely:
- *
- * <pre>
- * {@code VibrationEffect repeatingEffect = VibrationEffect.startComposition()
- * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
- * .addOffDuration(Duration.ofMillis(10))
- * .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK))
- * .addOffDuration(Duration.ofMillis(50))
- * .addEffect(VibrationEffect.createWaveform(pattern, repeatIndex))
- * .compose();}</pre>
- *
* <p>When choosing to play a composed effect, you should check that individual components are
- * supported by the device by using the appropriate vibrator method:
- *
- * <ul>
- * <li>Primitive support can be checked using {@link Vibrator#arePrimitivesSupported}.
- * <li>Effect support can be checked using {@link Vibrator#areEffectsSupported}.
- * <li>Amplitude control for one-shot and waveforms with amplitude values can be checked
- * using {@link Vibrator#hasAmplitudeControl}.
- * </ul>
+ * supported by the device by using {@link Vibrator#arePrimitivesSupported}.
*
* @see VibrationEffect#startComposition()
*/
@@ -1021,9 +1000,6 @@ public abstract class VibrationEffect implements Parcelable {
*
* @param primitiveId The primitive to add
* @return This {@link Composition} object to enable adding multiple elements in one chain.
- *
- * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
- * ending with a repeating effect.
*/
@NonNull
public Composition addPrimitive(@PrimitiveType int primitiveId) {
@@ -1038,9 +1014,6 @@ public abstract class VibrationEffect implements Parcelable {
* @param primitiveId The primitive to add
* @param scale The scale to apply to the intensity of the primitive.
* @return This {@link Composition} object to enable adding multiple elements in one chain.
- *
- * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
- * ending with a repeating effect.
*/
@NonNull
public Composition addPrimitive(@PrimitiveType int primitiveId,
@@ -1056,9 +1029,6 @@ public abstract class VibrationEffect implements Parcelable {
* @param delay The amount of time in milliseconds to wait before playing this primitive,
* starting at the time the previous element in this composition is finished.
* @return This {@link Composition} object to enable adding multiple elements in one chain.
- *
- * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
- * ending with a repeating effect.
*/
@NonNull
public Composition addPrimitive(@PrimitiveType int primitiveId,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ec6b4acd6aff..e4caa385bc9b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3064,7 +3064,8 @@ public final class ViewRootImpl implements ViewParent,
// WindowManagerService has reported back a frame from a configuration not yet
// handled by the client. In this case, we need to accept the configuration so we
// do not lay out and draw with the wrong configuration.
- if (!mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) {
+ if (mRelayoutRequested
+ && !mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) {
if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
+ mPendingMergedConfiguration.getMergedConfiguration());
performConfigurationChange(new MergedConfiguration(mPendingMergedConfiguration),
@@ -8085,7 +8086,8 @@ public final class ViewRootImpl implements ViewParent,
final int measuredWidth = mView.getMeasuredWidth();
final int measuredHeight = mView.getMeasuredHeight();
final boolean relayoutAsync;
- if (LOCAL_LAYOUT && !mFirst && viewVisibility == mViewVisibility
+ if (LOCAL_LAYOUT
+ && (mViewFrameInfo.flags & FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED) == 0
&& mWindowAttributes.type != TYPE_APPLICATION_STARTING
&& mSyncSeqId <= mLastSyncSeqId
&& winConfigFromAm.diff(winConfigFromWm, false /* compareUndefined */) == 0) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index b0fce8f18742..d9df144fe54d 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -17,11 +17,19 @@
package com.android.internal.os;
import android.annotation.NonNull;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.BatteryStats;
+import android.os.BatteryStats.BitDescription;
+import android.os.BatteryStats.HistoryItem;
+import android.os.BatteryStats.HistoryTag;
+import android.os.Build;
import android.os.Parcel;
+import android.os.Process;
import android.os.StatFs;
import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
@@ -105,6 +113,49 @@ public class BatteryStatsHistory {
private int mParcelIndex = 0;
/**
+ * A delegate for android.os.Trace to allow testing static calls. Due to
+ * limitations in Android Tracing (b/153319140), the delegate also records
+ * counter values in system properties which allows reading the value at the
+ * start of a tracing session. This overhead is limited to userdebug builds.
+ * On user builds, tracing still occurs but the counter value will be missing
+ * until the first change occurs.
+ */
+ @VisibleForTesting
+ public static class TraceDelegate {
+ // Note: certain tests currently run as platform_app which is not allowed
+ // to set debug system properties. To ensure that system properties are set
+ // only when allowed, we check the current UID.
+ private final boolean mShouldSetProperty =
+ Build.IS_USERDEBUG && (Process.myUid() == Process.SYSTEM_UID);
+
+ /**
+ * Returns true if trace counters should be recorded.
+ */
+ public boolean tracingEnabled() {
+ return Trace.isTagEnabled(Trace.TRACE_TAG_POWER) || mShouldSetProperty;
+ }
+
+ /**
+ * Records the counter value with the given name.
+ */
+ public void traceCounter(@NonNull String name, int value) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, name, value);
+ if (mShouldSetProperty) {
+ SystemProperties.set("debug.tracing." + name, Integer.toString(value));
+ }
+ }
+
+ /**
+ * Records an instant event (one with no duration).
+ */
+ public void traceInstantEvent(@NonNull String track, @NonNull String name) {
+ Trace.instantForTrack(Trace.TRACE_TAG_POWER, track, name);
+ }
+ }
+
+ private TraceDelegate mTracer = new TraceDelegate();
+
+ /**
* Constructor
* @param stats BatteryStatsImpl object.
* @param systemDir typically /data/system
@@ -497,4 +548,47 @@ public class BatteryStatsHistory {
}
return ret;
}
+
+ /**
+ * Writes event details into Atrace.
+ */
+ public void recordTraceEvents(int code, HistoryTag tag) {
+ if (code == HistoryItem.EVENT_NONE) return;
+ if (!mTracer.tracingEnabled()) return;
+
+ final int idx = code & HistoryItem.EVENT_TYPE_MASK;
+ final String prefix = (code & HistoryItem.EVENT_FLAG_START) != 0 ? "+" :
+ (code & HistoryItem.EVENT_FLAG_FINISH) != 0 ? "-" : "";
+
+ final String[] names = BatteryStats.HISTORY_EVENT_NAMES;
+ if (idx < 0 || idx >= BatteryStats.HISTORY_EVENT_NAMES.length) return;
+
+ final String track = "battery_stats." + names[idx];
+ final String name = prefix + names[idx] + "=" + tag.uid + ":\"" + tag.string + "\"";
+ mTracer.traceInstantEvent(track, name);
+ }
+
+ /**
+ * Writes changes to a HistoryItem state bitmap to Atrace.
+ */
+ public void recordTraceCounters(int oldval, int newval, BitDescription[] descriptions) {
+ if (!mTracer.tracingEnabled()) return;
+
+ int diff = oldval ^ newval;
+ if (diff == 0) return;
+
+ for (int i = 0; i < descriptions.length; i++) {
+ BitDescription bd = descriptions[i];
+ if ((diff & bd.mask) == 0) continue;
+
+ int value;
+ if (bd.shift < 0) {
+ value = (newval & bd.mask) != 0 ? 1 : 0;
+ } else {
+ value = (newval & bd.mask) >> bd.shift;
+ }
+
+ mTracer.traceCounter("battery_stats." + bd.name, value);
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 98d4c5976adc..ec2bc7cde1a0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4432,6 +4432,12 @@ public class BatteryStatsImpl extends BatteryStats {
+ Integer.toHexString(diffStates2) + " lastDiff2="
+ Integer.toHexString(lastDiffStates2));
}
+
+ mBatteryStatsHistory.recordTraceEvents(cur.eventCode, cur.eventTag);
+ mBatteryStatsHistory.recordTraceCounters(mHistoryLastWritten.states,
+ cur.states & mActiveHistoryStates, BatteryStats.HISTORY_STATE_DESCRIPTIONS);
+ mBatteryStatsHistory.recordTraceCounters(mHistoryLastWritten.states2,
+ cur.states2 & mActiveHistoryStates2, BatteryStats.HISTORY_STATE2_DESCRIPTIONS);
if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
&& timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0
&& (diffStates2&lastDiffStates2) == 0
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c3b6df144544..b94bd83aac12 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4877,9 +4877,6 @@
-->
</array>
- <!-- How long it takes for the HW to start illuminating after the illumination is requested. -->
- <integer name="config_udfps_illumination_transition_ms">50</integer>
-
<!-- Indicates whether device has a power button fingerprint sensor. -->
<bool name="config_is_powerbutton_fps" translatable="false" >false</bool>
@@ -5218,7 +5215,7 @@
<!-- Vertical position of a center of the letterboxed app window.
0 corresponds to the upper side of the screen and 1 to the lower side. If given value < 0
or > 1, it is ignored and central position is used (0.5). -->
- <item name="config_letterboxVerticalPositionMultiplier" format="float" type="dimen">0.5</item>
+ <item name="config_letterboxVerticalPositionMultiplier" format="float" type="dimen">0.0</item>
<!-- Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps.
-->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6c6a0a3e3abd..546bb21df2a6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2706,7 +2706,6 @@
<java-symbol type="bool" name="allow_test_udfps" />
<java-symbol type="array" name="config_udfps_sensor_props" />
<java-symbol type="array" name="config_sfps_sensor_props" />
- <java-symbol type="integer" name="config_udfps_illumination_transition_ms" />
<java-symbol type="bool" name="config_is_powerbutton_fps" />
<java-symbol type="array" name="config_udfps_enroll_stage_thresholds" />
<java-symbol type="array" name="config_sfps_enroll_stage_thresholds" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index a8764e05c3e2..d76ad3d27c70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -517,7 +517,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
}
- ActivityManager.RunningTaskInfo getTaskInfo() {
+ /** Returns the task info for the task in the TaskView. */
+ @Nullable
+ public ActivityManager.RunningTaskInfo getTaskInfo() {
return mTaskInfo;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index c39602032170..7c3c14e2210c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -55,6 +55,8 @@ import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.desktopmode.DesktopMode;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
import com.android.wm.shell.draganddrop.DragAndDropController;
@@ -477,11 +479,12 @@ public abstract class WMShellBaseModule {
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor
) {
return Optional.ofNullable(
RecentTasksController.create(context, shellInit, shellCommandHandler,
- taskStackListener, mainExecutor));
+ taskStackListener, desktopModeTaskRepository, mainExecutor));
}
//
@@ -666,6 +669,24 @@ public abstract class WMShellBaseModule {
}
//
+ // Desktop mode (optional feature)
+ //
+
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract DesktopModeTaskRepository optionalDesktopModeTaskRepository();
+
+ @WMSingleton
+ @Provides
+ static Optional<DesktopModeTaskRepository> providesDesktopModeTaskRepository(
+ @DynamicOverride Optional<DesktopModeTaskRepository> desktopModeTaskRepository) {
+ if (DesktopMode.IS_SUPPORTED) {
+ return desktopModeTaskRepository;
+ }
+ return Optional.empty();
+ }
+
+ //
// Misc
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 18ce3642335d..27d3e35fa07a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -51,6 +51,7 @@ import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopModeController;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
@@ -220,14 +221,14 @@ public abstract class WMShellModule {
Context context,
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<RecentTasksController> recentTasksController,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
WindowDecorViewModel<?> windowDecorViewModel) {
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
ShellInit init = FreeformComponents.isFreeformEnabled(context)
? shellInit
: null;
- return new FreeformTaskListener<>(init, shellTaskOrganizer, recentTasksController,
+ return new FreeformTaskListener<>(init, shellTaskOrganizer, desktopModeTaskRepository,
windowDecorViewModel);
}
@@ -599,17 +600,24 @@ public abstract class WMShellModule {
Context context, ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- @ShellMainThread Handler mainHandler
+ @ShellMainThread Handler mainHandler,
+ Transitions transitions
) {
if (DesktopMode.IS_SUPPORTED) {
return Optional.of(new DesktopModeController(context, shellInit, shellTaskOrganizer,
- rootDisplayAreaOrganizer,
- mainHandler));
+ rootDisplayAreaOrganizer, mainHandler, transitions));
} else {
return Optional.empty();
}
}
+ @WMSingleton
+ @Provides
+ @DynamicOverride
+ static DesktopModeTaskRepository provideDesktopModeTaskRepository() {
+ return new DesktopModeTaskRepository();
+ }
+
//
// Misc
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index 7d34ea481de6..c07ce1065302 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.desktopmode;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
@@ -37,6 +38,7 @@ import com.android.wm.shell.RootDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
/**
* Handles windowing changes when desktop mode system setting changes
@@ -47,15 +49,18 @@ public class DesktopModeController {
private final ShellTaskOrganizer mShellTaskOrganizer;
private final RootDisplayAreaOrganizer mRootDisplayAreaOrganizer;
private final SettingsObserver mSettingsObserver;
+ private final Transitions mTransitions;
public DesktopModeController(Context context, ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- @ShellMainThread Handler mainHandler) {
+ @ShellMainThread Handler mainHandler,
+ Transitions transitions) {
mContext = context;
mShellTaskOrganizer = shellTaskOrganizer;
mRootDisplayAreaOrganizer = rootDisplayAreaOrganizer;
mSettingsObserver = new SettingsObserver(mContext, mainHandler);
+ mTransitions = transitions;
shellInit.addInitCallback(this::onInit, this);
}
@@ -89,7 +94,11 @@ public class DesktopModeController {
}
wct.merge(mRootDisplayAreaOrganizer.prepareWindowingModeChange(displayId,
targetWindowingMode), true /* transfer */);
- mRootDisplayAreaOrganizer.applyTransaction(wct);
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mTransitions.startTransition(TRANSIT_CHANGE, wct, null);
+ } else {
+ mRootDisplayAreaOrganizer.applyTransaction(wct);
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
new file mode 100644
index 000000000000..988601c0e8a8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.desktopmode
+
+import android.util.ArraySet
+
+/**
+ * Keeps track of task data related to desktop mode.
+ */
+class DesktopModeTaskRepository {
+
+ /**
+ * Set of task ids that are marked as active in desktop mode.
+ * Active tasks in desktop mode are freeform tasks that are visible or have been visible after
+ * desktop mode was activated.
+ * Task gets removed from this list when it vanishes. Or when desktop mode is turned off.
+ */
+ private val activeTasks = ArraySet<Int>()
+ private val listeners = ArraySet<Listener>()
+
+ /**
+ * Add a [Listener] to be notified of updates to the repository.
+ */
+ fun addListener(listener: Listener) {
+ listeners.add(listener)
+ }
+
+ /**
+ * Remove a previously registered [Listener]
+ */
+ fun removeListener(listener: Listener) {
+ listeners.remove(listener)
+ }
+
+ /**
+ * Mark a task with given [taskId] as active.
+ */
+ fun addActiveTask(taskId: Int) {
+ val added = activeTasks.add(taskId)
+ if (added) {
+ listeners.onEach { it.onActiveTasksChanged() }
+ }
+ }
+
+ /**
+ * Remove task with given [taskId] from active tasks.
+ */
+ fun removeActiveTask(taskId: Int) {
+ val removed = activeTasks.remove(taskId)
+ if (removed) {
+ listeners.onEach { it.onActiveTasksChanged() }
+ }
+ }
+
+ /**
+ * Check if a task with the given [taskId] was marked as an active task
+ */
+ fun isActiveTask(taskId: Int): Boolean {
+ return activeTasks.contains(taskId)
+ }
+
+ /**
+ * Get a set of the active tasks
+ */
+ fun getActiveTasks(): ArraySet<Int> {
+ return ArraySet(activeTasks)
+ }
+
+ /**
+ * Defines interface for classes that can listen to changes in repository state.
+ */
+ interface Listener {
+ fun onActiveTasksChanged()
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 435d8eaa563e..62bf5172e106 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -17,6 +17,8 @@
package com.android.wm.shell.draganddrop;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -263,6 +265,9 @@ public class DragAndDropPolicy {
mStarter.startShortcut(packageName, id, position, opts, user);
} else {
final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
+ // Put BAL flags to avoid activity start aborted.
+ opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
+ opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
mStarter.startIntent(launchIntent, null /* fillIntent */, position, opts);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 1baac718ee95..ac95d4bc63d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -29,8 +29,8 @@ import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.desktopmode.DesktopMode;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -49,7 +49,7 @@ public class FreeformTaskListener<T extends AutoCloseable>
private static final String TAG = "FreeformTaskListener";
private final ShellTaskOrganizer mShellTaskOrganizer;
- private final Optional<RecentTasksController> mRecentTasksOptional;
+ private final Optional<DesktopModeTaskRepository> mDesktopModeTaskRepository;
private final WindowDecorViewModel<T> mWindowDecorationViewModel;
private final SparseArray<State<T>> mTasks = new SparseArray<>();
@@ -64,11 +64,11 @@ public class FreeformTaskListener<T extends AutoCloseable>
public FreeformTaskListener(
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<RecentTasksController> recentTasksController,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
WindowDecorViewModel<T> windowDecorationViewModel) {
mShellTaskOrganizer = shellTaskOrganizer;
mWindowDecorationViewModel = windowDecorationViewModel;
- mRecentTasksOptional = recentTasksController;
+ mDesktopModeTaskRepository = desktopModeTaskRepository;
if (shellInit != null) {
shellInit.addInitCallback(this::onInit, this);
}
@@ -93,7 +93,7 @@ public class FreeformTaskListener<T extends AutoCloseable>
if (DesktopMode.IS_SUPPORTED && taskInfo.isVisible) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Adding active freeform task: #%d", taskInfo.taskId);
- mRecentTasksOptional.ifPresent(rt -> rt.addActiveFreeformTask(taskInfo.taskId));
+ mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTask(taskInfo.taskId));
}
}
@@ -126,7 +126,7 @@ public class FreeformTaskListener<T extends AutoCloseable>
if (DesktopMode.IS_SUPPORTED) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Removing active freeform task: #%d", taskInfo.taskId);
- mRecentTasksOptional.ifPresent(rt -> rt.removeActiveFreeformTask(taskInfo.taskId));
+ mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId));
}
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -154,7 +154,7 @@ public class FreeformTaskListener<T extends AutoCloseable>
if (taskInfo.isVisible) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Adding active freeform task: #%d", taskInfo.taskId);
- mRecentTasksOptional.ifPresent(rt -> rt.addActiveFreeformTask(taskInfo.taskId));
+ mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTask(taskInfo.taskId));
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index e9f9bb5d7327..7d1259a732c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.fullscreen;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
@@ -107,13 +105,14 @@ public class FullscreenTaskListener<T extends AutoCloseable>
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
updateRecentsForVisibleFullscreenTask(taskInfo);
- if (shouldShowWindowDecor(taskInfo) && mWindowDecorViewModelOptional.isPresent()) {
+ if (mWindowDecorViewModelOptional.isPresent()) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
state.mWindowDecoration =
mWindowDecorViewModelOptional.get().createWindowDecoration(taskInfo,
leash, t, t);
t.apply();
- } else {
+ }
+ if (state.mWindowDecoration == null) {
mSyncQueue.runInSync(t -> {
// Reset several properties back to fullscreen (PiP, for example, leaves all these
// properties in a bad state).
@@ -178,13 +177,12 @@ public class FullscreenTaskListener<T extends AutoCloseable>
public void createWindowDecoration(TransitionInfo.Change change,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
- if (!mWindowDecorViewModelOptional.isPresent()
- || !shouldShowWindowDecor(state.mTaskInfo)) {
- return;
- }
-
- state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration(
+ if (!mWindowDecorViewModelOptional.isPresent()) return;
+ T newWindowDecor = mWindowDecorViewModelOptional.get().createWindowDecoration(
state.mTaskInfo, state.mLeash, startT, finishT);
+ if (newWindowDecor != null) {
+ state.mWindowDecoration = newWindowDecor;
+ }
}
/**
@@ -202,8 +200,7 @@ public class FullscreenTaskListener<T extends AutoCloseable>
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT,
@Nullable AutoCloseable windowDecor) {
- if (!mWindowDecorViewModelOptional.isPresent()
- || !shouldShowWindowDecor(change.getTaskInfo())) {
+ if (!mWindowDecorViewModelOptional.isPresent()) {
return false;
}
final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
@@ -214,8 +211,11 @@ public class FullscreenTaskListener<T extends AutoCloseable>
state.mTaskInfo, startT, finishT, state.mWindowDecoration);
return true;
} else {
- state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration(
+ T newWindowDecor = mWindowDecorViewModelOptional.get().createWindowDecoration(
state.mTaskInfo, state.mLeash, startT, finishT);
+ if (newWindowDecor != null) {
+ state.mWindowDecoration = newWindowDecor;
+ }
return false;
}
}
@@ -256,7 +256,7 @@ public class FullscreenTaskListener<T extends AutoCloseable>
windowDecor =
mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId);
}
- if (mWindowDecorViewModelOptional.isPresent()) {
+ if (mWindowDecorViewModelOptional.isPresent() && windowDecor != null) {
mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition(
taskInfo, startT, finishT, windowDecor);
}
@@ -336,10 +336,5 @@ public class FullscreenTaskListener<T extends AutoCloseable>
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
}
- private static boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
- return taskInfo.getConfiguration().windowConfiguration.getDisplayWindowingMode()
- == WINDOWING_MODE_FREEFORM;
- }
-
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 27bc1a189086..ff4b2edb7310 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -44,6 +44,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.desktopmode.DesktopMode;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
@@ -53,19 +54,20 @@ import com.android.wm.shell.util.SplitBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
/**
* Manages the recent task list from the system, caching it as necessary.
*/
public class RecentTasksController implements TaskStackListenerCallback,
- RemoteCallable<RecentTasksController> {
+ RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.Listener {
private static final String TAG = RecentTasksController.class.getSimpleName();
private final Context mContext;
private final ShellCommandHandler mShellCommandHandler;
+ private final Optional<DesktopModeTaskRepository> mDesktopModeTaskRepository;
private final ShellExecutor mMainExecutor;
private final TaskStackListenerImpl mTaskStackListener;
private final RecentTasks mImpl = new RecentTasksImpl();
@@ -84,15 +86,6 @@ public class RecentTasksController implements TaskStackListenerCallback,
private final Map<Integer, SplitBounds> mTaskSplitBoundsMap = new HashMap<>();
/**
- * Set of taskId's that have been launched in freeform mode.
- * This includes tasks that are currently running, visible and in freeform mode. And also
- * includes tasks that are running in the background, are no longer visible, but at some point
- * were visible to the user.
- * This is used to decide which freeform apps belong to the user's desktop.
- */
- private final HashSet<Integer> mActiveFreeformTasks = new HashSet<>();
-
- /**
* Creates {@link RecentTasksController}, returns {@code null} if the feature is not
* supported.
*/
@@ -102,24 +95,27 @@ public class RecentTasksController implements TaskStackListenerCallback,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor
) {
if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
return null;
}
return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener,
- mainExecutor);
+ desktopModeTaskRepository, mainExecutor);
}
RecentTasksController(Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
ShellExecutor mainExecutor) {
mContext = context;
mShellCommandHandler = shellCommandHandler;
mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mTaskStackListener = taskStackListener;
+ mDesktopModeTaskRepository = desktopModeTaskRepository;
mMainExecutor = mainExecutor;
shellInit.addInitCallback(this::onInit, this);
}
@@ -131,6 +127,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
private void onInit() {
mShellCommandHandler.addDumpCallback(this::dump, this);
mTaskStackListener.addListener(this);
+ mDesktopModeTaskRepository.ifPresent(it -> it.addListener(this));
}
/**
@@ -217,19 +214,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
notifyRecentTasksChanged();
}
- /**
- * Mark a task with given {@code taskId} as active in freeform
- */
- public void addActiveFreeformTask(int taskId) {
- mActiveFreeformTasks.add(taskId);
- notifyRecentTasksChanged();
- }
-
- /**
- * Remove task with given {@code taskId} from active freeform tasks
- */
- public void removeActiveFreeformTask(int taskId) {
- mActiveFreeformTasks.remove(taskId);
+ @Override
+ public void onActiveTasksChanged() {
notifyRecentTasksChanged();
}
@@ -312,7 +298,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
continue;
}
- if (desktopModeActive && mActiveFreeformTasks.contains(taskInfo.taskId)) {
+ if (desktopModeActive && mDesktopModeTaskRepository.isPresent()
+ && mDesktopModeTaskRepository.get().isActiveTask(taskInfo.taskId)) {
// Freeform tasks will be added as a separate entry
freeformTasks.add(taskInfo);
continue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 025e5592e367..3758471664cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -154,7 +154,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private StageCoordinator mStageCoordinator;
// Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
// outside the bounds of the roots by being reparented into a higher level fullscreen container
- private SurfaceControl mSplitTasksContainerLayer;
+ private SurfaceControl mGoingToRecentsTasksLayer;
+ private SurfaceControl mStartingSplitTasksLayer;
public SplitScreenController(Context context,
ShellInit shellInit,
@@ -509,19 +510,53 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
+ if (ENABLE_SHELL_TRANSITIONS) return null;
+
if (isSplitScreenVisible()) {
// Evict child tasks except the top visible one under split root to ensure it could be
// launched as full screen when switching to it on recents.
final WindowContainerTransaction wct = new WindowContainerTransaction();
mStageCoordinator.prepareEvictInvisibleChildTasks(wct);
mSyncQueue.queue(wct);
+ } else {
+ return null;
}
- return reparentSplitTasksForAnimation(apps, false /* enterSplitScreen */);
+
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
+ if (mGoingToRecentsTasksLayer != null) {
+ t.remove(mGoingToRecentsTasksLayer);
+ }
+ mGoingToRecentsTasksLayer = reparentSplitTasksForAnimation(apps, t,
+ "SplitScreenController#onGoingToRecentsLegacy" /* callsite */);
+ t.apply();
+ mTransactionPool.release(t);
+
+ return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
}
RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
+ if (ENABLE_SHELL_TRANSITIONS) return null;
+
+ int openingApps = 0;
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) openingApps++;
+ }
+ if (openingApps < 2) {
+ // Not having enough apps to enter split screen
+ return null;
+ }
+
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
+ if (mStartingSplitTasksLayer != null) {
+ t.remove(mStartingSplitTasksLayer);
+ }
+ mStartingSplitTasksLayer = reparentSplitTasksForAnimation(apps, t,
+ "SplitScreenController#onStartingSplitLegacy" /* callsite */);
+ t.apply();
+ mTransactionPool.release(t);
+
try {
- return reparentSplitTasksForAnimation(apps, true /* enterSplitScreen */);
+ return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
} finally {
for (RemoteAnimationTarget appTarget : apps) {
if (appTarget.leash != null) {
@@ -531,45 +566,23 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
}
- private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
- boolean enterSplitScreen) {
- if (ENABLE_SHELL_TRANSITIONS) return null;
-
- if (enterSplitScreen) {
- int openingApps = 0;
- for (int i = 0; i < apps.length; ++i) {
- if (apps[i].mode == MODE_OPENING) openingApps++;
- }
- if (openingApps < 2) {
- // Not having enough apps to enter split screen
- return null;
- }
- } else if (!isSplitScreenVisible()) {
- return null;
- }
-
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- if (mSplitTasksContainerLayer != null) {
- // Remove the previous layer before recreating
- transaction.remove(mSplitTasksContainerLayer);
- }
+ private SurfaceControl reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
+ SurfaceControl.Transaction t, String callsite) {
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
.setContainerLayer()
.setName("RecentsAnimationSplitTasks")
.setHidden(false)
- .setCallsite("SplitScreenController#onGoingtoRecentsLegacy");
+ .setCallsite(callsite);
mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
- mSplitTasksContainerLayer = builder.build();
+ final SurfaceControl splitTasksLayer = builder.build();
for (int i = 0; i < apps.length; ++i) {
final RemoteAnimationTarget appTarget = apps[i];
- transaction.reparent(appTarget.leash, mSplitTasksContainerLayer);
- transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
+ t.reparent(appTarget.leash, splitTasksLayer);
+ t.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
appTarget.screenSpaceBounds.top);
}
- transaction.apply();
- mTransactionPool.release(transaction);
- return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
+ return splitTasksLayer;
}
/**
* Sets drag info to be logged when splitscreen is entered.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index db35b48eb898..25793ed0317b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -18,8 +18,6 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
-import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -1109,9 +1107,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void addActivityOptions(Bundle opts, StageTaskListener stage) {
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
- // Put BAL flags to avoid activity start aborted.
- opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
- opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
}
void updateActivityOptions(Bundle opts, @SplitPosition int position) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 83aa539d24d6..e8a2cb160880 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -36,6 +36,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.transition.Transitions;
@@ -80,6 +81,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
+ if (!shouldShowWindowDecor(taskInfo)) return null;
final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
mContext,
mDisplayController,
@@ -101,9 +103,12 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
@Override
public CaptionWindowDecoration adoptWindowDecoration(AutoCloseable windowDecor) {
- return (windowDecor instanceof CaptionWindowDecoration)
- ? (CaptionWindowDecoration) windowDecor
- : null;
+ if (!(windowDecor instanceof CaptionWindowDecoration)) return null;
+ final CaptionWindowDecoration captionWindowDecor = (CaptionWindowDecoration) windowDecor;
+ if (!shouldShowWindowDecor(captionWindowDecor.mTaskInfo)) {
+ return null;
+ }
+ return captionWindowDecor;
}
@Override
@@ -231,4 +236,11 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
}
}
}
+
+ private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
+ return DesktopMode.IS_SUPPORTED
+ && mDisplayController.getDisplayContext(taskInfo.displayId)
+ .getResources().getConfiguration().smallestScreenWidthDp >= 600;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index c234949572bf..d9697d288ab6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -42,6 +42,7 @@ public interface WindowDecorViewModel<T extends AutoCloseable> {
/**
* Creates a window decoration for the given task.
+ * Can be {@code null} for Fullscreen tasks but not Freeform ones.
*
* @param taskInfo the initial task info of the task
* @param taskSurface the surface of the task
@@ -49,7 +50,7 @@ public interface WindowDecorViewModel<T extends AutoCloseable> {
* @param finishT the finish transaction to restore states after the transition
* @return the window decoration object
*/
- T createWindowDecoration(
+ @Nullable T createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
@@ -57,11 +58,12 @@ public interface WindowDecorViewModel<T extends AutoCloseable> {
/**
* Adopts the window decoration if possible.
+ * May be {@code null} if a window decor is not needed or the given one is incompatible.
*
* @param windowDecor the potential window decoration to adopt
* @return the window decoration if it can be adopted, or {@code null} otherwise.
*/
- T adoptWindowDecoration(@Nullable AutoCloseable windowDecor);
+ @Nullable T adoptWindowDecoration(@Nullable AutoCloseable windowDecor);
/**
* Notifies a task info update on the given task, with the window decoration created previously
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
index ef532e449bd6..577942505b13 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
@@ -43,6 +43,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
import org.junit.Test;
@@ -63,6 +64,8 @@ public class DesktopModeControllerTest extends ShellTestCase {
private ShellExecutor mTestExecutor;
@Mock
private Handler mMockHandler;
+ @Mock
+ private Transitions mMockTransitions;
private DesktopModeController mController;
private ShellInit mShellInit;
@@ -72,7 +75,7 @@ public class DesktopModeControllerTest extends ShellTestCase {
mShellInit = Mockito.spy(new ShellInit(mTestExecutor));
mController = new DesktopModeController(mContext, mShellInit, mShellTaskOrganizer,
- mRootDisplayAreaOrganizer, mMockHandler);
+ mRootDisplayAreaOrganizer, mMockHandler, mMockTransitions);
mShellInit.init();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
new file mode 100644
index 000000000000..9b28d11f6a9d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.desktopmode
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeTaskRepositoryTest : ShellTestCase() {
+
+ private lateinit var repo: DesktopModeTaskRepository
+
+ @Before
+ fun setUp() {
+ repo = DesktopModeTaskRepository()
+ }
+
+ @Test
+ fun addActiveTask_listenerNotifiedAndTaskIsActive() {
+ val listener = TestListener()
+ repo.addListener(listener)
+
+ repo.addActiveTask(1)
+ assertThat(listener.activeTaskChangedCalls).isEqualTo(1)
+ assertThat(repo.isActiveTask(1)).isTrue()
+ }
+
+ @Test
+ fun addActiveTask_sameTaskDoesNotNotify() {
+ val listener = TestListener()
+ repo.addListener(listener)
+
+ repo.addActiveTask(1)
+ repo.addActiveTask(1)
+ assertThat(listener.activeTaskChangedCalls).isEqualTo(1)
+ }
+
+ @Test
+ fun addActiveTask_multipleTasksAddedNotifiesForEach() {
+ val listener = TestListener()
+ repo.addListener(listener)
+
+ repo.addActiveTask(1)
+ repo.addActiveTask(2)
+ assertThat(listener.activeTaskChangedCalls).isEqualTo(2)
+ }
+
+ @Test
+ fun removeActiveTask_listenerNotifiedAndTaskNotActive() {
+ val listener = TestListener()
+ repo.addListener(listener)
+
+ repo.addActiveTask(1)
+ repo.removeActiveTask(1)
+ // Notify once for add and once for remove
+ assertThat(listener.activeTaskChangedCalls).isEqualTo(2)
+ assertThat(repo.isActiveTask(1)).isFalse()
+ }
+
+ @Test
+ fun removeActiveTask_removeNotExistingTaskDoesNotNotify() {
+ val listener = TestListener()
+ repo.addListener(listener)
+ repo.removeActiveTask(99)
+ assertThat(listener.activeTaskChangedCalls).isEqualTo(0)
+ }
+
+ @Test
+ fun isActiveTask_notExistingTaskReturnsFalse() {
+ assertThat(repo.isActiveTask(99)).isFalse()
+ }
+
+ class TestListener : DesktopModeTaskRepository.Listener {
+ var activeTaskChangedCalls = 0
+ override fun onActiveTasksChanged() {
+ activeTaskChangedCalls++
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index e9a1e2523a86..cadfeb0de312 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -55,6 +55,7 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopMode;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
@@ -82,6 +83,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
private TaskStackListenerImpl mTaskStackListener;
@Mock
private ShellCommandHandler mShellCommandHandler;
+ @Mock
+ private DesktopModeTaskRepository mDesktopModeTaskRepository;
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
@@ -94,7 +97,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
mShellInit = spy(new ShellInit(mMainExecutor));
mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit,
- mShellCommandHandler, mTaskStackListener, mMainExecutor));
+ mShellCommandHandler, mTaskStackListener, Optional.of(mDesktopModeTaskRepository),
+ mMainExecutor));
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
mMainExecutor);
@@ -195,8 +199,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
setRawList(t1, t2, t3, t4);
- mRecentTasksController.addActiveFreeformTask(1);
- mRecentTasksController.addActiveFreeformTask(3);
+ when(mDesktopModeTaskRepository.isActiveTask(1)).thenReturn(true);
+ when(mDesktopModeTaskRepository.isActiveTask(3)).thenReturn(true);
ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 3152e65d5a36..fa056e2b77bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -16,6 +16,22 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED;
+import static android.bluetooth.BluetoothAdapter.STATE_CONNECTING;
+import static android.bluetooth.BluetoothAdapter.STATE_DISCONNECTED;
+import static android.bluetooth.BluetoothAdapter.STATE_DISCONNECTING;
+import static android.bluetooth.BluetoothAdapter.STATE_OFF;
+import static android.bluetooth.BluetoothAdapter.STATE_ON;
+import static android.bluetooth.BluetoothAdapter.STATE_TURNING_OFF;
+import static android.bluetooth.BluetoothAdapter.STATE_TURNING_ON;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* BluetoothCallback provides a callback interface for the settings
@@ -33,7 +49,7 @@ public interface BluetoothCallback {
* {@link android.bluetooth.BluetoothAdapter#STATE_ON},
* {@link android.bluetooth.BluetoothAdapter#STATE_TURNING_OFF}.
*/
- default void onBluetoothStateChanged(int bluetoothState) {}
+ default void onBluetoothStateChanged(@AdapterState int bluetoothState) {}
/**
* It will be called when the local Bluetooth adapter has started
@@ -54,14 +70,14 @@ public interface BluetoothCallback {
*
* @param cachedDevice the Bluetooth device.
*/
- default void onDeviceAdded(CachedBluetoothDevice cachedDevice) {}
+ default void onDeviceAdded(@NonNull CachedBluetoothDevice cachedDevice) {}
/**
* It will be called when requiring to remove a remote device from CachedBluetoothDevice list
*
* @param cachedDevice the Bluetooth device.
*/
- default void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {}
+ default void onDeviceDeleted(@NonNull CachedBluetoothDevice cachedDevice) {}
/**
* It will be called when bond state of a remote device is changed.
@@ -73,7 +89,8 @@ public interface BluetoothCallback {
* {@link android.bluetooth.BluetoothDevice#BOND_BONDING},
* {@link android.bluetooth.BluetoothDevice#BOND_BONDED}.
*/
- default void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {}
+ default void onDeviceBondStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int bondState) {}
/**
* It will be called in following situations:
@@ -89,7 +106,9 @@ public interface BluetoothCallback {
* {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTED},
* {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTING}.
*/
- default void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {}
+ default void onConnectionStateChanged(
+ @Nullable CachedBluetoothDevice cachedDevice,
+ @ConnectionState int state) {}
/**
* It will be called when device been set as active for {@code bluetoothProfile}
@@ -101,7 +120,8 @@ public interface BluetoothCallback {
* @param activeDevice the active Bluetooth device.
* @param bluetoothProfile the profile of active Bluetooth device.
*/
- default void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
+ default void onActiveDeviceChanged(
+ @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
/**
* It will be called in following situations:
@@ -124,8 +144,10 @@ public interface BluetoothCallback {
* {@link android.bluetooth.BluetoothProfile#STATE_DISCONNECTING}.
* @param bluetoothProfile the BluetoothProfile id.
*/
- default void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
- int state, int bluetoothProfile) {
+ default void onProfileConnectionStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice,
+ @ConnectionState int state,
+ int bluetoothProfile) {
}
/**
@@ -138,6 +160,24 @@ public interface BluetoothCallback {
* {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTED},
* {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTED}
*/
- default void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
- }
+ default void onAclConnectionStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int state) {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_DISCONNECTED,
+ STATE_CONNECTING,
+ STATE_CONNECTED,
+ STATE_DISCONNECTING,
+ })
+ @interface ConnectionState {}
+
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_OFF,
+ STATE_TURNING_ON,
+ STATE_ON,
+ STATE_TURNING_OFF,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface AdapterState {}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 51812f043908..a9f4e9c74103 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -32,6 +32,7 @@ import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -193,19 +194,19 @@ public class BluetoothEventManager {
return deviceAdded;
}
- void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
+ void dispatchDeviceAdded(@NonNull CachedBluetoothDevice cachedDevice) {
for (BluetoothCallback callback : mCallbacks) {
callback.onDeviceAdded(cachedDevice);
}
}
- void dispatchDeviceRemoved(CachedBluetoothDevice cachedDevice) {
+ void dispatchDeviceRemoved(@NonNull CachedBluetoothDevice cachedDevice) {
for (BluetoothCallback callback : mCallbacks) {
callback.onDeviceDeleted(cachedDevice);
}
}
- void dispatchProfileConnectionStateChanged(CachedBluetoothDevice device, int state,
+ void dispatchProfileConnectionStateChanged(@NonNull CachedBluetoothDevice device, int state,
int bluetoothProfile) {
for (BluetoothCallback callback : mCallbacks) {
callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
@@ -228,7 +229,8 @@ public class BluetoothEventManager {
}
@VisibleForTesting
- void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+ void dispatchActiveDeviceChanged(
+ @Nullable CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
for (CachedBluetoothDevice cachedDevice : mDeviceManager.getCachedDevicesCopy()) {
boolean isActive = Objects.equals(cachedDevice, activeDevice);
@@ -239,7 +241,7 @@ public class BluetoothEventManager {
}
}
- private void dispatchAclStateChanged(CachedBluetoothDevice activeDevice, int state) {
+ private void dispatchAclStateChanged(@NonNull CachedBluetoothDevice activeDevice, int state) {
for (BluetoothCallback callback : mCallbacks) {
callback.onAclConnectionStateChanged(activeDevice, state);
}
@@ -456,6 +458,7 @@ public class BluetoothEventManager {
Log.w(TAG, "ActiveDeviceChangedHandler: action is null");
return;
}
+ @Nullable
CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
int bluetoothProfile = 0;
if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index a5f3df9f628d..7927c5d460a4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -81,8 +81,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
private int mDeviceMode;
private long mHiSyncId;
private int mGroupId;
+
// Need this since there is no method for getting RSSI
short mRssi;
+
// mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
// because current sub device is only for HearingAid and its profile is the same.
private final Collection<LocalBluetoothProfile> mProfiles = new CopyOnWriteArrayList<>();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java
new file mode 100644
index 000000000000..feb5e0bf6e69
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 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.bluetooth;
+
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.HashMap;
+
+/** Utils class to report hearing aid metrics to statsd */
+public final class HearingAidStatsLogUtils {
+
+ private static final String TAG = "HearingAidStatsLogUtils";
+ private static final HashMap<String, Integer> sDeviceAddressToBondEntryMap = new HashMap<>();
+
+ /**
+ * Sets the mapping from hearing aid device to the bond entry where this device starts it's
+ * bonding(connecting) process.
+ *
+ * @param bondEntry The entry page id where the bonding process starts
+ * @param device The bonding(connecting) hearing aid device
+ */
+ public static void setBondEntryForDevice(int bondEntry, CachedBluetoothDevice device) {
+ sDeviceAddressToBondEntryMap.put(device.getAddress(), bondEntry);
+ }
+
+ /**
+ * Logs hearing aid device information to westworld, including device mode, device side, and
+ * entry page id where the binding(connecting) process starts.
+ *
+ * Only logs the info once after hearing aid is bonded(connected). Clears the map entry of this
+ * device when logging is completed.
+ *
+ * @param device The bonded(connected) hearing aid device
+ */
+ public static void logHearingAidInfo(CachedBluetoothDevice device) {
+ final String deviceAddress = device.getAddress();
+ if (sDeviceAddressToBondEntryMap.containsKey(deviceAddress)) {
+ final int bondEntry = sDeviceAddressToBondEntryMap.getOrDefault(deviceAddress, -1);
+ final int deviceMode = device.getDeviceMode();
+ final int deviceSide = device.getDeviceSide();
+ FrameworkStatsLog.write(FrameworkStatsLog.HEARING_AID_INFO_REPORTED, deviceMode,
+ deviceSide, bondEntry);
+
+ sDeviceAddressToBondEntryMap.remove(deviceAddress);
+ } else {
+ Log.w(TAG, "The device address was not found. Hearing aid device info is not logged.");
+ }
+ }
+
+ @VisibleForTesting
+ static HashMap<String, Integer> getDeviceAddressToBondEntryMap() {
+ return sDeviceAddressToBondEntryMap;
+ }
+
+ private HearingAidStatsLogUtils() {}
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 4714ff93625b..8a9f9dd9c3fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -352,6 +352,8 @@ public class LocalBluetoothProfileManager {
cachedDevice.setHiSyncId(newHiSyncId);
}
}
+
+ HearingAidStatsLogUtils.logHearingAidInfo(cachedDevice);
}
if (getCsipSetCoordinatorProfile() != null
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java
new file mode 100644
index 000000000000..0cf5b8900245
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.HashMap;
+
+@RunWith(RobolectricTestRunner.class)
+public class HearingAidStatsLogUtilsTest {
+
+ private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+
+ @Test
+ public void setBondEntryForDevice_addsEntryToDeviceAddressToBondEntryMap() {
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
+
+ HearingAidStatsLogUtils.setBondEntryForDevice(
+ FrameworkStatsLog.HEARING_AID_INFO_REPORTED__BOND_ENTRY__BLUETOOTH,
+ mCachedBluetoothDevice);
+
+ final HashMap<String, Integer> map =
+ HearingAidStatsLogUtils.getDeviceAddressToBondEntryMap();
+ assertThat(map.containsKey(TEST_DEVICE_ADDRESS)).isTrue();
+ assertThat(map.get(TEST_DEVICE_ADDRESS)).isEqualTo(
+ FrameworkStatsLog.HEARING_AID_INFO_REPORTED__BOND_ENTRY__BLUETOOTH);
+ }
+
+ @Test
+ public void logHearingAidInfo_removesEntryFromDeviceAddressToBondEntryMap() {
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
+
+ HearingAidStatsLogUtils.setBondEntryForDevice(
+ FrameworkStatsLog.HEARING_AID_INFO_REPORTED__BOND_ENTRY__BLUETOOTH,
+ mCachedBluetoothDevice);
+ HearingAidStatsLogUtils.logHearingAidInfo(mCachedBluetoothDevice);
+
+ final HashMap<String, Integer> map =
+ HearingAidStatsLogUtils.getDeviceAddressToBondEntryMap();
+ assertThat(map.containsKey(TEST_DEVICE_ADDRESS)).isFalse();
+ }
+}
diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten1.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten1.jpeg
new file mode 100644
index 000000000000..6241b0b44bb6
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/res/drawable/kitten1.jpeg
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten2.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten2.jpeg
new file mode 100644
index 000000000000..870ef13ee2d9
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/res/drawable/kitten2.jpeg
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten3.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten3.jpeg
new file mode 100644
index 000000000000..bb7261c10033
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/res/drawable/kitten3.jpeg
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten4.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten4.jpeg
new file mode 100644
index 000000000000..e34b7ddf58ce
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/res/drawable/kitten4.jpeg
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten5.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten5.jpeg
new file mode 100644
index 000000000000..9cde24be59ef
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/res/drawable/kitten5.jpeg
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten6.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten6.jpeg
new file mode 100644
index 000000000000..17825b639a26
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/res/drawable/kitten6.jpeg
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
index 2e6456bcc4e1..6805bf83dff4 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
@@ -28,25 +28,25 @@ import com.android.systemui.compose.theme.SystemUITheme
/** The gallery app screens. */
object GalleryAppScreens {
- val Typography = ChildScreen("typography") { TypographyScreen() }
- val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() }
- val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() }
- val Buttons = ChildScreen("buttons") { ButtonsScreen() }
- val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() }
+ private val Typography = ChildScreen("typography") { TypographyScreen() }
+ private val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() }
+ private val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() }
+ private val Buttons = ChildScreen("buttons") { ButtonsScreen() }
+ private val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() }
- val PeopleEmpty =
+ private val PeopleEmpty =
ChildScreen("people_empty") { navController ->
EmptyPeopleScreen(onResult = { navController.popBackStack() })
}
- val PeopleFew =
+ private val PeopleFew =
ChildScreen("people_few") { navController ->
FewPeopleScreen(onResult = { navController.popBackStack() })
}
- val PeopleFull =
+ private val PeopleFull =
ChildScreen("people_full") { navController ->
FullPeopleScreen(onResult = { navController.popBackStack() })
}
- val People =
+ private val People =
ParentScreen(
"people",
mapOf(
@@ -55,6 +55,52 @@ object GalleryAppScreens {
"Full" to PeopleFull,
)
)
+ private val UserSwitcherSingleUser =
+ ChildScreen("user_switcher_single") { navController ->
+ UserSwitcherScreen(
+ userCount = 1,
+ onFinished = navController::popBackStack,
+ )
+ }
+ private val UserSwitcherThreeUsers =
+ ChildScreen("user_switcher_three") { navController ->
+ UserSwitcherScreen(
+ userCount = 3,
+ onFinished = navController::popBackStack,
+ )
+ }
+ private val UserSwitcherFourUsers =
+ ChildScreen("user_switcher_four") { navController ->
+ UserSwitcherScreen(
+ userCount = 4,
+ onFinished = navController::popBackStack,
+ )
+ }
+ private val UserSwitcherFiveUsers =
+ ChildScreen("user_switcher_five") { navController ->
+ UserSwitcherScreen(
+ userCount = 5,
+ onFinished = navController::popBackStack,
+ )
+ }
+ private val UserSwitcherSixUsers =
+ ChildScreen("user_switcher_six") { navController ->
+ UserSwitcherScreen(
+ userCount = 6,
+ onFinished = navController::popBackStack,
+ )
+ }
+ private val UserSwitcher =
+ ParentScreen(
+ "user_switcher",
+ mapOf(
+ "Single" to UserSwitcherSingleUser,
+ "Three" to UserSwitcherThreeUsers,
+ "Four" to UserSwitcherFourUsers,
+ "Five" to UserSwitcherFiveUsers,
+ "Six" to UserSwitcherSixUsers,
+ )
+ )
val Home =
ParentScreen(
@@ -66,6 +112,7 @@ object GalleryAppScreens {
"Example feature" to ExampleFeature,
"Buttons" to Buttons,
"People" to People,
+ "User Switcher" to UserSwitcher,
)
)
}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/UserSwitcherScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/UserSwitcherScreen.kt
new file mode 100644
index 000000000000..fe9707d22684
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/UserSwitcherScreen.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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.compose.gallery
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+import com.android.systemui.user.Fakes.fakeUserSwitcherViewModel
+import com.android.systemui.user.ui.compose.UserSwitcherScreen
+
+@Composable
+fun UserSwitcherScreen(
+ userCount: Int,
+ onFinished: () -> Unit,
+) {
+ val context = LocalContext.current.applicationContext
+ UserSwitcherScreen(
+ viewModel = fakeUserSwitcherViewModel(context, userCount = userCount),
+ onFinished = onFinished,
+ )
+}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
index 02d76f404a32..91a73ea16dc4 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
@@ -58,12 +58,29 @@ object Fakes {
(0 until userCount).map { index ->
UserModel(
id = index,
- name = Text.Loaded("user_$index"),
+ name =
+ Text.Loaded(
+ when (index % 6) {
+ 0 -> "Ross Geller"
+ 1 -> "Phoebe Buffay"
+ 2 -> "Monica Geller"
+ 3 -> "Rachel Greene"
+ 4 -> "Chandler Bing"
+ else -> "Joey Tribbiani"
+ }
+ ),
image =
checkNotNull(
AppCompatResources.getDrawable(
context,
- R.drawable.ic_avatar_guest_user
+ when (index % 6) {
+ 0 -> R.drawable.kitten1
+ 1 -> R.drawable.kitten2
+ 2 -> R.drawable.kitten3
+ 3 -> R.drawable.kitten4
+ 4 -> R.drawable.kitten5
+ else -> R.drawable.kitten6
+ },
)
),
isSelected = index == 0,
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 689938aded44..818248439d0a 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -498,7 +498,6 @@
-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiActivityModel.kt
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_off.xml
new file mode 100644
index 000000000000..a34712386d52
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_off.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M7.38 -0.28 C5.69,-2.81 1.5,-8.5 0.01,-7.32 C0.03,-6.22 -0.06,6.16 -0.06,7.78 C1.56,7.81 5.44,7.19 7.37,2.06 C7.37,1.21 7.35,1.69 7.35,1 C7.35,0.09 7.38,0.59 7.38,-0.28c "
+ android:valueTo="M6 -2.38 C1.81,-6.69 -0.5,-10 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 3,7.75 5.47,4.91 C6.33,3.91 7.21,2.42 7.24,1.13 C7.26,-0.05 6.87,-1.49 6,-2.38c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="pathData"
+ android:startOffset="17"
+ android:valueFrom="M6 -2.38 C1.81,-6.69 -0.5,-10 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 3,7.75 5.47,4.91 C6.33,3.91 7.21,2.42 7.24,1.13 C7.26,-0.05 6.87,-1.49 6,-2.38c "
+ android:valueTo="M4.94 -3.95 C-0.31,-9.06 0.52,-9.42 -5.75,-3.22 C-8.31,-0.69 -7.05,3 -6.94,3.22 C-3.63,9.31 2.63,9.5 5.94,4.59 C6.61,3.6 7.43,1.16 7.12,0 C6.72,-1.45 6.01,-2.9 4.94,-3.95c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="50"
+ android:valueFrom="M4.94 -3.95 C-0.31,-9.06 0.52,-9.42 -5.75,-3.22 C-8.31,-0.69 -7.05,3 -6.94,3.22 C-3.63,9.31 2.63,9.5 5.94,4.59 C6.61,3.6 7.43,1.16 7.12,0 C6.72,-1.45 6.01,-2.9 4.94,-3.95c "
+ android:valueTo="M3.07 -5.83 C-0.44,-10.25 -2.56,-6.87 -6.11,-2.6 C-7.03,-1.49 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.1,-4.1 3.07,-5.83c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="pathData"
+ android:startOffset="117"
+ android:valueFrom="M3.07 -5.83 C-0.44,-10.25 -2.56,-6.87 -6.11,-2.6 C-7.03,-1.49 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.1,-4.1 3.07,-5.83c "
+ android:valueTo="M0 -8.18 C-1.81,-7.06 -3.89,-5.21 -5.95,-2.81 C-7.56,-0.94 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.99 0,7.99 C0,6.34 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-6 0,-8.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="433"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M7.38 -0.28 C5.69,-2.81 1.5,-8.5 0.01,-7.32 C0.03,-6.22 -0.06,6.16 -0.06,7.78 C1.56,7.81 5.44,7.19 7.37,2.06 C7.37,1.21 7.35,1.69 7.35,1 C7.35,0.09 7.38,0.59 7.38,-0.28c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.19 C0,-7.19 2.93,-4.32 4.38,-2.87 C5.25,-2 5.88,-0.62 5.88,1.19 C5.88,4.5 2.8,6.97 0,7 C-2.8,7.03 -6,4.37 -6,1.13 C-6,-0.43 -5.38,-1.9 -4.25,-3.01 C-4.25,-3.01 0,-7.19 0,-7.19 M-5.65 -4.44 C-5.65,-4.44 -5.65,-4.44 -5.65,-4.44 C-7.1,-3.01 -8,-1.04 -8,1.13 C-8,5.48 -4.42,9 0,9 C4.42,9 8,5.48 8,1.13 C8,-1.04 7.1,-3.01 5.65,-4.44 C5.65,-4.44 5.65,-4.44 5.65,-4.44 C5.65,-4.44 0,-10 0,-10 C0,-10 -5.65,-4.44 -5.65,-4.44c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_on.xml
new file mode 100644
index 000000000000..c5609d8e465b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_on.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M0 -7.49 C-2.58,-7.49 -7.47,-2.59 -7.47,0.57 C-7.47,0.77 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c "
+ android:valueTo="M3.11 -6.83 C-1.37,-10.12 -6.04,-3.87 -7.22,0.09 C-7.26,0.29 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="100"
+ android:valueFrom="M3.11 -6.83 C-1.37,-10.12 -6.04,-3.87 -7.22,0.09 C-7.26,0.29 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueTo="M5 -3.95 C-0.25,-9.87 -0.47,-8.74 -5,-3.63 C-7.94,-0.31 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 5.06,-2.75 5,-3.95c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="167"
+ android:valueFrom="M5 -3.95 C-0.25,-9.87 -0.47,-8.74 -5,-3.63 C-7.94,-0.31 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 5.06,-2.75 5,-3.95c "
+ android:valueTo="M4.82 -3.81 C1,-7.87 0.75,-9.37 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.38,7.38 6.4,3.22 C6.77,2.46 7,1.69 6.74,0 C6.56,-1.16 5.85,-2.71 4.82,-3.81c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="233"
+ android:valueFrom="M4.82 -3.81 C1,-7.87 0.75,-9.37 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.38,7.38 6.4,3.22 C6.77,2.46 7,1.69 6.74,0 C6.56,-1.16 5.85,-2.71 4.82,-3.81c "
+ android:valueTo="M5.63 -2.97 C3.65,-4.93 0.75,-8.37 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 4.13,7.94 6.31,3.44 C6.68,2.66 7,1.37 6.87,0.06 C6.77,-0.84 6.13,-2.47 5.63,-2.97c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="433"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.49 C-2.58,-7.49 -7.47,-2.59 -7.47,0.57 C-7.47,0.77 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.19 C0,-7.19 2.93,-4.32 4.38,-2.87 C5.25,-2 5.88,-0.62 5.88,1.19 C5.88,4.5 2.8,6.97 0,7 C-2.8,7.03 -6,4.37 -6,1.13 C-6,-0.43 -5.38,-1.9 -4.25,-3.01 C-4.25,-3.01 0,-7.19 0,-7.19 M-5.65 -4.44 C-5.65,-4.44 -5.65,-4.44 -5.65,-4.44 C-7.1,-3.01 -8,-1.04 -8,1.13 C-8,5.48 -4.42,9 0,9 C4.42,9 8,5.48 8,1.13 C8,-1.04 7.1,-3.01 5.65,-4.44 C5.65,-4.44 5.65,-4.44 5.65,-4.44 C5.65,-4.44 0,-10 0,-10 C0,-10 -5.65,-4.44 -5.65,-4.44c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
index f239a8dd4291..420ae7044149 100644
--- a/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
+++ b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
@@ -15,11 +15,45 @@
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
<target android:name="time_group">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="183"
+ android:duration="333"
android:propertyName="translateX"
android:startOffset="0"
android:valueFrom="0"
@@ -38,13 +72,29 @@
<group
android:name="_R_G_L_0_G"
android:translateX="12"
- android:translateY="12">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c M1.5 1.5 C1.5,1.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5c " />
+ android:translateY="12.469">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_1"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c " />
+ </group>
</group>
</group>
<group android:name="time_group" />
diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
index d46fafa25816..57d7902f59ca 100644
--- a/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
+++ b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
@@ -15,35 +15,213 @@
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
- <target android:name="_R_G_L_1_G">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="667"
- android:pathData="M 12.125,34.75C 12.104,30.958 12.021,15.792 12,12"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
+ android:duration="250"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="383"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1.1500000000000001"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="383"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1.1500000000000001"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="767"
+ android:propertyName="scaleX"
+ android:startOffset="383"
+ android:valueFrom="1.1500000000000001"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="767"
+ android:propertyName="scaleY"
+ android:startOffset="383"
+ android:valueFrom="1.1500000000000001"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c "
+ android:valueTo="M-1.74 -2.93 C-1.74,-2.93 -2.06,-7.3 -2.06,-7.3 C-2.14,-8.99 -1.15,-10 0,-10 C1.15,-10 2.08,-8.88 2.09,-7.3 C2.09,-7.3 1.74,-3.09 1.74,-3.09 C1.74,-3.09 9.44,2.79 9.44,2.79 C9.44,2.79 9.44,4.79 9.44,4.79 C9.44,4.79 1.69,1.57 1.69,1.57 C1.69,1.57 -1.74,-2.93 -1.74,-2.93c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="pathData"
+ android:startOffset="200"
+ android:valueFrom="M-1.74 -2.93 C-1.74,-2.93 -2.06,-7.3 -2.06,-7.3 C-2.14,-8.99 -1.15,-10 0,-10 C1.15,-10 2.08,-8.88 2.09,-7.3 C2.09,-7.3 1.74,-3.09 1.74,-3.09 C1.74,-3.09 9.44,2.79 9.44,2.79 C9.44,2.79 9.44,4.79 9.44,4.79 C9.44,4.79 1.69,1.57 1.69,1.57 C1.69,1.57 -1.74,-2.93 -1.74,-2.93c "
+ android:valueTo="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="pathData"
+ android:startOffset="417"
+ android:valueFrom="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c "
+ android:valueTo="M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_0_G">
+ <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="517"
- android:pathData="M 12,12C 12.021,8.312 12.104,-6.436999999999999 12.125,-10.125"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
+ android:duration="383"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1.1500000000000001"
+ android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="383"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1.1500000000000001"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="767"
+ android:propertyName="scaleX"
+ android:startOffset="383"
+ android:valueFrom="1.1500000000000001"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="767"
+ android:propertyName="scaleY"
+ android:startOffset="383"
+ android:valueFrom="1.1500000000000001"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.199,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M1.51 1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.49 1.51,1.49c "
+ android:valueTo="M1.69 1.58 C1.69,1.58 -1.74,-2.92 -1.74,-2.92 C-1.74,-2.92 -9.44,2.79 -9.44,2.79 C-9.44,2.79 -9.44,4.79 -9.44,4.79 C-9.44,4.79 -1.67,1.42 -1.67,1.42 C-1.67,1.42 -1.11,7.28 -1.11,7.28 C-1.11,7.28 -2.94,8.78 -2.94,8.78 C-2.94,8.78 -2.92,10 -2.92,10 C-2.92,10 0,9 0,9 C0,9 2.92,10 2.92,10 C2.92,10 2.91,8.78 2.91,8.78 C2.91,8.78 1.08,7.28 1.08,7.28 C1.08,7.28 1.69,1.58 1.69,1.58c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="pathData"
+ android:startOffset="200"
+ android:valueFrom="M1.69 1.58 C1.69,1.58 -1.74,-2.92 -1.74,-2.92 C-1.74,-2.92 -9.44,2.79 -9.44,2.79 C-9.44,2.79 -9.44,4.79 -9.44,4.79 C-9.44,4.79 -1.67,1.42 -1.67,1.42 C-1.67,1.42 -1.11,7.28 -1.11,7.28 C-1.11,7.28 -2.94,8.78 -2.94,8.78 C-2.94,8.78 -2.92,10 -2.92,10 C-2.92,10 0,9 0,9 C0,9 2.92,10 2.92,10 C2.92,10 2.91,8.78 2.91,8.78 C2.91,8.78 1.08,7.28 1.08,7.28 C1.08,7.28 1.69,1.58 1.69,1.58c "
+ android:valueTo="M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="pathData"
+ android:startOffset="417"
+ android:valueFrom="M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c "
+ android:valueTo="M1.51 1.5 C1.51,1.5 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.5 1.51,1.5c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
@@ -53,7 +231,7 @@
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="683"
+ android:duration="1167"
android:propertyName="translateX"
android:startOffset="0"
android:valueFrom="0"
@@ -70,26 +248,31 @@
android:viewportWidth="24">
<group android:name="_R_G">
<group
- android:name="_R_G_L_1_G"
- android:translateX="12.125"
- android:translateY="34.75">
- <path
- android:name="_R_G_L_1_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData=" M10 4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.5 1.5,-8.5 C1.5,-9.33 0.83,-10 0,-10 C-0.83,-10 -1.5,-9.33 -1.5,-8.5 C-1.5,-8.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4c " />
- </group>
- <group
android:name="_R_G_L_0_G"
android:translateX="12"
- android:translateY="12">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c M1.5 1.5 C1.5,1.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5c " />
+ android:translateY="12.469">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_1"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M1.51 1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02 C-1.5,-3.02 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.51,1.49 1.51,1.49c " />
+ </group>
</group>
</group>
<group android:name="time_group" />
diff --git a/packages/SystemUI/res/drawable/qs_data_saver_icon_off.xml b/packages/SystemUI/res/drawable/qs_data_saver_icon_off.xml
new file mode 100644
index 000000000000..57777a670d33
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_data_saver_icon_off.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="133"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="150"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:pivotY="1"
+ android:rotation="180"
+ android:translateX="12"
+ android:translateY="10.984">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:rotation="1440"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M9.15 4.05 C9.15,4.05 6.55,2.55 6.55,2.55 C6.7,2.13 6.81,1.71 6.89,1.29 C6.96,0.86 7,0.43 7,0 C7,-1.77 6.43,-3.31 5.3,-4.62 C4.17,-5.94 2.73,-6.72 1,-6.95 C1,-6.95 1,-9.95 1,-9.95 C3.57,-9.7 5.71,-8.62 7.43,-6.72 C9.14,-4.82 10,-2.58 10,0 C10,0.7 9.94,1.39 9.81,2.08 C9.69,2.76 9.47,3.42 9.15,4.05c M0 10 C-1.38,10 -2.68,9.74 -3.9,9.21 C-5.12,8.69 -6.17,7.98 -7.07,7.08 C-7.97,6.18 -8.69,5.12 -9.21,3.9 C-9.74,2.68 -10,1.38 -10,0 C-10,-2.58 -9.14,-4.82 -7.42,-6.72 C-5.71,-8.62 -3.57,-9.7 -1,-9.95 C-1,-9.95 -1,-6.95 -1,-6.95 C-2.73,-6.72 -4.17,-5.94 -5.3,-4.62 C-6.43,-3.31 -7,-1.77 -7,0 C-7,1.95 -6.32,3.6 -4.96,4.96 C-3.6,6.32 -1.95,7 0,7 C1.07,7 2.08,6.78 3.04,6.33 C4,5.88 4.82,5.23 5.5,4.4 C5.5,4.4 8.1,5.9 8.1,5.9 C7.15,7.2 5.97,8.21 4.55,8.93 C3.13,9.64 1.62,10 0,10c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_data_saver_icon_on.xml b/packages/SystemUI/res/drawable/qs_data_saver_icon_on.xml
new file mode 100644
index 000000000000..b3764137793a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_data_saver_icon_on.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="333"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M1.02 2 C1.02,2 1,2 1,2 C1,2 1,1.98 1,1.98 C1,1.98 -1,1.98 -1,1.98 C-1,1.98 -1,2 -1,2 C-1,2 -1,2 -1,2 C-1,2 -1,0 -1,0 C-1,0 -1,0 -1,0 C-1,0 -1,0 -1,0 C-1,0 1,0 1,0 C1,0 1,0 1,0 C1,0 1.02,0 1.02,0 C1.02,0 1.02,2 1.02,2c "
+ android:valueTo="M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.2,0.2 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="500"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="613.191"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.086,0.422 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="rotation"
+ android:startOffset="500"
+ android:valueFrom="613.191"
+ android:valueTo="720"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.334,1.012 0.833,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="933"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="12"
+ android:translateY="10.75">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M1.02 2 C1.02,2 1,2 1,2 C1,2 1,1.98 1,1.98 C1,1.98 -1,1.98 -1,1.98 C-1,1.98 -1,2 -1,2 C-1,2 -1,2 -1,2 C-1,2 -1,0 -1,0 C-1,0 -1,0 -1,0 C-1,0 -1,0 -1,0 C-1,0 1,0 1,0 C1,0 1,0 1,0 C1,0 1.02,0 1.02,0 C1.02,0 1.02,2 1.02,2c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:rotation="0"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M9.15 4.05 C9.15,4.05 6.55,2.55 6.55,2.55 C6.7,2.13 6.81,1.71 6.89,1.29 C6.96,0.86 7,0.43 7,0 C7,-1.77 6.43,-3.31 5.3,-4.62 C4.17,-5.94 2.73,-6.72 1,-6.95 C1,-6.95 1,-9.95 1,-9.95 C3.57,-9.7 5.71,-8.62 7.43,-6.72 C9.14,-4.82 10,-2.58 10,0 C10,0.7 9.94,1.39 9.81,2.08 C9.69,2.76 9.47,3.42 9.15,4.05c M0 10 C-1.38,10 -2.68,9.74 -3.9,9.21 C-5.12,8.69 -6.17,7.98 -7.07,7.08 C-7.97,6.18 -8.69,5.12 -9.21,3.9 C-9.74,2.68 -10,1.38 -10,0 C-10,-2.58 -9.14,-4.82 -7.42,-6.72 C-5.71,-8.62 -3.57,-9.7 -1,-9.95 C-1,-9.95 -1,-6.95 -1,-6.95 C-2.73,-6.72 -4.17,-5.94 -5.3,-4.62 C-6.43,-3.31 -7,-1.77 -7,0 C-7,1.95 -6.32,3.6 -4.96,4.96 C-3.6,6.32 -1.95,7 0,7 C1.07,7 2.08,6.78 3.04,6.33 C4,5.88 4.82,5.23 5.5,4.4 C5.5,4.4 8.1,5.9 8.1,5.9 C7.15,7.2 5.97,8.21 4.55,8.93 C3.13,9.64 1.62,10 0,10c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_extra_dim_icon_off.xml b/packages/SystemUI/res/drawable/qs_extra_dim_icon_off.xml
new file mode 100644
index 000000000000..b0f85e7714c6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_extra_dim_icon_off.xml
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M93 39.5 C93,39.55 93,41.52 93,44 C93,46.49 93,48.58 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c M94 37 C94,37 92,37 92,37 C92,37 92,35.44 92,35.44 C92,35.44 94,35.44 94,35.44 C94,35.44 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 84.31,45 84.31,45 C84.31,45 84.31,43 84.31,43 C84.31,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,52.69 94,52.69 C94,52.69 92,52.69 92,52.69 C92,52.69 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 101.75,43 101.75,43 C101.75,43 101.75,45 101.75,45 C101.75,45 100,45 100,45c M86.07 38.57 C86.07,38.57 87.48,37.16 87.48,37.16 C87.48,37.16 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 86.07,38.57 86.07,38.57c M87.57 50.87 C87.57,50.87 86.16,49.46 86.16,49.46 C86.16,49.46 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 87.57,50.87 87.57,50.87c M99.9 49.43 C99.9,49.43 98.49,50.84 98.49,50.84 C98.49,50.84 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 99.9,49.43 99.9,49.43c M98.52 37.13 C98.52,37.13 99.93,38.54 99.93,38.54 C99.93,38.54 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 98.52,37.13 98.52,37.13c "
+ android:valueTo="M93 39.5 C95.49,39.5 97.5,41.52 97.5,44 C97.5,46.49 95.49,48.5 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c M94 37 C94,37 92,37 92,37 C92,37 92,33 92,33 C92,33 94,33 94,33 C94,33 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 82,45 82,45 C82,45 82,43 82,43 C82,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,55 94,55 C94,55 92,55 92,55 C92,55 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 104,43 104,43 C104,43 104,45 104,45 C104,45 100,45 100,45c M85.22 37.64 C85.22,37.64 86.64,36.22 86.64,36.22 C86.64,36.22 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 85.22,37.64 85.22,37.64c M86.64 51.78 C86.64,51.78 85.22,50.36 85.22,50.36 C85.22,50.36 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 86.64,51.78 86.64,51.78c M100.78 50.36 C100.78,50.36 99.36,51.78 99.36,51.78 C99.36,51.78 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 100.78,50.36 100.78,50.36c M99.36 36.22 C99.36,36.22 100.78,37.64 100.78,37.64 C100.78,37.64 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 99.36,36.22 99.36,36.22c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M94 37 C94,37 92,37 92,37 C92,37 92,35.44 92,35.44 C92,35.44 94,35.44 94,35.44 C94,35.44 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 84.31,45 84.31,45 C84.31,45 84.31,43 84.31,43 C84.31,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,52.69 94,52.69 C94,52.69 92,52.69 92,52.69 C92,52.69 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 101.75,43 101.75,43 C101.75,43 101.75,45 101.75,45 C101.75,45 100,45 100,45c "
+ android:valueTo="M94 37 C94,37 92,37 92,37 C92,37 92,33 92,33 C92,33 94,33 94,33 C94,33 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 82,45 82,45 C82,45 82,43 82,43 C82,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,55 94,55 C94,55 92,55 92,55 C92,55 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 104,43 104,43 C104,43 104,45 104,45 C104,45 100,45 100,45c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M86.07 38.57 C86.07,38.57 87.48,37.16 87.48,37.16 C87.48,37.16 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 86.07,38.57 86.07,38.57c M87.57 50.87 C87.57,50.87 86.16,49.46 86.16,49.46 C86.16,49.46 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 87.57,50.87 87.57,50.87c M99.9 49.43 C99.9,49.43 98.49,50.84 98.49,50.84 C98.49,50.84 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 99.9,49.43 99.9,49.43c M98.52 37.13 C98.52,37.13 99.93,38.54 99.93,38.54 C99.93,38.54 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 98.52,37.13 98.52,37.13c "
+ android:valueTo="M85.22 37.64 C85.22,37.64 86.64,36.22 86.64,36.22 C86.64,36.22 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 85.22,37.64 85.22,37.64c M86.64 51.78 C86.64,51.78 85.22,50.36 85.22,50.36 C85.22,50.36 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 86.64,51.78 86.64,51.78c M100.78 50.36 C100.78,50.36 99.36,51.78 99.36,51.78 C99.36,51.78 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 100.78,50.36 100.78,50.36c M99.36 36.22 C99.36,36.22 100.78,37.64 100.78,37.64 C100.78,37.64 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 99.36,36.22 99.36,36.22c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="517"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="-81"
+ android:translateY="-32">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:pathData=" M93 40 C95.21,40 97,41.79 97,44 C97,46.21 95.21,48 93,48 C90.79,48 89,46.21 89,44 C89,41.79 90.79,40 93,40c "
+ android:strokeAlpha="1"
+ android:strokeColor="#000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-81"
+ android:translateY="-32">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M93 39.5 C93,39.55 93,41.52 93,44 C93,46.49 93,48.58 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M94 37 C94,37 92,37 92,37 C92,37 92,35.44 92,35.44 C92,35.44 94,35.44 94,35.44 C94,35.44 94,37 94,37c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M86.07 38.57 C86.07,38.57 87.48,37.16 87.48,37.16 C87.48,37.16 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 86.07,38.57 86.07,38.57c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_extra_dim_icon_on.xml b/packages/SystemUI/res/drawable/qs_extra_dim_icon_on.xml
new file mode 100644
index 000000000000..5546b6f39813
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_extra_dim_icon_on.xml
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M93 39.5 C95.49,39.5 97.5,41.52 97.5,44 C97.5,46.49 95.49,48.5 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c M94 37 C94,37 92,37 92,37 C92,37 92,33 92,33 C92,33 94,33 94,33 C94,33 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 82,45 82,45 C82,45 82,43 82,43 C82,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,55 94,55 C94,55 92,55 92,55 C92,55 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 104,43 104,43 C104,43 104,45 104,45 C104,45 100,45 100,45c M85.22 37.64 C85.22,37.64 86.64,36.22 86.64,36.22 C86.64,36.22 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 85.22,37.64 85.22,37.64c M86.64 51.78 C86.64,51.78 85.22,50.36 85.22,50.36 C85.22,50.36 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 86.64,51.78 86.64,51.78c M100.78 50.36 C100.78,50.36 99.36,51.78 99.36,51.78 C99.36,51.78 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 100.78,50.36 100.78,50.36c M99.36 36.22 C99.36,36.22 100.78,37.64 100.78,37.64 C100.78,37.64 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 99.36,36.22 99.36,36.22c "
+ android:valueTo="M93 39.5 C93,39.55 93,41.52 93,44 C93,46.49 93,48.58 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c M94 37 C94,37 92,37 92,37 C92,37 92,35.44 92,35.44 C92,35.44 94,35.44 94,35.44 C94,35.44 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 84.31,45 84.31,45 C84.31,45 84.31,43 84.31,43 C84.31,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,52.69 94,52.69 C94,52.69 92,52.69 92,52.69 C92,52.69 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 101.75,43 101.75,43 C101.75,43 101.75,45 101.75,45 C101.75,45 100,45 100,45c M86.07 38.57 C86.07,38.57 87.48,37.16 87.48,37.16 C87.48,37.16 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 86.07,38.57 86.07,38.57c M87.57 50.87 C87.57,50.87 86.16,49.46 86.16,49.46 C86.16,49.46 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 87.57,50.87 87.57,50.87c M99.9 49.43 C99.9,49.43 98.49,50.84 98.49,50.84 C98.49,50.84 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 99.9,49.43 99.9,49.43c M98.52 37.13 C98.52,37.13 99.93,38.54 99.93,38.54 C99.93,38.54 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 98.52,37.13 98.52,37.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M94 37 C94,37 92,37 92,37 C92,37 92,33 92,33 C92,33 94,33 94,33 C94,33 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 82,45 82,45 C82,45 82,43 82,43 C82,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,55 94,55 C94,55 92,55 92,55 C92,55 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 104,43 104,43 C104,43 104,45 104,45 C104,45 100,45 100,45c "
+ android:valueTo="M94 37 C94,37 92,37 92,37 C92,37 92,35.44 92,35.44 C92,35.44 94,35.44 94,35.44 C94,35.44 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 84.31,45 84.31,45 C84.31,45 84.31,43 84.31,43 C84.31,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,52.69 94,52.69 C94,52.69 92,52.69 92,52.69 C92,52.69 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 101.75,43 101.75,43 C101.75,43 101.75,45 101.75,45 C101.75,45 100,45 100,45c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M85.22 37.64 C85.22,37.64 86.64,36.22 86.64,36.22 C86.64,36.22 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 85.22,37.64 85.22,37.64c M86.64 51.78 C86.64,51.78 85.22,50.36 85.22,50.36 C85.22,50.36 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 86.64,51.78 86.64,51.78c M100.78 50.36 C100.78,50.36 99.36,51.78 99.36,51.78 C99.36,51.78 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 100.78,50.36 100.78,50.36c M99.36 36.22 C99.36,36.22 100.78,37.64 100.78,37.64 C100.78,37.64 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 99.36,36.22 99.36,36.22c "
+ android:valueTo="M86.07 38.57 C86.07,38.57 87.48,37.16 87.48,37.16 C87.48,37.16 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 86.07,38.57 86.07,38.57c M87.57 50.87 C87.57,50.87 86.16,49.46 86.16,49.46 C86.16,49.46 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 87.57,50.87 87.57,50.87c M99.9 49.43 C99.9,49.43 98.49,50.84 98.49,50.84 C98.49,50.84 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 99.9,49.43 99.9,49.43c M98.52 37.13 C98.52,37.13 99.93,38.54 99.93,38.54 C99.93,38.54 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 98.52,37.13 98.52,37.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="517"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="-81"
+ android:translateY="-32">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:pathData=" M93 40 C95.21,40 97,41.79 97,44 C97,46.21 95.21,48 93,48 C90.79,48 89,46.21 89,44 C89,41.79 90.79,40 93,40c "
+ android:strokeAlpha="1"
+ android:strokeColor="#ffffff"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-81"
+ android:translateY="-32">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M93 39.5 C95.49,39.5 97.5,41.52 97.5,44 C97.5,46.49 95.49,48.5 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M94 37 C94,37 92,37 92,37 C92,37 92,33 92,33 C92,33 94,33 94,33 C94,33 94,37 94,37c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M85.22 37.64 C85.22,37.64 86.64,36.22 86.64,36.22 C86.64,36.22 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 85.22,37.64 85.22,37.64c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml b/packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml
new file mode 100644
index 000000000000..157e67a6e8e2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M0 2.02 C0.42,2.02 0.77,1.88 1.05,1.59 C1.35,1.29 1.5,0.94 1.5,0.52 C1.5,0.1 1.35,-0.25 1.05,-0.53 C0.77,-0.83 0.42,-0.98 0,-0.98 C-0.42,-0.98 -0.78,-0.83 -1.08,-0.53 C-1.36,-0.25 -1.5,0.1 -1.5,0.52 C-1.5,0.94 -1.36,1.29 -1.08,1.59 C-0.78,1.88 -0.42,2.02 0,2.02c M0 8.62 C-0.42,8.62 -2.78,8.82 -3.07,8.54 C-3.36,8.24 -3.37,-1.87 -3.37,-2.28 C-4.25,-2.69 -4.29,-5.2 -4.01,-5.48 C-3.71,-5.78 -0.42,-5.75 0,-5.75 C0.42,-5.75 4.39,-5.78 4.36,-5.36 C4.22,-2.97 4.47,-3.03 3.34,-1.78 C3.07,-1.48 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+ android:valueTo="M0 3 C0.42,3 0.77,2.86 1.05,2.58 C1.35,2.28 1.5,1.92 1.5,1.5 C1.5,1.08 1.35,0.73 1.05,0.45 C0.77,0.15 0.42,0 0,0 C-0.42,0 -0.78,0.15 -1.08,0.45 C-1.36,0.73 -1.5,1.08 -1.5,1.5 C-1.5,1.92 -1.36,2.28 -1.08,2.58 C-0.78,2.86 -0.42,3 0,3c M0 8.62 C-0.42,8.62 -2.24,8.83 -2.54,8.55 C-2.83,8.25 -2.86,9.1 -2.86,8.69 C-2.86,8.27 -3.02,8.85 -2.74,8.57 C-2.44,8.26 -0.43,8.31 -0.02,8.31 C0.4,8.31 2.4,8.17 2.68,8.47 C2.98,8.75 2.93,8.33 2.93,8.75 C2.93,9.17 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1000"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:pivotY="24"
+ android:scaleX="0.33332999999999996"
+ android:scaleY="0.33332999999999996"
+ android:translateX="12"
+ android:translateY="-4">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#1f1f1f"
+ android:fillType="nonZero"
+ android:pathData=" M-12 -15 C-12,-15 12,-15 12,-15 C12,-15 12,-13.8 12,-13.8 C12,-13.8 6,-4.8 6,-4.8 C6,-4.8 6,24 6,24 C6,24 -6,24 -6,24 C-6,24 -6,-4.8 -6,-4.8 C-6,-4.8 -12,-13.8 -12,-13.8 C-12,-13.8 -12,-15 -12,-15c M12 -21 C12,-21 -12,-21 -12,-21 C-12,-21 -12,-24 -12,-24 C-12,-24 12,-24 12,-24 C12,-24 12,-21 12,-21c M-12 -3 C-12,-3 -12,30 -12,30 C-12,30 12,30 12,30 C12,30 12,-3 12,-3 C12,-3 18,-12 18,-12 C18,-12 18,-30 18,-30 C18,-30 -18,-30 -18,-30 C-18,-30 -18,-12 -18,-12 C-18,-12 -12,-3 -12,-3c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#1f1f1f"
+ android:fillType="nonZero"
+ android:pathData=" M0 2.02 C0.42,2.02 0.77,1.88 1.05,1.59 C1.35,1.29 1.5,0.94 1.5,0.52 C1.5,0.1 1.35,-0.25 1.05,-0.53 C0.77,-0.83 0.42,-0.98 0,-0.98 C-0.42,-0.98 -0.78,-0.83 -1.08,-0.53 C-1.36,-0.25 -1.5,0.1 -1.5,0.52 C-1.5,0.94 -1.36,1.29 -1.08,1.59 C-0.78,1.88 -0.42,2.02 0,2.02c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml b/packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml
new file mode 100644
index 000000000000..22f5e00dad69
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M0 3 C0.42,3 0.77,2.86 1.05,2.58 C1.35,2.28 1.5,1.92 1.5,1.5 C1.5,1.08 1.35,0.73 1.05,0.45 C0.77,0.15 0.42,0 0,0 C-0.42,0 -0.78,0.15 -1.08,0.45 C-1.36,0.73 -1.5,1.08 -1.5,1.5 C-1.5,1.92 -1.36,2.28 -1.08,2.58 C-0.78,2.86 -0.42,3 0,3c M0 8.62 C-0.42,8.62 -2.24,8.83 -2.54,8.55 C-2.83,8.25 -2.86,9.1 -2.86,8.69 C-2.86,8.27 -3.02,8.85 -2.74,8.57 C-2.44,8.26 -0.43,8.31 -0.02,8.31 C0.4,8.31 2.4,8.17 2.68,8.47 C2.98,8.75 2.93,8.33 2.93,8.75 C2.93,9.17 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+ android:valueTo="M0 2.02 C0.42,2.02 0.77,1.88 1.05,1.59 C1.35,1.29 1.5,0.94 1.5,0.52 C1.5,0.1 1.35,-0.25 1.05,-0.53 C0.77,-0.83 0.42,-0.98 0,-0.98 C-0.42,-0.98 -0.78,-0.83 -1.08,-0.53 C-1.36,-0.25 -1.5,0.1 -1.5,0.52 C-1.5,0.94 -1.36,1.29 -1.08,1.59 C-0.78,1.88 -0.42,2.02 0,2.02c M0 8.62 C-0.42,8.62 -2.78,8.82 -3.07,8.54 C-3.36,8.24 -3.37,-1.87 -3.37,-2.28 C-4.25,-2.69 -4.29,-5.2 -4.01,-5.48 C-3.71,-5.78 -0.42,-5.75 0,-5.75 C0.42,-5.75 4.39,-5.78 4.36,-5.36 C4.22,-2.97 4.47,-3.03 3.34,-1.78 C3.07,-1.48 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1000"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:pivotY="24"
+ android:scaleX="0.33332999999999996"
+ android:scaleY="0.33332999999999996"
+ android:translateX="12"
+ android:translateY="-4">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#1f1f1f"
+ android:fillType="nonZero"
+ android:pathData=" M-12 -15 C-12,-15 12,-15 12,-15 C12,-15 12,-13.8 12,-13.8 C12,-13.8 6,-4.8 6,-4.8 C6,-4.8 6,24 6,24 C6,24 -6,24 -6,24 C-6,24 -6,-4.8 -6,-4.8 C-6,-4.8 -12,-13.8 -12,-13.8 C-12,-13.8 -12,-15 -12,-15c M12 -21 C12,-21 -12,-21 -12,-21 C-12,-21 -12,-24 -12,-24 C-12,-24 12,-24 12,-24 C12,-24 12,-21 12,-21c M-12 -3 C-12,-3 -12,30 -12,30 C-12,30 12,30 12,30 C12,30 12,-3 12,-3 C12,-3 18,-12 18,-12 C18,-12 18,-30 18,-30 C18,-30 -18,-30 -18,-30 C-18,-30 -18,-12 -18,-12 C-18,-12 -12,-3 -12,-3c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#1f1f1f"
+ android:fillType="nonZero"
+ android:pathData=" M0 3 C0.42,3 0.77,2.86 1.05,2.58 C1.35,2.28 1.5,1.92 1.5,1.5 C1.5,1.08 1.35,0.73 1.05,0.45 C0.77,0.15 0.42,0 0,0 C-0.42,0 -0.78,0.15 -1.08,0.45 C-1.36,0.73 -1.5,1.08 -1.5,1.5 C-1.5,1.92 -1.36,2.28 -1.08,2.58 C-0.78,2.86 -0.42,3 0,3c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_nightlight_icon_off.xml b/packages/SystemUI/res/drawable/qs_nightlight_icon_off.xml
new file mode 100644
index 000000000000..0769a85dae49
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_nightlight_icon_off.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="283"
+ android:propertyName="translateY"
+ android:startOffset="0"
+ android:valueFrom="12.125"
+ android:valueTo="12.312"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="283"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="-45"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G_T_1"
+ android:rotation="-45"
+ android:translateX="12.875"
+ android:translateY="12.125">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-2.375">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:pathData=" M3.33 8.62 C2.09,8.68 0.59,8.47 -0.64,7.95 C-1.65,7.53 -2.75,6.9 -3.59,6.09 C-4.38,5.32 -5.19,4.17 -5.61,3.08 C-6.04,1.99 -6.25,0.83 -6.25,-0.39 C-6.25,-1.72 -5.98,-2.85 -5.55,-3.94 C-5.13,-5.02 -4.37,-6 -3.59,-6.78 C-2.63,-7.75 -1.63,-8.28 -0.52,-8.77 C0.48,-9.2 2.04,-9.41 3.15,-9.38 C4.22,-9.35 4.94,-9.16 5.81,-8.79 C5.95,-8.73 6.28,-8.6 6.18,-8.55 C3.44,-6.63 1.83,-3.66 1.83,-0.39 C1.83,2.53 3.47,5.72 6.15,7.86 C6.16,7.9 6.03,7.97 5.91,8.04 C5.12,8.44 4.31,8.56 3.33,8.62c "
+ android:strokeAlpha="1"
+ android:strokeColor="#ffffff"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_nightlight_icon_on.xml b/packages/SystemUI/res/drawable/qs_nightlight_icon_on.xml
new file mode 100644
index 000000000000..5ffe262d8077
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_nightlight_icon_on.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="400"
+ android:propertyName="translateY"
+ android:startOffset="0"
+ android:valueFrom="12.312"
+ android:valueTo="12.125"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="400"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="-45"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G_T_1"
+ android:rotation="0"
+ android:translateX="12.875"
+ android:translateY="12.312">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-2.375">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:pathData=" M3.33 8.62 C2.09,8.68 0.59,8.47 -0.64,7.95 C-1.65,7.53 -2.75,6.9 -3.59,6.09 C-4.38,5.32 -5.19,4.17 -5.61,3.08 C-6.04,1.99 -6.25,0.83 -6.25,-0.39 C-6.25,-1.72 -5.98,-2.85 -5.55,-3.94 C-5.13,-5.02 -4.37,-6 -3.6,-6.78 C-2.63,-7.75 -1.63,-8.28 -0.52,-8.77 C0.48,-9.2 2.04,-9.41 3.14,-9.38 C4.22,-9.35 4.94,-9.16 5.81,-8.79 C5.94,-8.73 6.28,-8.6 6.18,-8.55 C3.44,-6.63 1.83,-3.66 1.83,-0.39 C1.83,2.53 3.47,5.72 6.15,7.86 C6.16,7.9 6.03,7.97 5.91,8.04 C5.13,8.43 4.31,8.56 3.33,8.62c "
+ android:strokeAlpha="1"
+ android:strokeColor="#ffffff"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml b/packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml
new file mode 100644
index 000000000000..b3bdd361398d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#edf2eb"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M3.5 -2.5 C3.5,-2.5 3.5,2.5 3.5,2.5 C3.5,3.05 3.05,3.5 2.5,3.5 C2.5,3.5 -2.5,3.5 -2.5,3.5 C-3.05,3.5 -3.5,3.05 -3.5,2.5 C-3.5,2.5 -3.5,-2.5 -3.5,-2.5 C-3.5,-3.05 -3.05,-3.5 -2.5,-3.5 C-2.5,-3.5 2.5,-3.5 2.5,-3.5 C3.05,-3.5 3.5,-3.05 3.5,-2.5c "
+ android:valueTo="M3.5 0 C3.5,0.97 3.11,1.84 2.48,2.48 C1.84,3.11 0.97,3.5 0,3.5 C-0.97,3.5 -1.84,3.11 -2.47,2.48 C-3.11,1.84 -3.5,0.97 -3.5,0 C-3.5,-0.97 -3.11,-1.84 -2.47,-2.47 C-1.84,-3.11 -0.97,-3.5 0,-3.5 C0.97,-3.5 1.84,-3.11 2.48,-2.47 C3.11,-1.84 3.5,-0.97 3.5,0c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="90"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#edf2eb"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#edf2eb"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_2">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#edf2eb"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_3">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#edf2eb"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1000"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:rotation="90"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M3.5 -2.5 C3.5,-2.5 3.5,2.5 3.5,2.5 C3.5,3.05 3.05,3.5 2.5,3.5 C2.5,3.5 -2.5,3.5 -2.5,3.5 C-3.05,3.5 -3.5,3.05 -3.5,2.5 C-3.5,2.5 -3.5,-2.5 -3.5,-2.5 C-3.5,-3.05 -3.05,-3.5 -2.5,-3.5 C-2.5,-3.5 2.5,-3.5 2.5,-3.5 C3.05,-3.5 3.5,-3.05 3.5,-2.5c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M6.6 -4.47 C6.6,-4.47 8.04,-5.91 8.04,-5.91 C9.26,-4.26 9.99,-2.21 10,0.01 C10,2.23 9.26,4.27 8.04,5.93 C8.04,5.93 6.6,4.49 6.6,4.49 C7.47,3.2 7.99,1.66 7.99,0 C7.99,-1.65 7.47,-3.19 6.6,-4.47c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_1"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M-8.01 0 C-8.01,1.67 -7.49,3.22 -6.61,4.5 C-6.61,4.5 -8.04,5.93 -8.04,5.93 C-9.27,4.27 -10,2.22 -10,0 C-10,-2.22 -9.27,-4.26 -8.05,-5.92 C-8.05,-5.92 -6.62,-4.49 -6.62,-4.49 C-7.49,-3.21 -8.01,-1.66 -8.01,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_2_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_2"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M-0.01 8 C1.66,8 3.2,7.48 4.48,6.61 C4.48,6.61 5.91,8.05 5.91,8.05 C4.25,9.27 2.21,10 -0.01,10 C-2.22,10 -4.26,9.27 -5.92,8.05 C-5.92,8.05 -4.48,6.61 -4.48,6.61 C-3.21,7.48 -1.67,8 -0.01,8c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_3_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_3"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M-5.93 -8.04 C-4.27,-9.27 -2.23,-10 -0.01,-10 C2.22,-10 4.26,-9.27 5.94,-8.03 C5.94,-8.03 4.5,-6.59 4.5,-6.59 C3.21,-7.47 1.67,-7.99 0,-7.99 C-1.67,-7.99 -3.21,-7.47 -4.49,-6.6 C-4.49,-6.6 -5.93,-8.04 -5.93,-8.04c " />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml b/packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml
new file mode 100644
index 000000000000..facbef5e4348
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml
@@ -0,0 +1,286 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 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.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#edf2eb"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M3.5 0 C3.5,0.97 3.11,1.84 2.48,2.48 C1.84,3.11 0.97,3.5 0,3.5 C-0.97,3.5 -1.84,3.11 -2.47,2.48 C-3.11,1.84 -3.5,0.97 -3.5,0 C-3.5,-0.97 -3.11,-1.84 -2.47,-2.47 C-1.84,-3.11 -0.97,-3.5 0,-3.5 C0.97,-3.5 1.84,-3.11 2.48,-2.47 C3.11,-1.84 3.5,-0.97 3.5,0c "
+ android:valueTo="M3.5 -2.5 C3.5,-2.5 3.5,2.5 3.5,2.5 C3.5,3.05 3.05,3.5 2.5,3.5 C2.5,3.5 -2.5,3.5 -2.5,3.5 C-3.05,3.5 -3.5,3.05 -3.5,2.5 C-3.5,2.5 -3.5,-2.5 -3.5,-2.5 C-3.5,-3.05 -3.05,-3.5 -2.5,-3.5 C-2.5,-3.5 2.5,-3.5 2.5,-3.5 C3.05,-3.5 3.5,-3.05 3.5,-2.5c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="90"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#edf2eb"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="833"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#edf2eb"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="833"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_2">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#edf2eb"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_2_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="833"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_3">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#edf2eb"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_3_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="833"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1000"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:rotation="0"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M3.5 0 C3.5,0.97 3.11,1.84 2.48,2.48 C1.84,3.11 0.97,3.5 0,3.5 C-0.97,3.5 -1.84,3.11 -2.47,2.48 C-3.11,1.84 -3.5,0.97 -3.5,0 C-3.5,-0.97 -3.11,-1.84 -2.47,-2.47 C-1.84,-3.11 -0.97,-3.5 0,-3.5 C0.97,-3.5 1.84,-3.11 2.48,-2.47 C3.11,-1.84 3.5,-0.97 3.5,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M6.6 -4.47 C6.6,-4.47 8.04,-5.91 8.04,-5.91 C9.26,-4.26 9.99,-2.21 10,0.01 C10,2.23 9.26,4.27 8.04,5.93 C8.04,5.93 6.6,4.49 6.6,4.49 C7.47,3.2 7.99,1.66 7.99,0 C7.99,-1.65 7.47,-3.19 6.6,-4.47c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_1"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M-8.01 0 C-8.01,1.67 -7.49,3.22 -6.61,4.5 C-6.61,4.5 -8.04,5.93 -8.04,5.93 C-9.27,4.27 -10,2.22 -10,0 C-10,-2.22 -9.27,-4.26 -8.05,-5.92 C-8.05,-5.92 -6.62,-4.49 -6.62,-4.49 C-7.49,-3.21 -8.01,-1.66 -8.01,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_2_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_2"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M-0.01 8 C1.66,8 3.2,7.48 4.48,6.61 C4.48,6.61 5.91,8.05 5.91,8.05 C4.25,9.27 2.21,10 -0.01,10 C-2.22,10 -4.26,9.27 -5.92,8.05 C-5.92,8.05 -4.48,6.61 -4.48,6.61 C-3.21,7.48 -1.67,8 -0.01,8c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_3_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_3"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M-5.93 -8.04 C-4.27,-9.27 -2.23,-10 -0.01,-10 C2.22,-10 4.26,-9.27 5.94,-8.03 C5.94,-8.03 4.5,-6.59 4.5,-6.59 C3.21,-7.47 1.67,-7.99 0,-7.99 C-1.67,-7.99 -3.21,-7.47 -4.49,-6.6 C-4.49,-6.6 -5.93,-8.04 -5.93,-8.04c " />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c827e21a20e6..75baeeff6025 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -241,6 +241,10 @@
<color name="dream_overlay_aqi_very_unhealthy">#AD1457</color>
<color name="dream_overlay_aqi_hazardous">#880E4F</color>
<color name="dream_overlay_aqi_unknown">#BDC1C6</color>
+
+ <!-- Dream overlay text shadows -->
<color name="dream_overlay_clock_key_text_shadow_color">#4D000000</color>
<color name="dream_overlay_clock_ambient_text_shadow_color">#4D000000</color>
+ <color name="dream_overlay_status_bar_key_text_shadow_color">#66000000</color>
+ <color name="dream_overlay_status_bar_ambient_text_shadow_color">#59000000</color>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1b169e2e1a31..eb6c45747924 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1547,10 +1547,20 @@
<dimen name="broadcast_dialog_btn_text_size">16sp</dimen>
<dimen name="broadcast_dialog_btn_minHeight">44dp</dimen>
<dimen name="broadcast_dialog_margin">16dp</dimen>
+
+ <!-- Shadow for dream overlay clock complication -->
<dimen name="dream_overlay_clock_key_text_shadow_dx">0dp</dimen>
<dimen name="dream_overlay_clock_key_text_shadow_dy">0dp</dimen>
- <dimen name="dream_overlay_clock_key_text_shadow_radius">5dp</dimen>
+ <dimen name="dream_overlay_clock_key_text_shadow_radius">3dp</dimen>
<dimen name="dream_overlay_clock_ambient_text_shadow_dx">0dp</dimen>
<dimen name="dream_overlay_clock_ambient_text_shadow_dy">0dp</dimen>
<dimen name="dream_overlay_clock_ambient_text_shadow_radius">1dp</dimen>
+
+ <!-- Shadow for dream overlay status bar complications -->
+ <dimen name="dream_overlay_status_bar_key_text_shadow_dx">0.5dp</dimen>
+ <dimen name="dream_overlay_status_bar_key_text_shadow_dy">0.5dp</dimen>
+ <dimen name="dream_overlay_status_bar_key_text_shadow_radius">1dp</dimen>
+ <dimen name="dream_overlay_status_bar_ambient_text_shadow_dx">0.5dp</dimen>
+ <dimen name="dream_overlay_status_bar_ambient_text_shadow_dy">0.5dp</dimen>
+ <dimen name="dream_overlay_status_bar_ambient_text_shadow_radius">2dp</dimen>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index b3b75f68e71e..c2e74456c032 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -23,6 +23,7 @@ import android.annotation.SuppressLint
import android.app.compat.ChangeIdStateCache.invalidate
import android.content.Context
import android.graphics.Canvas
+import android.text.Layout
import android.text.TextUtils
import android.text.format.DateFormat
import android.util.AttributeSet
@@ -78,6 +79,8 @@ class AnimatableClockView @JvmOverloads constructor(
private var textAnimator: TextAnimator? = null
private var onTextAnimatorInitialized: Runnable? = null
+ @VisibleForTesting var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator =
+ { layout, invalidateCb -> TextAnimator(layout, invalidateCb) }
@VisibleForTesting var isAnimationEnabled: Boolean = true
@VisibleForTesting var timeOverrideInMillis: Long? = null
@@ -151,8 +154,6 @@ class AnimatableClockView @JvmOverloads constructor(
// relayout if the text didn't actually change.
if (!TextUtils.equals(text, formattedText)) {
text = formattedText
- lastTextUpdate = getTimestamp()
-
// Because the TextLayout may mutate under the hood as a result of the new text, we
// notify the TextAnimator that it may have changed and request a measure/layout. A
// crash will occur on the next invocation of setTextStyle if the layout is mutated
@@ -161,6 +162,7 @@ class AnimatableClockView @JvmOverloads constructor(
textAnimator?.updateLayout(layout)
}
requestLayout()
+ lastTextUpdate = getTimestamp()
}
}
@@ -175,7 +177,7 @@ class AnimatableClockView @JvmOverloads constructor(
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val animator = textAnimator
if (animator == null) {
- textAnimator = TextAnimator(layout) { invalidate() }
+ textAnimator = textAnimatorFactory(layout, ::invalidate)
onTextAnimatorInitialized?.run()
onTextAnimatorInitialized = null
} else {
@@ -220,9 +222,6 @@ class AnimatableClockView @JvmOverloads constructor(
}
fun animateAppearOnLockscreen() {
- if (isAnimationEnabled && textAnimator == null) {
- return
- }
setTextStyle(
weight = dozingWeight,
textSize = -1f,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index b5e57667fbb8..19ac2e479bcb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -90,6 +90,9 @@ class DefaultClock(
override lateinit var animations: ClockAnimations
private set
+ private var smallRegionDarkness = false
+ private var largeRegionDarkness = false
+
init {
val parent = FrameLayout(ctx)
@@ -148,8 +151,14 @@ class DefaultClock(
smallClockIsDark: Boolean,
largeClockIsDark: Boolean
) {
- updateClockColor(smallClock, smallClockIsDark)
- updateClockColor(largeClock, largeClockIsDark)
+ if (smallRegionDarkness != smallClockIsDark) {
+ smallRegionDarkness = smallClockIsDark
+ updateClockColor(smallClock, smallClockIsDark)
+ }
+ if (largeRegionDarkness != largeClockIsDark) {
+ largeRegionDarkness = largeClockIsDark
+ updateClockColor(largeClock, largeClockIsDark)
+ }
}
override fun onLocaleChanged(locale: Locale) {
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 0e1e0cb2adcf..b444f4c1110b 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -137,6 +137,10 @@ open class ClockEventController @Inject constructor(
override fun onThemeChanged() {
updateFun.updateColors()
}
+
+ override fun onDensityOrFontScaleChanged() {
+ clock?.events?.onFontSettingChanged()
+ }
}
private val batteryCallback = object : BatteryStateChangeCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 436b756ea0cb..8f5cbb76222f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -54,6 +54,8 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
@@ -127,6 +129,7 @@ public class AuthContainerView extends LinearLayout
private final float mTranslationY;
@ContainerState private int mContainerState = STATE_UNKNOWN;
private final Set<Integer> mFailedModalities = new HashSet<Integer>();
+ private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
private final @Background DelayableExecutor mBackgroundExecutor;
private int mOrientation;
@@ -362,8 +365,7 @@ public class AuthContainerView extends LinearLayout
return false;
}
if (event.getAction() == KeyEvent.ACTION_UP) {
- sendEarlyUserCanceled();
- animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ onBackInvoked();
}
return true;
});
@@ -373,6 +375,11 @@ public class AuthContainerView extends LinearLayout
requestFocus();
}
+ private void onBackInvoked() {
+ sendEarlyUserCanceled();
+ animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ }
+
void sendEarlyUserCanceled() {
mConfig.mCallback.onSystemEvent(
BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL, getRequestId());
@@ -520,6 +527,11 @@ public class AuthContainerView extends LinearLayout
.start();
});
}
+ OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
+ if (dispatcher != null) {
+ dispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mBackCallback);
+ }
}
private Animator.AnimatorListener getJankListener(View v, String type, long timeout) {
@@ -618,6 +630,10 @@ public class AuthContainerView extends LinearLayout
@Override
public void onDetachedFromWindow() {
+ OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
+ if (dispatcher != null) {
+ findOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback);
+ }
super.onDetachedFromWindow();
mWakefulnessLifecycle.removeObserver(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index a3b6cfc11803..92f37e07614a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -291,7 +291,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
});
mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation);
- mUdfpsController.setHalControlsIllumination(mUdfpsProps.get(0).halControlsIllumination);
mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 734e941dd1ce..4fee0837a52c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -266,7 +266,7 @@ class AuthRippleController @Inject constructor(
acquireInfo: Int
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
- BiometricFingerprintConstants.shouldTurnOffHbm(acquireInfo) &&
+ BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquireInfo) &&
acquireInfo != BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD) {
// received an 'acquiredBad' message, so immediately retract
mView.retractDwellRipple()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 9281eb8acb56..ad966125b9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -54,13 +54,13 @@ public abstract class UdfpsAnimationView extends FrameLayout {
getDrawable().onSensorRectUpdated(bounds);
}
- void onIlluminationStarting() {
- getDrawable().setIlluminationShowing(true);
+ void onDisplayConfiguring() {
+ getDrawable().setDisplayConfigured(true);
getDrawable().invalidateSelf();
}
- void onIlluminationStopped() {
- getDrawable().setIlluminationShowing(false);
+ void onDisplayUnconfigured() {
+ getDrawable().setDisplayConfigured(false);
getDrawable().invalidateSelf();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index 742c65c2f854..3ad2bef97ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -31,10 +31,10 @@ import java.io.PrintWriter
/**
* Handles:
* 1. registering for listeners when its view is attached and unregistering on view detached
- * 2. pausing udfps when fingerprintManager may still be running but we temporarily want to hide
+ * 2. pausing UDFPS when FingerprintManager may still be running but we temporarily want to hide
* the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth)
* 3. sending events to its view including:
- * - illumination events
+ * - enabling and disabling of the UDFPS display mode
* - sensor position changes
* - doze time event
*/
@@ -167,19 +167,20 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
}
/**
- * Udfps has started illuminating and the fingerprint manager is working on authenticating.
+ * The display began transitioning into the UDFPS mode and the fingerprint manager started
+ * authenticating.
*/
- fun onIlluminationStarting() {
- view.onIlluminationStarting()
+ fun onDisplayConfiguring() {
+ view.onDisplayConfiguring()
view.postInvalidate()
}
/**
- * Udfps has stopped illuminating and the fingerprint manager is no longer attempting to
- * authenticate.
+ * The display transitioned away from the UDFPS mode and the fingerprint manager stopped
+ * authenticating.
*/
- fun onIlluminationStopped() {
- view.onIlluminationStopped()
+ fun onDisplayUnconfigured() {
+ view.onDisplayUnconfigured()
view.postInvalidate()
}
@@ -197,4 +198,4 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
* Called when a view should announce an accessibility event.
*/
open fun doAnnounceForAccessibility(str: String) {}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 7f2680b2137d..36287f59d746 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -86,7 +86,7 @@ import kotlin.Unit;
/**
* Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
- * and coordinates triggering of the high-brightness mode (HBM).
+ * and toggles the UDFPS display mode.
*
* Note that the current architecture is designed so that a single {@link UdfpsController}
* controls/manages all UDFPS sensors. In other words, a single controller is registered with
@@ -123,7 +123,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
@NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Nullable private final UdfpsHbmProvider mHbmProvider;
+ @Nullable private final UdfpsDisplayModeProvider mUdfpsDisplayMode;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock;
@NonNull private final UnlockedScreenOffAnimationController
@@ -135,7 +135,6 @@ public class UdfpsController implements DozeReceiver {
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting int mSensorId;
- private boolean mHalControlsIllumination;
@VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams();
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
@Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
@@ -147,10 +146,9 @@ public class UdfpsController implements DozeReceiver {
private int mActivePointerId = -1;
// The timestamp of the most recent touch log.
private long mTouchLogTime;
- // Sensor has a capture (good or bad) for this touch. Do not need to illuminate for this
- // particular touch event anymore. In other words, do not illuminate until user lifts and
- // touches the sensor area again.
- // TODO: We should probably try to make touch/illumination things more of a FSM
+ // Sensor has a capture (good or bad) for this touch. No need to enable the UDFPS display mode
+ // anymore for this particular touch event. In other words, do not enable the UDFPS mode until
+ // the user touches the sensor area again.
private boolean mAcquiredReceived;
// The current request from FingerprintService. Null if no current request.
@@ -211,8 +209,8 @@ public class UdfpsController implements DozeReceiver {
mKeyguardUpdateMonitor, mDialogManager, mDumpManager,
mLockscreenShadeTransitionController, mConfigurationController,
mSystemClock, mKeyguardStateController,
- mUnlockedScreenOffAnimationController, mHalControlsIllumination,
- mHbmProvider, requestId, reason, callback,
+ mUnlockedScreenOffAnimationController,
+ mUdfpsDisplayMode, requestId, reason, callback,
(view, event, fromUdfpsView) -> onTouch(requestId, event,
fromUdfpsView), mActivityLaunchAnimator)));
}
@@ -236,7 +234,7 @@ public class UdfpsController implements DozeReceiver {
int sensorId,
@BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo
) {
- if (BiometricFingerprintConstants.shouldTurnOffHbm(acquiredInfo)) {
+ if (BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquiredInfo)) {
boolean acquiredGood = acquiredInfo == FINGERPRINT_ACQUIRED_GOOD;
mFgExecutor.execute(() -> {
if (mOverlay == null) {
@@ -247,7 +245,7 @@ public class UdfpsController implements DozeReceiver {
mAcquiredReceived = true;
final UdfpsView view = mOverlay.getOverlayView();
if (view != null) {
- view.stopIllumination(); // turn off HBM
+ view.unconfigureDisplay();
}
if (acquiredGood) {
mOverlay.onAcquiredGood();
@@ -292,7 +290,7 @@ public class UdfpsController implements DozeReceiver {
/**
* Updates the overlay parameters and reconstructs or redraws the overlay, if necessary.
*
- * @param sensorId sensor for which the overlay is getting updated.
+ * @param sensorId sensor for which the overlay is getting updated.
* @param overlayParams See {@link UdfpsOverlayParams}.
*/
public void updateOverlayParams(int sensorId, @NonNull UdfpsOverlayParams overlayParams) {
@@ -321,11 +319,6 @@ public class UdfpsController implements DozeReceiver {
mAuthControllerUpdateUdfpsLocation = r;
}
- // TODO(b/229290039): UDFPS controller should manage its properties on its own. Remove this.
- public void setHalControlsIllumination(boolean value) {
- mHalControlsIllumination = value;
- }
-
/**
* Calculate the pointer speed given a velocity tracker and the pointer id.
* This assumes that the velocity tracker has already been passed all relevant motion events.
@@ -369,8 +362,8 @@ public class UdfpsController implements DozeReceiver {
}
/**
- * @param x coordinate
- * @param y coordinate
+ * @param x coordinate
+ * @param y coordinate
* @param relativeToUdfpsView true if the coordinates are relative to the udfps view; else,
* calculate from the display dimensions in portrait orientation
*/
@@ -423,7 +416,7 @@ public class UdfpsController implements DozeReceiver {
}
final UdfpsView udfpsView = mOverlay.getOverlayView();
- final boolean isIlluminationRequested = udfpsView.isIlluminationRequested();
+ final boolean isDisplayConfigured = udfpsView.isDisplayConfigured();
boolean handled = false;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_OUTSIDE:
@@ -507,7 +500,7 @@ public class UdfpsController implements DozeReceiver {
"minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
minor, major, v, exceedsVelocityThreshold);
final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
- if (!isIlluminationRequested && !mAcquiredReceived
+ if (!isDisplayConfigured && !mAcquiredReceived
&& !exceedsVelocityThreshold) {
final float scale = mOverlayParams.getScaleFactor();
@@ -598,7 +591,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull VibratorHelper vibrator,
@NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
@NonNull UdfpsShell udfpsShell,
- @NonNull Optional<UdfpsHbmProvider> hbmProvider,
+ @NonNull Optional<UdfpsDisplayModeProvider> udfpsDisplayMode,
@NonNull KeyguardStateController keyguardStateController,
@NonNull DisplayManager displayManager,
@Main Handler mainHandler,
@@ -630,7 +623,7 @@ public class UdfpsController implements DozeReceiver {
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
- mHbmProvider = hbmProvider.orElse(null);
+ mUdfpsDisplayMode = udfpsDisplayMode.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
mConfigurationController = configurationController;
@@ -804,15 +797,14 @@ public class UdfpsController implements DozeReceiver {
}
/**
- * Cancel updfs scan affordances - ability to hide the HbmSurfaceView (white circle) before
- * user explicitly lifts their finger. Generally, this should be called whenever udfps fails
- * or errors.
+ * Cancel UDFPS affordances - ability to hide the UDFPS overlay before the user explicitly
+ * lifts their finger. Generally, this should be called on errors in the authentication flow.
*
* The sensor that triggers an AOD fingerprint interrupt (see onAodInterrupt) doesn't give
* ACTION_UP/ACTION_CANCEL events, so and AOD interrupt scan needs to be cancelled manually.
* This should be called when authentication either succeeds or fails. Failing to cancel the
- * scan will leave the screen in high brightness mode and will show the HbmSurfaceView until
- * the user lifts their finger.
+ * scan will leave the display in the UDFPS mode until the user lifts their finger. On optical
+ * sensors, this can result in illumination persisting for longer than necessary.
*/
void onCancelUdfps() {
if (mOverlay != null && mOverlay.getOverlayView() != null) {
@@ -874,7 +866,7 @@ public class UdfpsController implements DozeReceiver {
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
final UdfpsView view = mOverlay.getOverlayView();
if (view != null) {
- view.startIllumination(() -> {
+ view.configureDisplay(() -> {
if (mAlternateTouchProvider != null) {
mBiometricExecutor.execute(() -> {
mAlternateTouchProvider.onUiReady();
@@ -914,8 +906,8 @@ public class UdfpsController implements DozeReceiver {
}
}
mOnFingerDown = false;
- if (view.isIlluminationRequested()) {
- view.stopIllumination();
+ if (view.isDisplayConfigured()) {
+ view.unconfigureDisplay();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index ec720579fbee..1c62f8a4e508 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -77,8 +77,7 @@ class UdfpsControllerOverlay(
private val systemClock: SystemClock,
private val keyguardStateController: KeyguardStateController,
private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
- private val halControlsIllumination: Boolean,
- private var hbmProvider: UdfpsHbmProvider,
+ private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
val requestId: Long,
@ShowReason val requestReason: Int,
private val controllerCallback: IUdfpsOverlayControllerCallback,
@@ -102,8 +101,8 @@ class UdfpsControllerOverlay(
fitInsetsTypes = 0
gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- flags =
- (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
+ flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or
+ WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
// Avoid announcing window title.
accessibilityTitle = " "
@@ -140,8 +139,7 @@ class UdfpsControllerOverlay(
R.layout.udfps_view, null, false
) as UdfpsView).apply {
overlayParams = params
- halControlsIllumination = this@UdfpsControllerOverlay.halControlsIllumination
- setHbmProvider(hbmProvider)
+ setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
val animation = inflateUdfpsAnimation(this, controller)
if (animation != null) {
animation.init()
@@ -250,8 +248,8 @@ class UdfpsControllerOverlay(
val wasShowing = isShowing
overlayView?.apply {
- if (isIlluminationRequested) {
- stopIllumination()
+ if (isDisplayConfigured) {
+ unconfigureDisplay()
}
windowManager.removeView(this)
setOnTouchListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java
new file mode 100644
index 000000000000..c6957acf1dc2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.Nullable;
+
+/**
+ * Interface for toggling the optimal display mode for the under-display fingerprint sensor
+ * (UDFPS). For example, the implementation might change the refresh rate and activate a
+ * high-brightness mode.
+ */
+public interface UdfpsDisplayModeProvider {
+
+ /**
+ * Enables the optimal display mode for UDFPS. The mode will persist until
+ * {@link #disable(Runnable)} is called.
+ *
+ * This call must be made from the UI thread. The callback, if provided, will also be invoked
+ * from the UI thread.
+ *
+ * @param onEnabled A runnable that will be executed once the mode is enabled.
+ */
+ void enable(@Nullable Runnable onEnabled);
+
+ /**
+ * Disables the mode that was enabled by {@link #enable(Runnable)}.
+ *
+ * The call must be made from the UI thread. The callback, if provided, will also be invoked
+ * from the UI thread.
+ *
+ * @param onDisabled A runnable that will be executed once mode is disabled.
+ */
+ void disable(@Nullable Runnable onDisabled);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
index ee112b47e243..511b4e34fa0e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
@@ -51,7 +51,7 @@ abstract class UdfpsDrawable(
invalidateSelf()
}
- var isIlluminationShowing: Boolean = false
+ var isDisplayConfigured: Boolean = false
set(showing) {
if (field == showing) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 1317492aefac..1e359584ceec 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -197,7 +197,7 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
@Override
public void draw(@NonNull Canvas canvas) {
- if (isIlluminationShowing()) {
+ if (isDisplayConfigured()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
index 1afa36bd5000..9f6b6d7472f9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
@@ -23,7 +23,7 @@ import android.graphics.Canvas
*/
class UdfpsFpDrawable(context: Context) : UdfpsDrawable(context) {
override fun draw(canvas: Canvas) {
- if (isIlluminationShowing) {
+ if (isDisplayConfigured) {
return
}
fingerprintDrawable.draw(canvas)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
deleted file mode 100644
index f26dd5f57061..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.annotation.Nullable;
-
-/**
- * Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
- * enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
- * illumination is no longer necessary.
- */
-public interface UdfpsHbmProvider {
-
- /**
- * UdfpsView will call this to enable the HBM when the fingerprint illumination is needed.
- *
- * This method is a no-op when some type of HBM is already enabled.
- *
- * This method must be called from the UI thread. The callback, if provided, will also be
- * invoked from the UI thread.
- *
- * @param onHbmEnabled A runnable that will be executed once HBM is enabled.
- *
- * TODO(b/231335067): enableHbm with halControlsIllumination=true shouldn't make sense.
- * This only makes sense now because vendor code may rely on the side effects of enableHbm.
- */
- void enableHbm(boolean halControlsIllumination, @Nullable Runnable onHbmEnabled);
-
- /**
- * UdfpsView will call this to disable HBM when illumination is no longer needed.
- *
- * This method will disable HBM if HBM is enabled. Otherwise, if HBM is already disabled,
- * this method is a no-op.
- *
- * The call must be made from the UI thread. The callback, if provided, will also be invoked
- * from the UI thread.
- *
- * @param onHbmDisabled A runnable that will be executed once HBM is disabled.
- */
- void disableHbm(@Nullable Runnable onHbmDisabled);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
deleted file mode 100644
index f85e9361ecf2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.annotation.Nullable;
-
-/**
- * Interface that should be implemented by UI's that need to coordinate user touches,
- * views/animations, and modules that start/stop display illumination.
- */
-interface UdfpsIlluminator {
- /**
- * @param hbmProvider Invoked when HBM should be enabled or disabled.
- */
- void setHbmProvider(@Nullable UdfpsHbmProvider hbmProvider);
-
- /**
- * Invoked when illumination should start.
- * @param onIlluminatedRunnable Invoked when the display has been illuminated.
- */
- void startIllumination(@Nullable Runnable onIlluminatedRunnable);
-
- /**
- * Invoked when illumination should end.
- */
- void stopIllumination();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index f28fedb9155b..bc274a0af95f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -101,11 +101,11 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
}
@Override
- void onIlluminationStarting() {
+ void onDisplayConfiguring() {
}
@Override
- void onIlluminationStopped() {
+ void onDisplayUnconfigured() {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
index 245c2252d57b..a15456d46897 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
@@ -36,12 +36,12 @@ private const val TAG = "UdfpsView"
class UdfpsView(
context: Context,
attrs: AttributeSet?
-) : FrameLayout(context, attrs), DozeReceiver, UdfpsIlluminator {
+) : FrameLayout(context, attrs), DozeReceiver {
// sensorRect may be bigger than the sensor. True sensor dimensions are defined in
// overlayParams.sensorBounds
private val sensorRect = RectF()
- private var hbmProvider: UdfpsHbmProvider? = null
+ private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null
private val debugTextPaint = Paint().apply {
isAntiAlias = true
color = Color.BLUE
@@ -56,19 +56,12 @@ class UdfpsView(
a.getFloat(R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f)
}
- private val onIlluminatedDelayMs = context.resources.getInteger(
- com.android.internal.R.integer.config_udfps_illumination_transition_ms
- ).toLong()
-
/** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
var animationViewController: UdfpsAnimationViewController<*>? = null
/** Parameters that affect the position and size of the overlay. */
var overlayParams = UdfpsOverlayParams()
- /** Whether the HAL is responsible for enabling and disabling of LHBM. */
- var halControlsIllumination: Boolean = true
-
/** Debug message. */
var debugMessage: String? = null
set(value) {
@@ -76,12 +69,12 @@ class UdfpsView(
postInvalidate()
}
- /** When [startIllumination] has been called but not stopped via [stopIllumination]. */
- var isIlluminationRequested: Boolean = false
+ /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */
+ var isDisplayConfigured: Boolean = false
private set
- override fun setHbmProvider(provider: UdfpsHbmProvider?) {
- hbmProvider = provider
+ fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) {
+ mUdfpsDisplayMode = udfpsDisplayModeProvider
}
// Don't propagate any touch events to the child views.
@@ -124,7 +117,7 @@ class UdfpsView(
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
- if (!isIlluminationRequested) {
+ if (!isDisplayConfigured) {
if (!debugMessage.isNullOrEmpty()) {
canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint)
}
@@ -147,36 +140,15 @@ class UdfpsView(
!(animationViewController?.shouldPauseAuth() ?: false)
}
- /**
- * Start and run [onIlluminatedRunnable] when the first illumination frame reaches the panel.
- */
- override fun startIllumination(onIlluminatedRunnable: Runnable?) {
- isIlluminationRequested = true
- animationViewController?.onIlluminationStarting()
- doIlluminate(onIlluminatedRunnable)
- }
-
- private fun doIlluminate(onIlluminatedRunnable: Runnable?) {
- // TODO(b/231335067): enableHbm with halControlsIllumination=true shouldn't make sense.
- // This only makes sense now because vendor code may rely on the side effects of enableHbm.
- hbmProvider?.enableHbm(halControlsIllumination) {
- if (onIlluminatedRunnable != null) {
- if (halControlsIllumination) {
- onIlluminatedRunnable.run()
- } else {
- // No framework API can reliably tell when a frame reaches the panel. A timeout
- // is the safest solution.
- postDelayed(onIlluminatedRunnable, onIlluminatedDelayMs)
- }
- } else {
- Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null")
- }
- }
+ fun configureDisplay(onDisplayConfigured: Runnable) {
+ isDisplayConfigured = true
+ animationViewController?.onDisplayConfiguring()
+ mUdfpsDisplayMode?.enable(onDisplayConfigured)
}
- override fun stopIllumination() {
- isIlluminationRequested = false
- animationViewController?.onIlluminationStopped()
- hbmProvider?.disableHbm(null /* onHbmDisabled */)
+ fun unconfigureDisplay() {
+ isDisplayConfigured = false
+ animationViewController?.onDisplayUnconfigured()
+ mUdfpsDisplayMode?.disable(null /* onDisabled */)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
new file mode 100644
index 000000000000..96af42bfda22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 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.bluetooth
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.BluetoothLog
+import javax.inject.Inject
+
+/** Helper class for logging bluetooth events. */
+@SysUISingleton
+class BluetoothLogger @Inject constructor(@BluetoothLog private val logBuffer: LogBuffer) {
+ fun logActiveDeviceChanged(address: String?, profileId: Int) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = address
+ int1 = profileId
+ },
+ { "ActiveDeviceChanged. address=$str1 profileId=$int1" }
+ )
+
+ fun logDeviceConnectionStateChanged(address: String?, state: String) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = address
+ str2 = state
+ },
+ { "DeviceConnectionStateChanged. address=$str1 state=$str2" }
+ )
+
+ fun logAclConnectionStateChanged(address: String, state: String) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = address
+ str2 = state
+ },
+ { "AclConnectionStateChanged. address=$str1 state=$str2" }
+ )
+
+ fun logProfileConnectionStateChanged(address: String?, state: String, profileId: Int) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = address
+ str2 = state
+ int1 = profileId
+ },
+ { "ProfileConnectionStateChanged. address=$str1 state=$str2 profileId=$int1" }
+ )
+
+ fun logStateChange(state: String) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = state },
+ { "BluetoothStateChanged. state=$str1" }
+ )
+
+ fun logBondStateChange(address: String, state: Int) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = address
+ int1 = state
+ },
+ { "DeviceBondStateChanged. address=$str1 state=$int1" }
+ )
+
+ fun logDeviceAdded(address: String) =
+ logBuffer.log(TAG, LogLevel.DEBUG, { str1 = address }, { "DeviceAdded. address=$str1" })
+
+ fun logDeviceDeleted(address: String) =
+ logBuffer.log(TAG, LogLevel.DEBUG, { str1 = address }, { "DeviceDeleted. address=$str1" })
+
+ fun logDeviceAttributesChanged() =
+ logBuffer.log(TAG, LogLevel.DEBUG, {}, { "DeviceAttributesChanged." })
+}
+
+private const val TAG = "BluetoothLog"
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index c21e36ab6ecc..7e499ebdf691 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -119,15 +119,12 @@ import java.util.ArrayList;
*/
public class ClipboardOverlayController {
private static final String TAG = "ClipboardOverlayCtrlr";
- private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
/** Constants for screenshot/copy deconflicting */
public static final String SCREENSHOT_ACTION = "com.android.systemui.SCREENSHOT";
public static final String SELF_PERMISSION = "com.android.systemui.permission.SELF";
public static final String COPY_OVERLAY_ACTION = "com.android.systemui.COPY";
- private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard";
-
private static final int CLIPBOARD_DEFAULT_TIMEOUT_MILLIS = 6000;
private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
private static final int FONT_SEARCH_STEP_PX = 4;
@@ -383,7 +380,7 @@ public class ClipboardOverlayController {
mTextPreview);
accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
}
- Intent remoteCopyIntent = getRemoteCopyIntent(clipData);
+ Intent remoteCopyIntent = IntentCreator.getRemoteCopyIntent(clipData, mContext);
// Only show remote copy if it's available.
PackageManager packageManager = mContext.getPackageManager();
if (packageManager.resolveActivity(
@@ -500,41 +497,19 @@ public class ClipboardOverlayController {
private void editImage(Uri uri) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- String editorPackage = mContext.getString(R.string.config_screenshotEditor);
- Intent editIntent = new Intent(Intent.ACTION_EDIT);
- if (!TextUtils.isEmpty(editorPackage)) {
- editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
- }
- editIntent.setDataAndType(uri, "image/*");
- editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- editIntent.putExtra(EXTRA_EDIT_SOURCE_CLIPBOARD, true);
- mContext.startActivity(editIntent);
+ mContext.startActivity(IntentCreator.getImageEditIntent(uri, mContext));
animateOut();
}
private void editText() {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- Intent editIntent = new Intent(mContext, EditTextActivity.class);
- editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivity(editIntent);
+ mContext.startActivity(IntentCreator.getTextEditorIntent(mContext));
animateOut();
}
private void shareContent(ClipData clip) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
- Intent shareIntent = new Intent(Intent.ACTION_SEND);
- shareIntent.setDataAndType(
- clip.getItemAt(0).getUri(), clip.getDescription().getMimeType(0));
- shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
- if (clip.getItemAt(0).getUri() != null) {
- shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- Intent chooserIntent = Intent.createChooser(shareIntent, null)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- mContext.startActivity(chooserIntent);
+ mContext.startActivity(IntentCreator.getShareIntent(clip, mContext));
animateOut();
}
@@ -667,20 +642,6 @@ public class ClipboardOverlayController {
mContext.getString(R.string.clipboard_edit), null);
}
- private Intent getRemoteCopyIntent(ClipData clipData) {
- Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
-
- String remoteCopyPackage = mContext.getString(R.string.config_remoteCopyPackage);
- if (!TextUtils.isEmpty(remoteCopyPackage)) {
- nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
- }
-
- nearbyIntent.setClipData(clipData);
- nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- return nearbyIntent;
- }
-
private void animateIn() {
if (mAccessibilityManager.isEnabled()) {
mDismissButton.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
new file mode 100644
index 000000000000..3d5e601f18f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 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.clipboardoverlay;
+
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import com.android.systemui.R;
+
+class IntentCreator {
+ private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard";
+ private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
+
+ static Intent getTextEditorIntent(Context context) {
+ Intent intent = new Intent(context, EditTextActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ return intent;
+ }
+
+ static Intent getShareIntent(ClipData clipData, Context context) {
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
+
+ // From the ACTION_SEND docs:
+ // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
+ // MIME type of the data in EXTRA_STREAM"
+ if (clipData.getItemAt(0).getUri() != null) {
+ shareIntent.setDataAndType(
+ clipData.getItemAt(0).getUri(), clipData.getDescription().getMimeType(0));
+ shareIntent.putExtra(Intent.EXTRA_STREAM, clipData.getItemAt(0).getUri());
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } else {
+ shareIntent.putExtra(Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context));
+ shareIntent.setType("text/plain");
+ }
+ Intent chooserIntent = Intent.createChooser(shareIntent, null)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ return chooserIntent;
+ }
+
+ static Intent getImageEditIntent(Uri uri, Context context) {
+ String editorPackage = context.getString(R.string.config_screenshotEditor);
+ Intent editIntent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ }
+ editIntent.setDataAndType(uri, "image/*");
+ editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ editIntent.putExtra(EXTRA_EDIT_SOURCE_CLIPBOARD, true);
+ return editIntent;
+ }
+
+ static Intent getRemoteCopyIntent(ClipData clipData, Context context) {
+ Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
+
+ String remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage);
+ if (!TextUtils.isEmpty(remoteCopyPackage)) {
+ nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
+ }
+
+ nearbyIntent.setClipData(clipData);
+ nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ return nearbyIntent;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/drawable/CircularDrawable.kt b/packages/SystemUI/src/com/android/systemui/common/ui/drawable/CircularDrawable.kt
new file mode 100644
index 000000000000..ec71c3824156
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/drawable/CircularDrawable.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.common.ui.drawable
+
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import kotlin.math.min
+
+/** Renders the wrapped [Drawable] as a circle. */
+class CircularDrawable(
+ drawable: Drawable,
+) : DrawableWrapper(drawable) {
+ private val path: Path by lazy { Path() }
+
+ override fun onBoundsChange(bounds: Rect) {
+ super.onBoundsChange(bounds)
+ updateClipPath()
+ }
+
+ override fun draw(canvas: Canvas) {
+ canvas.save()
+ canvas.clipPath(path)
+ drawable?.draw(canvas)
+ canvas.restore()
+ }
+
+ private fun updateClipPath() {
+ path.reset()
+ path.addCircle(
+ bounds.centerX().toFloat(),
+ bounds.centerY().toFloat(),
+ min(bounds.width(), bounds.height()) / 2f,
+ Path.Direction.CW
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 318529b289ec..0469152de776 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -30,7 +30,7 @@ import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
-import com.android.systemui.biometrics.UdfpsHbmProvider;
+import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
import com.android.systemui.biometrics.dagger.BiometricsModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.controls.dagger.ControlsModule;
@@ -197,7 +197,7 @@ public abstract class SystemUIModule {
abstract CentralSurfaces optionalCentralSurfaces();
@BindsOptionalOf
- abstract UdfpsHbmProvider optionalUdfpsHbmProvider();
+ abstract UdfpsDisplayModeProvider optionalUdfpsDisplayModeProvider();
@BindsOptionalOf
abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider();
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java
index 653f4dc66200..789ebc517271 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java
@@ -17,24 +17,21 @@
package com.android.systemui.dreams.complication;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.TextClock;
import com.android.systemui.R;
+import com.android.systemui.dreams.complication.DoubleShadowTextHelper.ShadowInfo;
+
+import kotlin.Unit;
/**
* Extension of {@link TextClock} which draws two shadows on the text (ambient and key shadows)
*/
public class DoubleShadowTextClock extends TextClock {
- private final float mAmbientShadowBlur;
- private final int mAmbientShadowColor;
- private final float mKeyShadowBlur;
- private final float mKeyShadowOffsetX;
- private final float mKeyShadowOffsetY;
- private final int mKeyShadowColor;
- private final float mAmbientShadowOffsetX;
- private final float mAmbientShadowOffsetY;
+ private final DoubleShadowTextHelper mShadowHelper;
public DoubleShadowTextClock(Context context) {
this(context, null);
@@ -46,38 +43,28 @@ public class DoubleShadowTextClock extends TextClock {
public DoubleShadowTextClock(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mKeyShadowBlur = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_radius);
- mKeyShadowOffsetX = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dx);
- mKeyShadowOffsetY = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dy);
- mKeyShadowColor = context.getResources().getColor(
- R.color.dream_overlay_clock_key_text_shadow_color);
- mAmbientShadowBlur = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_radius);
- mAmbientShadowColor = context.getResources().getColor(
- R.color.dream_overlay_clock_ambient_text_shadow_color);
- mAmbientShadowOffsetX = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dx);
- mAmbientShadowOffsetY = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dy);
+
+ final Resources resources = context.getResources();
+ final ShadowInfo keyShadowInfo = new ShadowInfo(
+ resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_radius),
+ resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dx),
+ resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dy),
+ resources.getColor(R.color.dream_overlay_clock_key_text_shadow_color));
+
+ final ShadowInfo ambientShadowInfo = new ShadowInfo(
+ resources.getDimensionPixelSize(
+ R.dimen.dream_overlay_clock_ambient_text_shadow_radius),
+ resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dx),
+ resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dy),
+ resources.getColor(R.color.dream_overlay_clock_ambient_text_shadow_color));
+ mShadowHelper = new DoubleShadowTextHelper(keyShadowInfo, ambientShadowInfo);
}
@Override
public void onDraw(Canvas canvas) {
- // We enhance the shadow by drawing the shadow twice
- getPaint().setShadowLayer(mAmbientShadowBlur, mAmbientShadowOffsetX, mAmbientShadowOffsetY,
- mAmbientShadowColor);
- super.onDraw(canvas);
- canvas.save();
- canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
- getScrollX() + getWidth(),
- getScrollY() + getHeight());
-
- getPaint().setShadowLayer(
- mKeyShadowBlur, mKeyShadowOffsetX, mKeyShadowOffsetY, mKeyShadowColor);
- super.onDraw(canvas);
- canvas.restore();
+ mShadowHelper.applyShadows(this, canvas, () -> {
+ super.onDraw(canvas);
+ return Unit.INSTANCE;
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextHelper.kt b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextHelper.kt
new file mode 100644
index 000000000000..b1dc5a2e5dea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextHelper.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication
+
+import android.graphics.Canvas
+import android.widget.TextView
+import androidx.annotation.ColorInt
+
+class DoubleShadowTextHelper
+constructor(
+ private val keyShadowInfo: ShadowInfo,
+ private val ambientShadowInfo: ShadowInfo,
+) {
+ data class ShadowInfo(
+ val blur: Float,
+ val offsetX: Float = 0f,
+ val offsetY: Float = 0f,
+ @ColorInt val color: Int
+ )
+
+ fun applyShadows(view: TextView, canvas: Canvas, onDrawCallback: () -> Unit) {
+ // We enhance the shadow by drawing the shadow twice
+ view.paint.setShadowLayer(
+ ambientShadowInfo.blur,
+ ambientShadowInfo.offsetX,
+ ambientShadowInfo.offsetY,
+ ambientShadowInfo.color
+ )
+ onDrawCallback()
+ canvas.save()
+ canvas.clipRect(
+ view.scrollX,
+ view.scrollY + view.extendedPaddingTop,
+ view.scrollX + view.width,
+ view.scrollY + view.height
+ )
+
+ view.paint.setShadowLayer(
+ keyShadowInfo.blur,
+ keyShadowInfo.offsetX,
+ keyShadowInfo.offsetY,
+ keyShadowInfo.color
+ )
+ onDrawCallback()
+ canvas.restore()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextView.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextView.java
new file mode 100644
index 000000000000..cf7e3127dedf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextView.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.dreams.complication;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+
+import kotlin.Unit;
+
+/**
+ * Extension of {@link TextView} which draws two shadows on the text (ambient and key shadows}
+ */
+public class DoubleShadowTextView extends TextView {
+ private final DoubleShadowTextHelper mShadowHelper;
+
+ public DoubleShadowTextView(Context context) {
+ this(context, null);
+ }
+
+ public DoubleShadowTextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DoubleShadowTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ final Resources resources = context.getResources();
+ final DoubleShadowTextHelper.ShadowInfo
+ keyShadowInfo = new DoubleShadowTextHelper.ShadowInfo(
+ resources.getDimensionPixelSize(
+ R.dimen.dream_overlay_status_bar_key_text_shadow_radius),
+ resources.getDimensionPixelSize(
+ R.dimen.dream_overlay_status_bar_key_text_shadow_dx),
+ resources.getDimensionPixelSize(
+ R.dimen.dream_overlay_status_bar_key_text_shadow_dy),
+ resources.getColor(R.color.dream_overlay_status_bar_key_text_shadow_color));
+
+ final DoubleShadowTextHelper.ShadowInfo
+ ambientShadowInfo = new DoubleShadowTextHelper.ShadowInfo(
+ resources.getDimensionPixelSize(
+ R.dimen.dream_overlay_status_bar_ambient_text_shadow_radius),
+ resources.getDimensionPixelSize(
+ R.dimen.dream_overlay_status_bar_ambient_text_shadow_dx),
+ resources.getDimensionPixelSize(
+ R.dimen.dream_overlay_status_bar_ambient_text_shadow_dy),
+ resources.getColor(R.color.dream_overlay_status_bar_ambient_text_shadow_color));
+ mShadowHelper = new DoubleShadowTextHelper(keyShadowInfo, ambientShadowInfo);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ mShadowHelper.applyShadows(this, canvas, () -> {
+ super.onDraw(canvas);
+ return Unit.INSTANCE;
+ });
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index a3dc77993d30..568143c8df71 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -42,6 +42,8 @@ import android.util.Pair;
import android.util.Slog;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -601,13 +603,14 @@ public class KeyboardUI extends CoreStartable implements InputManager.OnTabletMo
private final class BluetoothCallbackHandler implements BluetoothCallback {
@Override
- public void onBluetoothStateChanged(int bluetoothState) {
+ public void onBluetoothStateChanged(@BluetoothCallback.AdapterState int bluetoothState) {
mHandler.obtainMessage(MSG_ON_BLUETOOTH_STATE_CHANGED,
bluetoothState, 0).sendToTarget();
}
@Override
- public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+ public void onDeviceBondStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int bondState) {
mHandler.obtainMessage(MSG_ON_DEVICE_BOND_STATE_CHANGED,
bondState, 0, cachedDevice).sendToTarget();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 2da92326a7fd..ddcd0533c588 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -202,7 +202,8 @@ public class KeyguardService extends Service {
}
}
- // Wrap Keyguard going away animation
+ // Wrap Keyguard going away animation.
+ // Note: Also used for wrapping occlude by Dream animation. It works (with some redundancy).
private static IRemoteTransition wrap(IRemoteAnimationRunner runner) {
return new IRemoteTransition.Stub() {
final ArrayMap<IBinder, IRemoteTransitionFinishedCallback> mFinishCallbacks =
@@ -388,6 +389,27 @@ public class KeyguardService extends Service {
f = new TransitionFilter();
f.mTypeSet = new int[]{TRANSIT_KEYGUARD_UNOCCLUDE};
mShellTransitions.registerRemote(f, unoccludeTransition);
+
+ Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_OCCLUDE for DREAM");
+ // Register for occluding by Dream
+ f = new TransitionFilter();
+ f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
+ f.mRequirements = new TransitionFilter.Requirement[]{
+ new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
+ // First require at-least one app of type DREAM showing that occludes.
+ f.mRequirements[0].mActivityType = WindowConfiguration.ACTIVITY_TYPE_DREAM;
+ f.mRequirements[0].mMustBeIndependent = false;
+ f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD;
+ f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ // Then require that we aren't closing any occludes (because this would mean a
+ // regular task->task or activity->activity animation not involving keyguard).
+ f.mRequirements[1].mNot = true;
+ f.mRequirements[1].mMustBeIndependent = false;
+ f.mRequirements[1].mFlags = FLAG_OCCLUDES_KEYGUARD;
+ f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ mShellTransitions.registerRemote(f, new RemoteTransition(
+ wrap(mKeyguardViewMediator.getOccludeByDreamAnimationRunner()),
+ getIApplicationThread()));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt
new file mode 100644
index 000000000000..4887b6a14658
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for bluetooth. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class BluetoothLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
index 323ee21953ea..b551125fccc7 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
@@ -1,4 +1,9 @@
package com.android.systemui.log.dagger
+import javax.inject.Qualifier
+
/** A [com.android.systemui.log.LogBuffer] for KeyguardUpdateMonitor. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
annotation class KeyguardUpdateMonitorLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 756feb0c55c2..5612c22311fb 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -316,4 +316,14 @@ public class LogModule {
public static LogBuffer provideKeyguardUpdateMonitorLogBuffer(LogBufferFactory factory) {
return factory.create("KeyguardUpdateMonitorLog", 200);
}
+
+ /**
+ * Provides a {@link LogBuffer} for bluetooth-related logs.
+ */
+ @Provides
+ @SysUISingleton
+ @BluetoothLog
+ public static LogBuffer providerBluetoothLogBuffer(LogBufferFactory factory) {
+ return factory.create("BluetoothLog", 50);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java
index f03fbcba41b7..b237f2d74483 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java
@@ -19,7 +19,6 @@ package com.android.systemui.log.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.systemui.log.LogBuffer;
-import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -27,7 +26,7 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for events processed by {@link ConnectivityInfoProcessor}
+ * A {@link LogBuffer} for status bar connectivity events.
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
index d0826553ad2c..556560c3534c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
@@ -34,7 +34,7 @@ import com.android.systemui.monet.ColorScheme
* is triggered.
*/
interface ColorTransition {
- fun updateColorScheme(scheme: ColorScheme?)
+ fun updateColorScheme(scheme: ColorScheme?): Boolean
}
/**
@@ -64,14 +64,16 @@ open class AnimatingColorTransition(
applyColor(currentColor)
}
- override fun updateColorScheme(scheme: ColorScheme?) {
+ override fun updateColorScheme(scheme: ColorScheme?): Boolean {
val newTargetColor = if (scheme == null) defaultColor else extractColor(scheme)
if (newTargetColor != targetColor) {
sourceColor = currentColor
targetColor = newTargetColor
valueAnimator.cancel()
valueAnimator.start()
+ return true
}
+ return false
}
init {
@@ -198,8 +200,10 @@ class ColorSchemeTransition internal constructor(
return Utils.getColorAttr(context, id).defaultColor
}
- fun updateColorScheme(colorScheme: ColorScheme?) {
- colorTransitions.forEach { it.updateColorScheme(colorScheme) }
+ fun updateColorScheme(colorScheme: ColorScheme?): Boolean {
+ var anyChanged = false
+ colorTransitions.forEach { anyChanged = it.updateColorScheme(colorScheme) || anyChanged }
colorScheme?.let { mediaViewHolder.gutsViewHolder.colorScheme = colorScheme }
+ return anyChanged
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index b02393b4f73a..759795f84963 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -741,10 +741,14 @@ public class MediaControlPanel {
}
mArtworkBoundId = reqId;
+ // Transition Colors to current color scheme
+ boolean colorSchemeChanged = mColorSchemeTransition.updateColorScheme(colorScheme);
+
// Bind the album view to the artwork or a transition drawable
ImageView albumView = mMediaViewHolder.getAlbumView();
albumView.setPadding(0, 0, 0, 0);
- if (updateBackground || (!mIsArtworkBound && isArtworkBound)) {
+ if (updateBackground || colorSchemeChanged
+ || (!mIsArtworkBound && isArtworkBound)) {
if (mPrevArtwork == null) {
albumView.setImageDrawable(artwork);
} else {
@@ -767,9 +771,6 @@ public class MediaControlPanel {
mIsArtworkBound = isArtworkBound;
}
- // Transition Colors to current color scheme
- mColorSchemeTransition.updateColorScheme(colorScheme);
-
// App icon - use notification icon
ImageView appIconView = mMediaViewHolder.getAppIcon();
appIconView.clearColorFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt b/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
index 88f6f3dd9d0e..6bc94cd5f525 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
@@ -60,6 +60,8 @@ class SquigglyProgress : Drawable() {
linePaint.strokeWidth = value
}
+ // Enables a transition region where the amplitude
+ // of the wave is reduced linearly across it.
var transitionEnabled = true
set(value) {
field = value
@@ -116,44 +118,40 @@ class SquigglyProgress : Drawable() {
}
val progress = level / 10_000f
- val totalProgressPx = bounds.width() * progress
- val waveProgressPx = bounds.width() * (
+ val totalWidth = bounds.width().toFloat()
+ val totalProgressPx = totalWidth * progress
+ val waveProgressPx = totalWidth * (
if (!transitionEnabled || progress > matchedWaveEndpoint) progress else
lerp(minWaveEndpoint, matchedWaveEndpoint, lerpInv(0f, matchedWaveEndpoint, progress)))
// Build Wiggly Path
- val waveStart = -phaseOffset
- val waveEnd = waveProgressPx
- val transitionLength = if (transitionEnabled) transitionPeriods * waveLength else 0.01f
+ val waveStart = -phaseOffset - waveLength / 2f
+ val waveEnd = if (transitionEnabled) totalWidth else waveProgressPx
// helper function, computes amplitude for wave segment
val computeAmplitude: (Float, Float) -> Float = { x, sign ->
- sign * heightFraction * lineAmplitude *
- lerpInvSat(waveEnd, waveEnd - transitionLength, x)
+ if (transitionEnabled) {
+ val length = transitionPeriods * waveLength
+ val coeff = lerpInvSat(
+ waveProgressPx + length / 2f,
+ waveProgressPx - length / 2f,
+ x)
+ sign * heightFraction * lineAmplitude * coeff
+ } else {
+ sign * heightFraction * lineAmplitude
+ }
}
- var currentX = waveEnd
- var waveSign = if (phaseOffset < waveLength / 2) 1f else -1f
+ // Reset path object to the start
path.rewind()
+ path.moveTo(waveStart, 0f)
- // Draw flat line from end to wave endpoint
- path.moveTo(bounds.width().toFloat(), 0f)
- path.lineTo(waveEnd, 0f)
-
- // First wave has shortened wavelength
- // approx quarter wave gets us to first wave peak
- // shouldn't be big enough to notice it's not a sin wave
- currentX -= phaseOffset % (waveLength / 2)
- val controlRatio = 0.25f
+ // Build the wave, incrementing by half the wavelength each time
+ var currentX = waveStart
+ var waveSign = 1f
var currentAmp = computeAmplitude(currentX, waveSign)
- path.cubicTo(
- waveEnd, currentAmp * controlRatio,
- lerp(currentX, waveEnd, controlRatio), currentAmp,
- currentX, currentAmp)
-
- // Other waves have full wavelength
- val dist = -1 * waveLength / 2f
- while (currentX > waveStart) {
+ val dist = waveLength / 2f
+ while (currentX < waveEnd) {
waveSign = -waveSign
val nextX = currentX + dist
val midX = currentX + dist / 2
@@ -166,34 +164,35 @@ class SquigglyProgress : Drawable() {
currentX = nextX
}
- // Draw path; clip to progress position
+ // translate to the start position of the progress bar for all draw commands
+ val clipTop = lineAmplitude + strokeWidth
canvas.save()
canvas.translate(bounds.left.toFloat(), bounds.centerY().toFloat())
- canvas.clipRect(
- 0f,
- -lineAmplitude - strokeWidth,
- totalProgressPx,
- lineAmplitude + strokeWidth)
- canvas.drawPath(path, wavePaint)
- canvas.restore()
- // Draw path; clip between progression position & far edge
+ // Draw path up to progress position
canvas.save()
- canvas.translate(bounds.left.toFloat(), bounds.centerY().toFloat())
- canvas.clipRect(
- totalProgressPx,
- -lineAmplitude - strokeWidth,
- bounds.width().toFloat(),
- lineAmplitude + strokeWidth)
- canvas.drawPath(path, linePaint)
+ canvas.clipRect(0f, -1f * clipTop, totalProgressPx, clipTop)
+ canvas.drawPath(path, wavePaint)
canvas.restore()
+ if (transitionEnabled) {
+ // If there's a smooth transition, we draw the rest of the
+ // path in a different color (using different clip params)
+ canvas.save()
+ canvas.clipRect(totalProgressPx, -1f * clipTop, totalWidth, clipTop)
+ canvas.drawPath(path, linePaint)
+ canvas.restore()
+ } else {
+ // No transition, just draw a flat line to the end of the region.
+ // The discontinuity is hidden by the progress bar thumb shape.
+ canvas.drawLine(totalProgressPx, 0f, totalWidth, 0f, linePaint)
+ }
+
// Draw round line cap at the beginning of the wave
- val startAmp = cos(abs(waveEnd - phaseOffset) / waveLength * TWO_PI)
- canvas.drawPoint(
- bounds.left.toFloat(),
- bounds.centerY() + startAmp * lineAmplitude * heightFraction,
- wavePaint)
+ val startAmp = cos(abs(waveStart) / waveLength * TWO_PI)
+ canvas.drawPoint(0f, startAmp * lineAmplitude * heightFraction, wavePaint)
+
+ canvas.restore()
}
override fun getOpacity(): Int {
@@ -233,4 +232,4 @@ class SquigglyProgress : Drawable() {
linePaint.color = ColorUtils.setAlphaComponent(tintColor,
(DISABLED_ALPHA * (alpha / 255f)).toInt())
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index c2a82a76d3e8..a31500c6bb18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -49,7 +49,6 @@ import javax.inject.Inject;
/** Quick settings tile: Invert colors **/
public class ColorInversionTile extends QSTileImpl<BooleanState> {
- private final Icon mIcon = ResourceIcon.get(drawable.ic_invert_colors);
private final SettingObserver mSetting;
@Inject
@@ -123,7 +122,9 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> {
state.value = enabled;
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.label = mContext.getString(R.string.quick_settings_inversion_label);
- state.icon = mIcon;
+ state.icon = ResourceIcon.get(state.value
+ ? drawable.qs_invert_colors_icon_on
+ : drawable.qs_invert_colors_icon_off);
state.expandedAccessibilityClassName = Switch.class.getName();
state.contentDescription = state.label;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 9fdf5940c392..2fc99f323611 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -128,13 +128,13 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.value = arg instanceof Boolean ? (Boolean) arg
+ state.value = arg instanceof Boolean ? ((Boolean) arg).booleanValue()
: mDataSaverController.isDataSaverEnabled();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.label = mContext.getString(R.string.data_saver);
state.contentDescription = state.label;
- state.icon = ResourceIcon.get(state.value ? R.drawable.ic_data_saver
- : R.drawable.ic_data_saver_off);
+ state.icon = ResourceIcon.get(state.value ? R.drawable.qs_data_saver_icon_on
+ : R.drawable.qs_data_saver_icon_off);
state.expandedAccessibilityClassName = Switch.class.getName();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 73b0896b06a5..a74792687289 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -43,11 +43,12 @@ import com.android.systemui.statusbar.policy.FlashlightController;
import javax.inject.Inject;
-/** Quick settings tile: Control flashlight **/
+/**
+ * Quick settings tile: Control flashlight
+ **/
public class FlashlightTile extends QSTileImpl<BooleanState> implements
FlashlightController.FlashlightListener {
- private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_flashlight);
private final FlashlightController mFlashlightController;
@Inject
@@ -116,19 +117,15 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- if (state.slash == null) {
- state.slash = new SlashState();
- }
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
state.secondaryLabel = "";
state.stateDescription = "";
if (!mFlashlightController.isAvailable()) {
- state.icon = mIcon;
- state.slash.isSlashed = true;
state.secondaryLabel = mContext.getString(
R.string.quick_settings_flashlight_camera_in_use);
state.stateDescription = state.secondaryLabel;
state.state = Tile.STATE_UNAVAILABLE;
+ state.icon = ResourceIcon.get(R.drawable.qs_flashlight_icon_off);
return;
}
if (arg instanceof Boolean) {
@@ -140,11 +137,11 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
} else {
state.value = mFlashlightController.isEnabled();
}
- state.icon = mIcon;
- state.slash.isSlashed = !state.value;
state.contentDescription = mContext.getString(R.string.quick_settings_flashlight_label);
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.icon = ResourceIcon.get(state.value
+ ? R.drawable.qs_flashlight_icon_on : R.drawable.qs_flashlight_icon_off);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 81813db5abfd..0e9f6599522f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -144,9 +144,10 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements
protected void handleUpdateState(BooleanState state, Object arg) {
state.value = mManager.isNightDisplayActivated();
state.label = mContext.getString(R.string.quick_settings_night_display_label);
- state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_night_display_on);
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.icon = ResourceIcon.get(state.value ? R.drawable.qs_nightlight_icon_on
+ : R.drawable.qs_nightlight_icon_off);
state.secondaryLabel = getSecondaryLabel(state.value);
state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
? state.label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index 6921ad549626..1dac33909ba7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -49,7 +49,6 @@ import javax.inject.Named;
public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState>
implements ReduceBrightColorsController.Listener{
- private final Icon mIcon = ResourceIcon.get(drawable.ic_reduce_bright_colors);
private final boolean mIsAvailable;
private final ReduceBrightColorsController mReduceBrightColorsController;
private boolean mIsListening;
@@ -111,7 +110,9 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState>
state.label = mContext.getString(R.string.reduce_bright_colors_feature_name);
state.expandedAccessibilityClassName = Switch.class.getName();
state.contentDescription = state.label;
- state.icon = mIcon;
+ state.icon = ResourceIcon.get(state.value
+ ? drawable.qs_extra_dim_icon_on
+ : drawable.qs_extra_dim_icon_off);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 75fb393d84b5..f63f0444210b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -120,7 +120,9 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
state.value = isRecording || isStarting;
state.state = (isRecording || isStarting) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.label = mContext.getString(R.string.quick_settings_screen_record_label);
- state.icon = ResourceIcon.get(R.drawable.ic_screenrecord);
+ state.icon = ResourceIcon.get(state.value
+ ? R.drawable.qs_screen_record_icon_on
+ : R.drawable.qs_screen_record_icon_off);
// Show expand icon when clicking will open a dialog
state.forceExpandIcon = state.state == Tile.STATE_INACTIVE;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 6eb54f799a24..0dfb2f4063a9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -197,6 +197,8 @@ public class InternetDialog extends SystemUIDialog implements
mUiEventLogger.log(InternetDialogEvent.INTERNET_DIALOG_SHOW);
mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog,
null);
+ mDialogView.setAccessibilityPaneTitle(
+ mContext.getText(R.string.accessibility_desc_quick_settings));
final Window window = getWindow();
window.setContentView(mDialogView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4c5c23cd3b2f..786714716596 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -667,9 +667,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private void setDozing(boolean dozing) {
if (mDozing != dozing) {
mDozing = dozing;
- if (dozing || mBouncer.needsFullscreenBouncer() || mOccluded) {
- reset(dozing /* hideBouncerWhenShowing */);
- }
+ reset(true /* hideBouncerWhenShowing */);
updateStates();
if (!dozing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
deleted file mode 100644
index fe846747d0c6..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.pipeline
-
-import android.content.Context
-import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
-import javax.inject.Inject
-import javax.inject.Provider
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.launch
-
-/**
- * A temporary object that collects on [WifiViewModel] flows for debugging purposes.
- *
- * This will eventually get migrated to a view binder that will use the flow outputs to set state on
- * views. For now, this just collects on flows so that the information gets logged.
- */
-@SysUISingleton
-class ConnectivityInfoProcessor @Inject constructor(
- context: Context,
- // TODO(b/238425913): Don't use the application scope; instead, use the status bar view's
- // scope so we only do work when there's UI that cares about it.
- @Application private val scope: CoroutineScope,
- private val statusBarPipelineFlags: StatusBarPipelineFlags,
- private val wifiViewModelProvider: Provider<WifiViewModel>,
-) : CoreStartable(context) {
- override fun start() {
- if (!statusBarPipelineFlags.isNewPipelineBackendEnabled()) {
- return
- }
- // TODO(b/238425913): The view binder should do this instead. For now, do it here so we can
- // see the logs.
- scope.launch {
- wifiViewModelProvider.get().isActivityInVisible.collect { }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 681dc6fc13a2..9a7c3fae780c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -16,25 +16,15 @@
package com.android.systemui.statusbar.pipeline.dagger
-import com.android.systemui.CoreStartable
-import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
import dagger.Binds
import dagger.Module
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
@Module
abstract class StatusBarPipelineModule {
- /** Inject into ConnectivityInfoProcessor. */
- @Binds
- @IntoMap
- @ClassKey(ConnectivityInfoProcessor::class)
- abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
-
@Binds
abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index e7fa6d239012..aae0f93a0e19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -27,7 +27,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.Log;
import androidx.annotation.NonNull;
@@ -37,6 +36,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.bluetooth.BluetoothLogger;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -57,9 +57,9 @@ import javax.inject.Inject;
public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
CachedBluetoothDevice.Callback, LocalBluetoothProfileManager.ServiceListener {
private static final String TAG = "BluetoothController";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final DumpManager mDumpManager;
+ private final BluetoothLogger mLogger;
private final LocalBluetoothManager mLocalBluetoothManager;
private final UserManager mUserManager;
private final int mCurrentUser;
@@ -70,6 +70,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
private final List<CachedBluetoothDevice> mConnectedDevices = new ArrayList<>();
private boolean mEnabled;
+ @ConnectionState
private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
private boolean mAudioProfileOnly;
private boolean mIsActive;
@@ -83,10 +84,12 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
public BluetoothControllerImpl(
Context context,
DumpManager dumpManager,
+ BluetoothLogger logger,
@Background Looper bgLooper,
@Main Looper mainLooper,
@Nullable LocalBluetoothManager localBluetoothManager) {
mDumpManager = dumpManager;
+ mLogger = logger;
mLocalBluetoothManager = localBluetoothManager;
mBgHandler = new Handler(bgLooper);
mHandler = new H(mainLooper);
@@ -116,7 +119,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
return;
}
pw.print(" mEnabled="); pw.println(mEnabled);
- pw.print(" mConnectionState="); pw.println(stateToString(mConnectionState));
+ pw.print(" mConnectionState="); pw.println(connectionStateToString(mConnectionState));
pw.print(" mAudioProfileOnly="); pw.println(mAudioProfileOnly);
pw.print(" mIsActive="); pw.println(mIsActive);
pw.print(" mConnectedDevices="); pw.println(getConnectedDevices());
@@ -127,7 +130,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
}
}
- private static String stateToString(int state) {
+ private static String connectionStateToString(@ConnectionState int state) {
switch (state) {
case BluetoothAdapter.STATE_CONNECTED:
return "CONNECTED";
@@ -320,8 +323,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
}
@Override
- public void onBluetoothStateChanged(int bluetoothState) {
- if (DEBUG) Log.d(TAG, "BluetoothStateChanged=" + stateToString(bluetoothState));
+ public void onBluetoothStateChanged(@AdapterState int bluetoothState) {
+ mLogger.logStateChange(BluetoothAdapter.nameForState(bluetoothState));
mEnabled = bluetoothState == BluetoothAdapter.STATE_ON
|| bluetoothState == BluetoothAdapter.STATE_TURNING_ON;
mState = bluetoothState;
@@ -330,24 +333,25 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
}
@Override
- public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
- if (DEBUG) Log.d(TAG, "DeviceAdded=" + cachedDevice.getAddress());
+ public void onDeviceAdded(@NonNull CachedBluetoothDevice cachedDevice) {
+ mLogger.logDeviceAdded(cachedDevice.getAddress());
cachedDevice.registerCallback(this);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
- public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
- if (DEBUG) Log.d(TAG, "DeviceDeleted=" + cachedDevice.getAddress());
+ public void onDeviceDeleted(@NonNull CachedBluetoothDevice cachedDevice) {
+ mLogger.logDeviceDeleted(cachedDevice.getAddress());
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
- public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
- if (DEBUG) Log.d(TAG, "DeviceBondStateChanged=" + cachedDevice.getAddress());
+ public void onDeviceBondStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int bondState) {
+ mLogger.logBondStateChange(cachedDevice.getAddress(), bondState);
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
@@ -355,50 +359,47 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
@Override
public void onDeviceAttributesChanged() {
- if (DEBUG) Log.d(TAG, "DeviceAttributesChanged");
+ mLogger.logDeviceAttributesChanged();
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
- public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
- if (DEBUG) {
- Log.d(TAG, "ConnectionStateChanged=" + cachedDevice.getAddress() + " "
- + stateToString(state));
- }
+ public void onConnectionStateChanged(
+ @Nullable CachedBluetoothDevice cachedDevice,
+ @ConnectionState int state) {
+ mLogger.logDeviceConnectionStateChanged(
+ getAddressOrNull(cachedDevice), connectionStateToString(state));
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
- public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
- int state, int bluetoothProfile) {
- if (DEBUG) {
- Log.d(TAG, "ProfileConnectionStateChanged=" + cachedDevice.getAddress() + " "
- + stateToString(state) + " profileId=" + bluetoothProfile);
- }
+ public void onProfileConnectionStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice,
+ @ConnectionState int state,
+ int bluetoothProfile) {
+ mLogger.logProfileConnectionStateChanged(
+ cachedDevice.getAddress(), connectionStateToString(state), bluetoothProfile);
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
- public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
- if (DEBUG) {
- Log.d(TAG, "ActiveDeviceChanged=" + activeDevice.getAddress()
- + " profileId=" + bluetoothProfile);
- }
+ public void onActiveDeviceChanged(
+ @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+ mLogger.logActiveDeviceChanged(getAddressOrNull(activeDevice), bluetoothProfile);
updateActive();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
- public void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
- if (DEBUG) {
- Log.d(TAG, "ACLConnectionStateChanged=" + cachedDevice.getAddress() + " "
- + stateToString(state));
- }
+ public void onAclConnectionStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int state) {
+ mLogger.logAclConnectionStateChanged(
+ cachedDevice.getAddress(), connectionStateToString(state));
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
@@ -415,6 +416,11 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
return state;
}
+ @Nullable
+ private String getAddressOrNull(@Nullable CachedBluetoothDevice device) {
+ return device == null ? null : device.getAddress();
+ }
+
@Override
public void onServiceConnected() {
updateConnected();
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
index 83a3d0d0457a..d7ad3cefaf06 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
@@ -129,7 +129,10 @@ object UserSwitcherViewBinder {
viewModel.users.collect { users ->
val viewPool =
view.children.filter { it.tag == USER_VIEW_TAG }.toMutableList()
- viewPool.forEach { view.removeView(it) }
+ viewPool.forEach {
+ view.removeView(it)
+ flowWidget.removeView(it)
+ }
users.forEach { userViewModel ->
val userView =
if (viewPool.isNotEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index 66ce01b7a86e..398341d256d2 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -20,6 +20,7 @@ package com.android.systemui.user.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.systemui.R
+import com.android.systemui.common.ui.drawable.CircularDrawable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
@@ -130,7 +131,7 @@ private constructor(
return UserViewModel(
viewKey = model.id,
name = model.name,
- image = model.image,
+ image = CircularDrawable(model.image),
isSelectionMarkerVisible = model.isSelected,
alpha =
if (model.isSelectable) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index e6a3e74c1caf..3094a8c2eb1b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -48,6 +48,7 @@ import android.app.KeyguardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -74,6 +75,7 @@ import android.os.VibrationEffect;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.InputFilter;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -1047,7 +1049,13 @@ public class VolumeDialogImpl implements VolumeDialog,
Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
dismissH(DISMISS_REASON_SETTINGS_CLICKED);
mMediaOutputDialogFactory.dismiss();
- mVolumePanelFactory.create(true /* aboveStatusBar */, null);
+ if (FeatureFlagUtils.isEnabled(mContext,
+ FeatureFlagUtils.SETTINGS_VOLUME_PANEL_IN_SYSTEMUI)) {
+ mVolumePanelFactory.create(true /* aboveStatusBar */, null);
+ } else {
+ mActivityStarter.startActivity(new Intent(Settings.Panel.ACTION_VOLUME),
+ true /* dismissShade */);
+ }
});
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 914d94522e83..25e7dbb5f26d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -119,6 +119,20 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
+ fun fontChanged_verifyFontSizeUpdated() {
+ clockEventController.clock = clock
+ verify(events).onColorPaletteChanged(any(), any(), any())
+
+ clockEventController.registerListeners()
+
+ val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
+ verify(configurationController).addCallback(capture(captor))
+ captor.value.onDensityOrFontScaleChanged()
+
+ verify(events).onFontSettingChanged()
+ }
+
+ @Test
fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() {
clockEventController.clock = clock
clockEventController.registerListeners()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 0275152c680e..ae980f58cb3f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -328,8 +328,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@After
public void tearDown() {
mMockitoSession.finishMocking();
- mKeyguardUpdateMonitor.removeCallback(mTestCallback);
- mKeyguardUpdateMonitor.destroy();
+ cleanupKeyguardUpdateMonitor();
}
@Test
@@ -351,6 +350,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testSimStateInitialized() {
+ cleanupKeyguardUpdateMonitor();
final int subId = 3;
final int state = TelephonyManager.SIM_STATE_ABSENT;
@@ -1205,7 +1205,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() {
- mFaceManager = null;
+ cleanupKeyguardUpdateMonitor();
+ mSpiedContext.addMockSystemService(FaceManager.class, null);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
@@ -1258,6 +1260,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
+ cleanupKeyguardUpdateMonitor();
// This disables face auth
when(mUserManager.isPrimaryUser()).thenReturn(false);
mKeyguardUpdateMonitor =
@@ -1587,9 +1590,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testFingerAcquired_wakesUpPowerManager() {
- mContext.getOrCreateTestableResources().addOverride(
+ cleanupKeyguardUpdateMonitor();
+ mSpiedContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.kg_wake_on_acquire_start, true);
- mSpiedContext = spy(mContext);
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
fingerprintAcquireStart();
@@ -1598,15 +1601,23 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testFingerAcquired_doesNotWakeUpPowerManager() {
- mContext.getOrCreateTestableResources().addOverride(
+ cleanupKeyguardUpdateMonitor();
+ mSpiedContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.kg_wake_on_acquire_start, false);
- mSpiedContext = spy(mContext);
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
fingerprintAcquireStart();
verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
}
+ private void cleanupKeyguardUpdateMonitor() {
+ if (mKeyguardUpdateMonitor != null) {
+ mKeyguardUpdateMonitor.removeCallback(mTestCallback);
+ mKeyguardUpdateMonitor.destroy();
+ mKeyguardUpdateMonitor = null;
+ }
+ }
+
private void faceAuthLockedOut() {
mKeyguardUpdateMonitor.mFaceAuthenticationCallback
.onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index bf3788e4c76a..4a5b23c02e40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -29,6 +29,7 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.testing.ViewUtils
+import android.view.KeyEvent
import android.view.View
import android.view.WindowInsets
import android.view.WindowManager
@@ -92,6 +93,21 @@ class AuthContainerViewTest : SysuiTestCase() {
}
@Test
+ fun testDismissesOnBack() {
+ val container = initializeFingerprintContainer(addToView = true)
+ assertThat(container.parent).isNotNull()
+ val root = container.rootView
+
+ // Simulate back invocation
+ container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK))
+ container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK))
+ waitForIdleSync()
+
+ assertThat(container.parent).isNull()
+ assertThat(root.isAttachedToWindow).isFalse()
+ }
+
+ @Test
fun testIgnoresAnimatedInWhenDismissed() {
val container = initializeFingerprintContainer(addToView = false)
container.dismissFromSystemServer()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index cb8358dd22cc..5c564e65ea86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -17,23 +17,13 @@
package com.android.systemui.biometrics
import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
-import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
+import android.hardware.biometrics.BiometricOverlayConstants.*
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
-import android.view.LayoutInflater
-import android.view.MotionEvent
-import android.view.View
-import android.view.Surface
+import android.view.*
import android.view.Surface.Rotation
-import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
@@ -65,7 +55,6 @@ import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
-private const val HAL_CONTROLS_ILLUMINATION = true
private const val REQUEST_ID = 2L
// Dimensions for the current display resolution.
@@ -95,8 +84,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var systemClock: SystemClock
@Mock private lateinit var keyguardStateController: KeyguardStateController
- @Mock private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
- @Mock private lateinit var hbmProvider: UdfpsHbmProvider
+ @Mock private lateinit var unlockedScreenOffAnimationController:
+ UnlockedScreenOffAnimationController
+ @Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
@Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var udfpsView: UdfpsView
@@ -130,8 +120,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager,
keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
configurationController, systemClock, keyguardStateController,
- unlockedScreenOffAnimationController, HAL_CONTROLS_ILLUMINATION, hbmProvider,
- REQUEST_ID, reason, controllerCallback, onTouch, activityLaunchAnimator)
+ unlockedScreenOffAnimationController, udfpsDisplayMode, REQUEST_ID, reason,
+ controllerCallback, onTouch, activityLaunchAnimator
+ )
block()
}
@@ -246,7 +237,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
val didShow = controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(eq(controllerOverlay.overlayView), any())
- verify(udfpsView).setHbmProvider(eq(hbmProvider))
+ verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode))
verify(udfpsView).animationViewController = any()
verify(udfpsView).addView(any())
@@ -351,12 +342,12 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
}
@Test
- fun stopIlluminatingOnHide() = withReason(REASON_AUTH_BP) {
- whenever(udfpsView.isIlluminationRequested).thenReturn(true)
+ fun unconfigureDisplayOnHide() = withReason(REASON_AUTH_BP) {
+ whenever(udfpsView.isDisplayConfigured).thenReturn(true)
controllerOverlay.show(udfpsController, overlayParams)
controllerOverlay.hide()
- verify(udfpsView).stopIllumination()
+ verify(udfpsView).unconfigureDisplay()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 09dc8e4fdb8e..08b1c28f2112 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -125,7 +125,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private WindowManager mWindowManager;
@Mock
- private UdfpsHbmProvider mHbmProvider;
+ private UdfpsDisplayModeProvider mDisplayModeProvider;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
@@ -193,7 +193,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
private IUdfpsOverlayController mOverlayController;
@Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
@Captor private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor;
- @Captor private ArgumentCaptor<Runnable> mOnIlluminatedRunnableCaptor;
+ @Captor private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor;
@Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
private ScreenLifecycle.Observer mScreenObserver;
@@ -256,7 +256,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mVibrator,
mUdfpsHapticsSimulator,
mUdfpsShell,
- Optional.of(mHbmProvider),
+ Optional.of(mDisplayModeProvider),
mKeyguardStateController,
mDisplayManager,
mHandler,
@@ -506,7 +506,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
final float expectedMajor = touchMajor / scaleFactor;
// Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// Show the overlay.
@@ -584,7 +584,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void fingerDown() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
@@ -611,12 +611,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
anyFloat(), anyFloat());
verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- // AND illumination begins
- verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
+ // AND display configuration begins
+ verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
- // AND onIlluminatedRunnable notifies FingerprintManager about onUiReady
- mOnIlluminatedRunnableCaptor.getValue().run();
+ // AND onDisplayConfigured notifies FingerprintManager about onUiReady
+ mOnDisplayConfiguredCaptor.getValue().run();
mBiometricsExecutor.runAllReady();
InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
inOrder.verify(mAlternateTouchProvider).onUiReady();
@@ -634,10 +634,10 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
mFgExecutor.runAllReady();
- // THEN illumination begins
- // AND onIlluminatedRunnable that notifies FingerprintManager is set
- verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
- mOnIlluminatedRunnableCaptor.getValue().run();
+ // THEN display configuration begins
+ // AND onDisplayConfigured notifies FingerprintManager about onUiReady
+ verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
+ mOnDisplayConfiguredCaptor.getValue().run();
mBiometricsExecutor.runAllReady();
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */);
@@ -655,11 +655,11 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFgExecutor.runAllReady();
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- when(mUdfpsView.isIlluminationRequested()).thenReturn(true);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
// WHEN it is cancelled
mUdfpsController.onCancelUdfps();
- // THEN the illumination is hidden
- verify(mUdfpsView).stopIllumination();
+ // THEN the display is unconfigured
+ verify(mUdfpsView).unconfigureDisplay();
}
@Test
@@ -672,12 +672,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- when(mUdfpsView.isIlluminationRequested()).thenReturn(true);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
// WHEN it times out
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- // THEN the illumination is hidden
- verify(mUdfpsView).stopIllumination();
+ // THEN the display is unconfigured
+ verify(mUdfpsView).unconfigureDisplay();
}
@Test
@@ -692,8 +692,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- // THEN no illumination because screen is off
- verify(mUdfpsView, never()).startIllumination(any());
+ // THEN display doesn't get configured because it's off
+ verify(mUdfpsView, never()).configureDisplay(any());
}
@Test
@@ -709,14 +709,14 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- // THEN no illumination because screen is off
- verify(mUdfpsView, never()).startIllumination(any());
+ // THEN display doesn't get configured because it's off
+ verify(mUdfpsView, never()).configureDisplay(any());
}
@Test
public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing and a11y touch exploration enabled
@@ -751,7 +751,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing and a11y touch exploration NOT enabled
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index 0327cfcf3450..b78c06391057 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
@@ -36,13 +36,12 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.nullable
import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
private const val SENSOR_X = 50
private const val SENSOR_Y = 250
@@ -57,7 +56,7 @@ class UdfpsViewTest : SysuiTestCase() {
var rule = MockitoJUnit.rule()
@Mock
- lateinit var hbmProvider: UdfpsHbmProvider
+ lateinit var hbmProvider: UdfpsDisplayModeProvider
@Mock
lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView>
@@ -66,13 +65,11 @@ class UdfpsViewTest : SysuiTestCase() {
@Before
fun setup() {
context.setTheme(R.style.Theme_AppCompat)
- context.orCreateTestableResources.addOverride(
- com.android.internal.R.integer.config_udfps_illumination_transition_ms, 0)
view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
view.animationViewController = animationViewController
val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
view.overlayParams = UdfpsOverlayParams(sensorBounds, 1920, 1080, 1f, Surface.ROTATION_0)
- view.setHbmProvider(hbmProvider)
+ view.setUdfpsDisplayModeProvider(hbmProvider)
ViewUtils.attachView(view)
}
@@ -143,27 +140,27 @@ class UdfpsViewTest : SysuiTestCase() {
@Test
fun startAndStopIllumination() {
val onDone: Runnable = mock()
- view.startIllumination(onDone)
+ view.configureDisplay(onDone)
val illuminator = withArgCaptor<Runnable> {
- verify(hbmProvider).enableHbm(anyBoolean(), capture())
+ verify(hbmProvider).enable(capture())
}
- assertThat(view.isIlluminationRequested).isTrue()
- verify(animationViewController).onIlluminationStarting()
- verify(animationViewController, never()).onIlluminationStopped()
+ assertThat(view.isDisplayConfigured).isTrue()
+ verify(animationViewController).onDisplayConfiguring()
+ verify(animationViewController, never()).onDisplayUnconfigured()
verify(onDone, never()).run()
// fake illumination event
illuminator.run()
waitForLooper()
verify(onDone).run()
- verify(hbmProvider, never()).disableHbm(any())
+ verify(hbmProvider, never()).disable(any())
- view.stopIllumination()
- assertThat(view.isIlluminationRequested).isFalse()
- verify(animationViewController).onIlluminationStopped()
- verify(hbmProvider).disableHbm(nullable(Runnable::class.java))
+ view.unconfigureDisplay()
+ assertThat(view.isDisplayConfigured).isFalse()
+ verify(animationViewController).onDisplayUnconfigured()
+ verify(hbmProvider).disable(nullable(Runnable::class.java))
}
private fun waitForLooper() = TestableLooper.get(this).processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
new file mode 100644
index 000000000000..08fe7c486529
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 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.clipboardoverlay;
+
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IntentCreatorTest extends SysuiTestCase {
+ private static final int EXTERNAL_INTENT_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION;
+
+ @Test
+ public void test_getTextEditorIntent() {
+ Intent intent = IntentCreator.getTextEditorIntent(getContext());
+ assertEquals(new ComponentName(getContext(), EditTextActivity.class),
+ intent.getComponent());
+ assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+
+ @Test
+ public void test_getRemoteCopyIntent() {
+ getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage,
+ "");
+
+ ClipData clipData = ClipData.newPlainText("Test", "Test Item");
+ Intent intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
+
+ assertEquals(null, intent.getComponent());
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+ assertEquals(clipData, intent.getClipData());
+
+ // Try again with a remote copy component
+ ComponentName fakeComponent = new ComponentName("com.android.remotecopy",
+ "com.android.remotecopy.RemoteCopyActivity");
+ getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage,
+ fakeComponent.flattenToString());
+
+ intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
+ assertEquals(fakeComponent, intent.getComponent());
+ }
+
+ @Test
+ public void test_getImageEditIntent() {
+ getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
+ "");
+ Uri fakeUri = Uri.parse("content://foo");
+ Intent intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
+
+ assertEquals(Intent.ACTION_EDIT, intent.getAction());
+ assertEquals("image/*", intent.getType());
+ assertEquals(null, intent.getComponent());
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+
+ // try again with an editor component
+ ComponentName fakeComponent = new ComponentName("com.android.remotecopy",
+ "com.android.remotecopy.RemoteCopyActivity");
+ getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
+ fakeComponent.flattenToString());
+ intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
+ assertEquals(fakeComponent, intent.getComponent());
+ }
+
+ @Test
+ public void test_getShareIntent_plaintext() {
+ ClipData clipData = ClipData.newPlainText("Test", "Test Item");
+ Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+ Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+ assertEquals("Test Item", target.getStringExtra(Intent.EXTRA_TEXT));
+ assertEquals("text/plain", target.getType());
+ }
+
+ @Test
+ public void test_getShareIntent_html() {
+ ClipData clipData = ClipData.newHtmlText("Test", "Some HTML",
+ "<b>Some HTML</b>");
+ Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+ Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+ assertEquals("Some HTML", target.getStringExtra(Intent.EXTRA_TEXT));
+ assertEquals("text/plain", target.getType());
+ }
+
+ @Test
+ public void test_getShareIntent_image() {
+ Uri uri = Uri.parse("content://something");
+ ClipData clipData = new ClipData("Test", new String[]{"image/png"},
+ new ClipData.Item(uri));
+ Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+ Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+ assertEquals(uri, target.getData());
+ assertEquals("image/png", target.getType());
+ }
+
+ // Assert that the given flags are set
+ private void assertFlags(Intent intent, int flags) {
+ assertTrue((intent.getFlags() & flags) == flags);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index bef46953395b..7de5719c03ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -591,14 +591,20 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindAlbumView_bitmapInLaterStates_setAfterExecutors() {
- val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
- val canvas = Canvas(bmp)
- canvas.drawColor(Color.RED)
- val albumArt = Icon.createWithBitmap(bmp)
+ val redBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+ val redCanvas = Canvas(redBmp)
+ redCanvas.drawColor(Color.RED)
+ val redArt = Icon.createWithBitmap(redBmp)
+
+ val greenBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+ val greenCanvas = Canvas(greenBmp)
+ greenCanvas.drawColor(Color.GREEN)
+ val greenArt = Icon.createWithBitmap(greenBmp)
val state0 = mediaData.copy(artwork = null)
- val state1 = mediaData.copy(artwork = albumArt)
- val state2 = mediaData.copy(artwork = albumArt)
+ val state1 = mediaData.copy(artwork = redArt)
+ val state2 = mediaData.copy(artwork = redArt)
+ val state3 = mediaData.copy(artwork = greenArt)
player.attachPlayer(viewHolder)
// First binding sets (empty) drawable
@@ -627,6 +633,12 @@ public class MediaControlPanelTest : SysuiTestCase() {
bgExecutor.runAllReady()
mainExecutor.runAllReady()
verify(albumView, times(2)).setImageDrawable(any(Drawable::class.java))
+
+ // Fourth binding to new image runs transition due to color scheme change
+ player.bindPlayer(state3, PACKAGE)
+ bgExecutor.runAllReady()
+ mainExecutor.runAllReady()
+ verify(albumView, times(3)).setImageDrawable(any(Drawable::class.java))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
index bf682a8d4e0a..3fd25019e2a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
@@ -33,12 +33,15 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -54,6 +57,8 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class ColorInversionTileTest extends SysuiTestCase {
+ private static final Integer COLOR_INVERSION_DISABLED = 0;
+ private static final Integer COLOR_INVERSION_ENABLED = 1;
@Mock
private QSTileHost mHost;
@@ -113,4 +118,24 @@ public class ColorInversionTileTest extends SysuiTestCase {
assertThat(IntentCaptor.getValue().getAction()).isEqualTo(
Settings.ACTION_COLOR_INVERSION_SETTINGS);
}
+
+ @Test
+ public void testIcon_whenColorInversionDisabled_isOffState() {
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, COLOR_INVERSION_DISABLED);
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_invert_colors_icon_off));
+ }
+
+ @Test
+ public void testIcon_whenColorInversionEnabled_isOnState() {
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, COLOR_INVERSION_ENABLED);
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_invert_colors_icon_on));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
new file mode 100644
index 000000000000..ce62f2d1cf36
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.DataSaverController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class DataSaverTileTest : SysuiTestCase() {
+
+ @Mock private lateinit var mHost: QSHost
+ @Mock private lateinit var mMetricsLogger: MetricsLogger
+ @Mock private lateinit var mStatusBarStateController: StatusBarStateController
+ @Mock private lateinit var mActivityStarter: ActivityStarter
+ @Mock private lateinit var mQsLogger: QSLogger
+ private val falsingManager = FalsingManagerFake()
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var dataSaverController: DataSaverController
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+
+ private val uiEventLogger = UiEventLoggerFake()
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: DataSaverTile
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+
+ Mockito.`when`(mHost.context).thenReturn(mContext)
+ Mockito.`when`(mHost.uiEventLogger).thenReturn(uiEventLogger)
+
+ tile =
+ DataSaverTile(
+ mHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ falsingManager,
+ mMetricsLogger,
+ statusBarStateController,
+ activityStarter,
+ mQsLogger,
+ dataSaverController,
+ dialogLaunchAnimator
+ )
+ }
+
+ @Test
+ fun testIcon_whenDataSaverEnabled_isOnState() {
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, true)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_data_saver_icon_on))
+ }
+
+ @Test
+ fun testIcon_whenDataSaverDisabled_isOffState() {
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, false)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_data_saver_icon_off))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
new file mode 100644
index 000000000000..d0f851bded75
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
@@ -0,0 +1,108 @@
+package com.android.systemui.qs.tiles
+
+import android.content.Context
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.FlashlightController
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class FlashlightTileTest : SysuiTestCase() {
+
+ @Mock private lateinit var mockContext: Context
+
+ @Mock private lateinit var qsLogger: QSLogger
+
+ @Mock private lateinit var qsHost: QSTileHost
+
+ @Mock private lateinit var metricsLogger: MetricsLogger
+
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ @Mock private lateinit var flashlightController: FlashlightController
+
+ private val uiEventLogger = UiEventLoggerFake()
+ private val falsingManager = FalsingManagerFake()
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: FlashlightTile
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+
+ Mockito.`when`(qsHost.context).thenReturn(mockContext)
+ Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+ tile =
+ FlashlightTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ flashlightController
+ )
+ }
+
+ @Test
+ fun testIcon_whenFlashlightEnabled_isOnState() {
+ Mockito.`when`(flashlightController.isAvailable).thenReturn(true)
+ Mockito.`when`(flashlightController.isEnabled).thenReturn(true)
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ Truth.assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_on))
+ }
+
+ @Test
+ fun testIcon_whenFlashlightDisabled_isOffState() {
+ Mockito.`when`(flashlightController.isAvailable).thenReturn(true)
+ Mockito.`when`(flashlightController.isEnabled).thenReturn(false)
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ Truth.assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_off))
+ }
+
+ @Test
+ fun testIcon_whenFlashlightUnavailable_isOffState() {
+ Mockito.`when`(flashlightController.isAvailable).thenReturn(false)
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ Truth.assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_off))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
new file mode 100644
index 000000000000..188c3a3d9e42
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.hardware.display.ColorDisplayManager
+import android.hardware.display.NightDisplayListener
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.dagger.NightDisplayListenerModule
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.LocationController
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class NightDisplayTileTest : SysuiTestCase() {
+ @Mock
+ private lateinit var mHost: QSHost
+
+ @Mock
+ private lateinit var mMetricsLogger: MetricsLogger
+
+ @Mock
+ private lateinit var mStatusBarStateController: StatusBarStateController
+
+ @Mock
+ private lateinit var mActivityStarter: ActivityStarter
+
+ @Mock
+ private lateinit var mQsLogger: QSLogger
+
+ @Mock
+ private lateinit var mLocationController: LocationController
+
+ @Mock
+ private lateinit var mColorDisplayManager: ColorDisplayManager
+
+ @Mock
+ private lateinit var mNightDisplayListenerBuilder: NightDisplayListenerModule.Builder
+
+ @Mock
+ private lateinit var mNightDisplayListener: NightDisplayListener
+
+ private lateinit var mTestableLooper: TestableLooper
+ private lateinit var mTile: NightDisplayTile
+
+ private val mUiEventLogger: UiEventLogger = UiEventLoggerFake()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mTestableLooper = TestableLooper.get(this)
+ whenever(mHost.context).thenReturn(mContext)
+ whenever(mHost.uiEventLogger).thenReturn(mUiEventLogger)
+ whenever(mHost.userContext).thenReturn(mContext)
+ whenever(mNightDisplayListenerBuilder.setUser(anyInt())).thenReturn(
+ mNightDisplayListenerBuilder
+ )
+ whenever(mNightDisplayListenerBuilder.build()).thenReturn(mNightDisplayListener)
+
+ mTile = NightDisplayTile(
+ mHost,
+ mTestableLooper.looper,
+ Handler(mTestableLooper.looper),
+ FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQsLogger,
+ mLocationController,
+ mColorDisplayManager,
+ mNightDisplayListenerBuilder
+ )
+ }
+
+ @Test
+ fun testIcon_whenDisabled_showsOffState() {
+ whenever(mColorDisplayManager.isNightDisplayActivated).thenReturn(false)
+ val state = QSTile.BooleanState()
+
+ mTile.handleUpdateState(state, /* arg= */ null)
+
+ Truth.assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_nightlight_icon_off))
+ }
+
+ @Test
+ fun testIcon_whenEnabled_showsOnState() {
+ whenever(mColorDisplayManager.isNightDisplayActivated).thenReturn(true)
+ val state = QSTile.BooleanState()
+
+ mTile.handleUpdateState(state, /* arg= */ null)
+
+ Truth.assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_nightlight_icon_on))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 9eb688de3511..8601d6c0a357 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -32,13 +32,16 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R.drawable;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
import org.junit.Before;
@@ -130,4 +133,26 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
.setReduceBrightColorsActivated(eq(true));
}
+ @Test
+ public void testIcon_whenTileEnabled_isOnState() {
+ when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(true);
+ mTile.refreshState();
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(drawable.qs_extra_dim_icon_on));
+ }
+
+ @Test
+ public void testIcon_whenTileDisabled_isOffState() {
+ when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(false);
+ mTile.refreshState();
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(drawable.qs_extra_dim_icon_off));
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index e6bd396ebc6c..30debdf4b744 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -41,9 +41,11 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -229,4 +231,38 @@ public class ScreenRecordTileTest extends SysuiTestCase {
assertFalse(mTile.getState().forceExpandIcon);
}
+
+ @Test
+ public void testIcon_whenRecording_isOnState() {
+ when(mController.isStarting()).thenReturn(false);
+ when(mController.isRecording()).thenReturn(true);
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_on));
+ }
+
+ @Test
+ public void testIcon_whenStarting_isOnState() {
+ when(mController.isStarting()).thenReturn(true);
+ when(mController.isRecording()).thenReturn(false);
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_on));
+ }
+
+ @Test
+ public void testIcon_whenRecordingOff_isOffState() {
+ when(mController.isStarting()).thenReturn(false);
+ when(mController.isRecording()).thenReturn(false);
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_off));
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index d09a5a11040f..f92247580df0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -143,6 +143,12 @@ public class InternetDialogTest extends SysuiTestCase {
}
@Test
+ public void createInternetDialog_setAccessibilityPaneTitleToQuickSettings() {
+ assertThat(mDialogView.getAccessibilityPaneTitle())
+ .isEqualTo(mContext.getText(R.string.accessibility_desc_quick_settings));
+ }
+
+ @Test
public void hideWifiViews_WifiViewsGone() {
mInternetDialog.hideWifiViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 6ce9cff9a730..002ef2962b03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -34,6 +34,7 @@ import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
+import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.ScreenshotHelper
import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
@@ -65,6 +66,7 @@ private const val USER_ID = 1
private const val TASK_ID = 1
@RunWith(AndroidTestingRunner::class)
+@SmallTest
class TakeScreenshotServiceTest : SysuiTestCase() {
private val application = mock<Application>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
new file mode 100644
index 000000000000..eb34561d15a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 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.shared.clocks
+
+import android.testing.AndroidTestingRunner
+import android.view.LayoutInflater
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.TextAnimator
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class AnimatableClockViewTest : SysuiTestCase() {
+
+ @JvmField @Rule val mockito = MockitoJUnit.rule()
+
+ @Mock private lateinit var mockTextAnimator: TextAnimator
+ private lateinit var clockView: AnimatableClockView
+
+ @Before
+ fun setUp() {
+ val layoutInflater = LayoutInflater.from(context)
+ clockView =
+ layoutInflater.inflate(R.layout.clock_default_small, null) as AnimatableClockView
+ clockView.textAnimatorFactory = { _, _ -> mockTextAnimator }
+ }
+
+ @Test
+ fun validateColorAnimationRunsBeforeMeasure() {
+ clockView.setColors(100, 200)
+ clockView.animateAppearOnLockscreen()
+ clockView.measure(50, 50)
+
+ verify(mockTextAnimator).glyphFilter = null
+ verify(mockTextAnimator).setTextStyle(300, -1.0f, 200, false, 350L, null, 0L, null)
+ verifyNoMoreInteractions(mockTextAnimator)
+ }
+
+ @Test
+ fun validateColorAnimationRunsAfterMeasure() {
+ clockView.setColors(100, 200)
+ clockView.measure(50, 50)
+ clockView.animateAppearOnLockscreen()
+
+ verify(mockTextAnimator, times(2)).glyphFilter = null
+ verify(mockTextAnimator).setTextStyle(100, -1.0f, 200, false, 0L, null, 0L, null)
+ verify(mockTextAnimator).setTextStyle(300, -1.0f, 200, true, 350L, null, 0L, null)
+ verifyNoMoreInteractions(mockTextAnimator)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index e790d85763d0..a4453f854ce5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -505,4 +506,21 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mBouncerExpansionCallback.onVisibilityChanged(false);
verify(mCentralSurfaces).setBouncerShowingOverDream(false);
}
+
+ @Test
+ public void testSetDozing_Dozing() {
+ clearInvocations(mBouncer);
+ mStatusBarKeyguardViewManager.onDozingChanged(true);
+ // Once when shown and once with dozing changed.
+ verify(mBouncer, times(1)).hide(false);
+ }
+
+ @Test
+ public void testSetDozing_notDozing() {
+ mStatusBarKeyguardViewManager.onDozingChanged(true);
+ clearInvocations(mBouncer);
+ mStatusBarKeyguardViewManager.onDozingChanged(false);
+ // Once when shown and twice with dozing changed.
+ verify(mBouncer, times(1)).hide(false);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 3dd36d134cf7..d0391ac0795c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -41,6 +41,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bluetooth.BluetoothLogger;
import com.android.systemui.dump.DumpManager;
import org.junit.Before;
@@ -81,6 +82,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
mBluetoothControllerImpl = new BluetoothControllerImpl(mContext,
mMockDumpManager,
+ mock(BluetoothLogger.class),
mTestableLooper.getLooper(),
mTestableLooper.getLooper(),
mMockBluetoothManager);
@@ -233,4 +235,11 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
assertTrue(mBluetoothControllerImpl.isBluetoothAudioActive());
assertTrue(mBluetoothControllerImpl.isBluetoothAudioProfileOnly());
}
+
+ /** Regression test for b/246876230. */
+ @Test
+ public void testOnActiveDeviceChanged_null_noCrash() {
+ mBluetoothControllerImpl.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
+ // No assert, just need no crash.
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 99c849527034..6417db0eb050 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1109,7 +1109,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final List<AccessibilityServiceInfo> result = new ArrayList<>(serviceCount);
for (int i = 0; i < serviceCount; ++i) {
final AccessibilityServiceConnection service = services.get(i);
- if ((service.mFeedbackType & feedbackType) != 0) {
+ if ((service.mFeedbackType & feedbackType) != 0
+ || feedbackType == AccessibilityServiceInfo.FEEDBACK_ALL_MASK) {
result.add(service.getServiceInfo());
}
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 0fac808fb13b..4d55d4e545ee 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1786,8 +1786,8 @@ abstract public class ManagedServices {
* from receiving events from the profile.
*/
public boolean isPermittedForProfile(int userId) {
- if (!mUserProfiles.canProfileUseBoundServices(userId)) {
- return false;
+ if (!mUserProfiles.isProfileUser(userId)) {
+ return true;
}
DevicePolicyManager dpm =
(DevicePolicyManager) mContext.getSystemService(DEVICE_POLICY_SERVICE);
@@ -1862,16 +1862,16 @@ abstract public class ManagedServices {
}
}
- public boolean canProfileUseBoundServices(int userId) {
+ public boolean isProfileUser(int userId) {
synchronized (mCurrentProfiles) {
UserInfo user = mCurrentProfiles.get(userId);
if (user == null) {
return false;
}
if (user.isManagedProfile() || user.isCloneProfile()) {
- return false;
+ return true;
}
- return true;
+ return false;
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4e0cc61a061f..f11801f61d58 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1828,7 +1828,7 @@ public class NotificationManagerService extends SystemService {
} else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
- if (mUserProfiles.canProfileUseBoundServices(userId)) {
+ if (!mUserProfiles.isProfileUser(userId)) {
// reload per-user settings
mSettingsObserver.update(null);
// Refresh managed services
@@ -1842,7 +1842,7 @@ public class NotificationManagerService extends SystemService {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
if (userId != USER_NULL) {
mUserProfiles.updateCache(context);
- if (mUserProfiles.canProfileUseBoundServices(userId)) {
+ if (!mUserProfiles.isProfileUser(userId)) {
allowDefaultApprovedServices(userId);
}
}
@@ -1860,7 +1860,7 @@ public class NotificationManagerService extends SystemService {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
mAssistants.onUserUnlocked(userId);
- if (mUserProfiles.canProfileUseBoundServices(userId)) {
+ if (!mUserProfiles.isProfileUser(userId)) {
mConditionProviders.onUserUnlocked(userId);
mListeners.onUserUnlocked(userId);
mZenModeHelper.onUserUnlocked(userId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4c0a0171024a..2dbeee96da63 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8042,11 +8042,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Horizontal position
int offsetX = 0;
if (parentBounds.width() != screenResolvedBounds.width()) {
- if (screenResolvedBounds.width() >= parentAppBounds.width()) {
- // If resolved bounds overlap with insets, center within app bounds.
- offsetX = getCenterOffset(
- parentAppBounds.width(), screenResolvedBounds.width());
- } else {
+ if (screenResolvedBounds.width() <= parentAppBounds.width()) {
float positionMultiplier =
mLetterboxUiController.getHorizontalPositionMultiplier(
newParentConfiguration);
@@ -8058,11 +8054,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Vertical position
int offsetY = 0;
if (parentBounds.height() != screenResolvedBounds.height()) {
- if (screenResolvedBounds.height() >= parentAppBounds.height()) {
- // If resolved bounds overlap with insets, center within app bounds.
- offsetY = getCenterOffset(
- parentAppBounds.height(), screenResolvedBounds.height());
- } else {
+ if (screenResolvedBounds.height() <= parentAppBounds.height()) {
float positionMultiplier =
mLetterboxUiController.getVerticalPositionMultiplier(
newParentConfiguration);
@@ -8080,6 +8072,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
offsetBounds(resolvedConfig, offsetX, offsetY);
}
+ // If the top is aligned with parentAppBounds add the vertical insets back so that the app
+ // content aligns with the status bar
+ if (resolvedConfig.windowConfiguration.getAppBounds().top == parentAppBounds.top) {
+ resolvedConfig.windowConfiguration.getBounds().top = parentBounds.top;
+ if (mSizeCompatBounds != null) {
+ mSizeCompatBounds.top = parentBounds.top;
+ }
+ }
+
// Since bounds has changed, the configuration needs to be computed accordingly.
getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
@@ -8457,7 +8458,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
final boolean fillContainer = resolvedBounds.equals(containingBounds);
final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
- final int screenPosY = fillContainer ? containerBounds.top : containerAppBounds.top;
+ final int screenPosY = fillContainer ? containerBounds.top : containerAppBounds.top;
if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
@@ -8803,24 +8804,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Also account for the insets (e.g. display cutouts, navigation bar), which will be
// clipped away later in {@link Task#computeConfigResourceOverrides()}, i.e., the out
// bounds are the app bounds restricted by aspect ratio + clippable insets. Otherwise,
- // the app bounds would end up too small.
+ // the app bounds would end up too small. To achieve this we will also add clippable insets
+ // when the corresponding dimension fully fills the parent
+
int right = activityWidth + containingAppBounds.left;
+ int left = containingAppBounds.left;
if (right >= containingAppBounds.right) {
- right += containingBounds.right - containingAppBounds.right;
+ right = containingBounds.right;
+ left = containingBounds.left;
}
int bottom = activityHeight + containingAppBounds.top;
+ int top = containingAppBounds.top;
if (bottom >= containingAppBounds.bottom) {
- bottom += containingBounds.bottom - containingAppBounds.bottom;
+ bottom = containingBounds.bottom;
+ top = containingBounds.top;
}
- outBounds.set(containingBounds.left, containingBounds.top, right, bottom);
-
- // If the bounds are restricted by fixed aspect ratio, then out bounds should be put in the
- // container app bounds. Otherwise the entire container bounds are available.
- if (!outBounds.equals(containingBounds)) {
- // The horizontal position should not cover insets (e.g. display cutout).
- outBounds.left = containingAppBounds.left;
- }
-
+ outBounds.set(left, top, right, bottom);
return true;
}
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 219092baface..1266db5bff98 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -357,7 +357,12 @@ class AsyncRotationController extends FadeAnimationController implements Consume
* or seamless transformation in a rotated display.
*/
boolean shouldFreezeInsetsPosition(WindowState w) {
- return mTransitionOp != OP_LEGACY && w.mTransitionController.inTransition()
+ if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) {
+ // Expect a screenshot layer has covered the screen, so it is fine to let client side
+ // insets animation runner update the position directly.
+ return false;
+ }
+ return mTransitionOp != OP_LEGACY && !mIsStartTransactionCommitted
&& isTargetToken(w.mToken);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b093d2566710..95d71a36313f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -245,6 +245,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.utils.RegionUtils;
import com.android.server.wm.utils.RotationCache;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -5780,6 +5781,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (w.isVisible() && !w.inPinnedWindowingMode()) {
w.getKeepClearAreas(outRestricted, outUnrestricted, tmpMatrix, tmpFloat9);
+
+ if (w.mIsImWindow) {
+ Region touchableRegion = Region.obtain();
+ w.getEffectiveTouchableRegion(touchableRegion);
+ RegionUtils.forEachRect(touchableRegion, rect -> outUnrestricted.add(rect));
+ }
}
// We stop traversing when we reach the base of a fullscreen app.
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 8f32c47fcb5a..571534f5d16e 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -195,6 +195,8 @@ jobject translateGnssLocation(JNIEnv* env, const android::hardware::gnss::GnssLo
flags = static_cast<uint32_t>(location.elapsedRealtime.flags);
if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIMESTAMP_NS) {
SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+ } else {
+ SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
}
if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS) {
SET(ElapsedRealtimeUncertaintyNanos,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 49879efe4b51..798604306b43 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -1704,8 +1704,8 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
- public void testInfoIsPermittedForProfile_notAllowed() {
- when(mUserProfiles.canProfileUseBoundServices(anyInt())).thenReturn(false);
+ public void testInfoIsPermittedForProfile_notProfile() {
+ when(mUserProfiles.isProfileUser(anyInt())).thenReturn(false);
IInterface service = mock(IInterface.class);
when(service.asBinder()).thenReturn(mock(IBinder.class));
@@ -1714,12 +1714,12 @@ public class ManagedServicesTest extends UiServiceTestCase {
services.registerSystemService(service, null, 10, 1000);
ManagedServices.ManagedServiceInfo info = services.checkServiceTokenLocked(service);
- assertFalse(info.isPermittedForProfile(0));
+ assertTrue(info.isPermittedForProfile(0));
}
@Test
- public void testInfoIsPermittedForProfile_allows() {
- when(mUserProfiles.canProfileUseBoundServices(anyInt())).thenReturn(true);
+ public void testInfoIsPermittedForProfile_profileAndDpmAllows() {
+ when(mUserProfiles.isProfileUser(anyInt())).thenReturn(true);
when(mDpm.isNotificationListenerServicePermitted(anyString(), anyInt())).thenReturn(true);
IInterface service = mock(IInterface.class);
@@ -1734,6 +1734,22 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ public void testInfoIsPermittedForProfile_profileAndDpmDenies() {
+ when(mUserProfiles.isProfileUser(anyInt())).thenReturn(true);
+ when(mDpm.isNotificationListenerServicePermitted(anyString(), anyInt())).thenReturn(false);
+
+ IInterface service = mock(IInterface.class);
+ when(service.asBinder()).thenReturn(mock(IBinder.class));
+ ManagedServices services = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm, APPROVAL_BY_PACKAGE);
+ services.registerSystemService(service, null, 10, 1000);
+ ManagedServices.ManagedServiceInfo info = services.checkServiceTokenLocked(service);
+ info.component = new ComponentName("a","b");
+
+ assertFalse(info.isPermittedForProfile(0));
+ }
+
+ @Test
public void testUserProfiles_canProfileUseBoundServices_managedProfile() {
List<UserInfo> users = new ArrayList<>();
UserInfo profile = new UserInfo(ActivityManager.getCurrentUser(), "current", 0);
@@ -1750,9 +1766,9 @@ public class ManagedServicesTest extends UiServiceTestCase {
ManagedServices.UserProfiles profiles = new ManagedServices.UserProfiles();
profiles.updateCache(mContext);
- assertTrue(profiles.canProfileUseBoundServices(ActivityManager.getCurrentUser()));
- assertFalse(profiles.canProfileUseBoundServices(12));
- assertFalse(profiles.canProfileUseBoundServices(13));
+ assertFalse(profiles.isProfileUser(ActivityManager.getCurrentUser()));
+ assertTrue(profiles.isProfileUser(12));
+ assertTrue(profiles.isProfileUser(13));
}
private void resetComponentsAndPackages() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 60d3f10bb103..181e81d70dfa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -210,10 +210,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertFitted();
// After the orientation of activity is changed, the display is rotated, the aspect
- // ratio should be the same (bounds=[100, 0 - 800, 583], appBounds=[100, 0 - 800, 583]).
+ // ratio should be the same (bounds=[0, 0 - 800, 583], appBounds=[100, 0 - 800, 583]).
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
- // The notch is no longer on top.
- assertEquals(appBounds, mActivity.getBounds());
// Activity max bounds are sandboxed.
assertActivityMaxBoundsSandboxed();
@@ -467,8 +465,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
assertEquals(origBounds.width(), currentBounds.width());
- // The notch is on horizontal side, so current height changes from 1460 to 1400.
- assertEquals(origBounds.height() - notchHeight, currentBounds.height());
// Make sure the app size is the same
assertEquals(origAppBounds.width(), appBounds.width());
assertEquals(origAppBounds.height(), appBounds.height());
@@ -2271,7 +2267,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
// Bounds are letterboxed to respect the provided max aspect ratio.
- assertEquals(mActivity.getBounds(), new Rect(0, 850, 1000, 1950));
+ assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 1100));
// Move activity to split screen which has landscape size.
mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents */ false, "test");
@@ -2338,6 +2334,8 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testLetterboxDetailsForStatusBar_letterboxNotOverlappingStatusBar() {
+ // Align to center so that we don't overlap with the status bar
+ mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
.setNotch(100)
.build();
@@ -2354,7 +2352,7 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mRootWindowContainer.performSurfacePlacement();
Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
- assertEquals(mBounds, new Rect(0, 750, 1000, 1950));
+ assertEquals(mBounds, new Rect(0, 900, 1000, 2000));
DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
LetterboxDetails[] expectedLetterboxDetails = {new LetterboxDetails(
@@ -2454,7 +2452,7 @@ public class SizeCompatTests extends WindowTestsBase {
// At launch.
/* fixedOrientationLetterbox */ new Rect(0, 0, 700, 1400),
// After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(0, 700, 700, 2100),
+ /* sizeCompatUnscaled */ new Rect(0, 0, 700, 1400),
// After the display is resized to (700, 1400).
/* sizeCompatScaled */ new Rect(0, 0, 350, 700));
}
@@ -2467,7 +2465,7 @@ public class SizeCompatTests extends WindowTestsBase {
// At launch.
/* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
// After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(350, 700, 1050, 2100),
+ /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
// After the display is resized to (700, 1400).
/* sizeCompatScaled */ new Rect(525, 0, 875, 700));
}
@@ -2482,7 +2480,7 @@ public class SizeCompatTests extends WindowTestsBase {
// At launch.
/* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
// After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(350, 700, 1050, 2100),
+ /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
// After the display is resized to (700, 1400).
/* sizeCompatScaled */ new Rect(525, 0, 875, 700));
@@ -2492,7 +2490,7 @@ public class SizeCompatTests extends WindowTestsBase {
// At launch.
/* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
// After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(350, 700, 1050, 2100),
+ /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
// After the display is resized to (700, 1400).
/* sizeCompatScaled */ new Rect(525, 0, 875, 700));
}
@@ -2505,7 +2503,7 @@ public class SizeCompatTests extends WindowTestsBase {
// At launch.
/* fixedOrientationLetterbox */ new Rect(2100, 0, 2800, 1400),
// After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(700, 700, 1400, 2100),
+ /* sizeCompatUnscaled */ new Rect(700, 0, 1400, 1400),
// After the display is resized to (700, 1400).
/* sizeCompatScaled */ new Rect(1050, 0, 1400, 700));
}
@@ -2534,6 +2532,64 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testApplyAspectRatio_activityAlignWithParentAppVertical() {
+ // The display's app bounds will be (0, 100, 1000, 2350)
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500)
+ .setCanRotate(false)
+ .setCutout(0, 100, 0, 150)
+ .build();
+
+ setUpApp(display);
+ prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ // The activity height is 2100 and the display's app bounds height is 2250, so the activity
+ // can be aligned inside parentAppBounds
+ assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 2200));
+ }
+ @Test
+ public void testApplyAspectRatio_activityCannotAlignWithParentAppVertical() {
+ // The display's app bounds will be (0, 100, 1000, 2150)
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2300)
+ .setCanRotate(false)
+ .setCutout(0, 100, 0, 150)
+ .build();
+
+ setUpApp(display);
+ prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ // The activity height is 2100 and the display's app bounds height is 2050, so the activity
+ // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
+ assertEquals(mActivity.getBounds(), display.getBounds());
+ }
+
+ @Test
+ public void testApplyAspectRatio_activityAlignWithParentAppHorizontal() {
+ // The display's app bounds will be (100, 0, 2350, 1000)
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2500, 1000)
+ .setCanRotate(false)
+ .setCutout(100, 0, 150, 0)
+ .build();
+
+ setUpApp(display);
+ prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ // The activity width is 2100 and the display's app bounds width is 2250, so the activity
+ // can be aligned inside parentAppBounds
+ assertEquals(mActivity.getBounds(), new Rect(175, 0, 2275, 1000));
+ }
+ @Test
+ public void testApplyAspectRatio_activityCannotAlignWithParentAppHorizontal() {
+ // The display's app bounds will be (100, 0, 2150, 1000)
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2300, 1000)
+ .setCanRotate(false)
+ .setCutout(100, 0, 150, 0)
+ .build();
+
+ setUpApp(display);
+ prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ // The activity width is 2100 and the display's app bounds width is 2050, so the activity
+ // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
+ assertEquals(mActivity.getBounds(), display.getBounds());
+ }
+
+ @Test
public void testUpdateResolvedBoundsHorizontalPosition_activityFillParentWidth() {
// When activity width equals parent width, multiplier shouldn't have any effect.
assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
@@ -2608,6 +2664,25 @@ public class SizeCompatTests extends WindowTestsBase {
/* sizeCompatScaled */ new Rect(0, 1050, 700, 1400));
}
+ @Test
+ public void testUpdateResolvedBoundsPosition_alignToTop() {
+ final int notchHeight = 100;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
+ .setNotch(notchHeight)
+ .build();
+ setUpApp(display);
+
+ // Prepare unresizable activity with max aspect ratio
+ prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
+
+ Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+ Rect appBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
+ // The insets should be cut for aspect ratio and then added back because the appBounds
+ // are aligned to the top of the parentAppBounds
+ assertEquals(mBounds, new Rect(0, 0, 1000, 1200));
+ assertEquals(appBounds, new Rect(0, notchHeight, 1000, 1200));
+ }
+
private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
float letterboxVerticalPositionMultiplier, Rect fixedOrientationLetterbox,
Rect sizeCompatUnscaled, Rect sizeCompatScaled) {
@@ -2955,7 +3030,7 @@ public class SizeCompatTests extends WindowTestsBase {
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
assertTrue(mActivity.inSizeCompatMode());
// Activity is in size compat mode but not scaled.
- assertEquals(new Rect(0, 1050, 1400, 1750), mActivity.getBounds());
+ assertEquals(new Rect(0, 0, 1400, 700), mActivity.getBounds());
}
private void assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index aa3ca18073c3..bf1d1fa98249 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -143,11 +143,24 @@ class TestDisplayContent extends DisplayContent {
mInfo.ownerUid = ownerUid;
return this;
}
- Builder setNotch(int height) {
+ Builder setCutout(int left, int top, int right, int bottom) {
+ final int cutoutFillerSize = 80;
+ Rect boundLeft = left != 0 ? new Rect(0, 0, left, cutoutFillerSize) : null;
+ Rect boundTop = top != 0 ? new Rect(0, 0, cutoutFillerSize, top) : null;
+ Rect boundRight = right != 0 ? new Rect(mInfo.logicalWidth - right, 0,
+ mInfo.logicalWidth, cutoutFillerSize) : null;
+ Rect boundBottom = bottom != 0
+ ? new Rect(0, mInfo.logicalHeight - bottom, cutoutFillerSize,
+ mInfo.logicalHeight) : null;
+
mInfo.displayCutout = new DisplayCutout(
- Insets.of(0, height, 0, 0), null, new Rect(20, 0, 80, height), null, null);
+ Insets.of(left, top, right, bottom),
+ boundLeft, boundTop, boundRight, boundBottom);
return this;
}
+ Builder setNotch(int height) {
+ return setCutout(0, height, 0, 0);
+ }
Builder setStatusBarHeight(int height) {
mStatusBarHeight = height;
return this;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 28bcc03fb3e7..8cf32baa49eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -729,7 +729,7 @@ public class TransitionTests extends WindowTestsBase {
assertTrue(ime.mToken.inTransition());
assertTrue(task.inTransition());
assertTrue(asyncRotationController.isTargetToken(decorToken));
- assertTrue(asyncRotationController.shouldFreezeInsetsPosition(navBar));
+ assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
screenDecor.setOrientationChanging(false);
// Status bar finishes drawing before the start transaction. Its fade-in animation will be
@@ -744,6 +744,7 @@ public class TransitionTests extends WindowTestsBase {
// The transaction is committed, so fade-in animation for status bar is consumed.
transactionCommittedListener.onTransactionCommitted();
assertFalse(asyncRotationController.isTargetToken(statusBar.mToken));
+ assertShouldFreezeInsetsPosition(asyncRotationController, navBar, false);
// Navigation bar finishes drawing after the start transaction, so its fade-in animation
// can execute directly.
@@ -779,7 +780,7 @@ public class TransitionTests extends WindowTestsBase {
final AsyncRotationController asyncRotationController =
mDisplayContent.getAsyncRotationController();
assertNotNull(asyncRotationController);
- assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar));
+ assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
statusBar.setOrientationChanging(true);
player.startTransition();
@@ -825,7 +826,7 @@ public class TransitionTests extends WindowTestsBase {
final AsyncRotationController asyncRotationController =
mDisplayContent.getAsyncRotationController();
assertNotNull(asyncRotationController);
- assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar));
+ assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
assertTrue(app.getTask().inTransition());
player.start();
@@ -860,6 +861,15 @@ public class TransitionTests extends WindowTestsBase {
assertNull(mDisplayContent.getAsyncRotationController());
}
+ private static void assertShouldFreezeInsetsPosition(AsyncRotationController controller,
+ WindowState w, boolean freeze) {
+ if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) {
+ // Non blast sync should never freeze insets position.
+ freeze = false;
+ }
+ assertEquals(freeze, controller.shouldFreezeInsetsPosition(w));
+ }
+
@Test
public void testDeferRotationForTransientLaunch() {
final TestTransitionPlayer player = registerTestTransitionPlayer();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index b6373b4c439f..ef532f5732eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -238,7 +238,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
// Ensure letterbox vertical position multiplier is not overridden on any device target.
// {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier},
// may be set on some device form factors.
- mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
+ mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.0f);
// Ensure letterbox horizontal reachability treatment isn't overridden on any device target.
// {@link com.android.internal.R.bool.config_letterboxIsHorizontalReachabilityEnabled},
// may be set on some device form factors.
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index e7d95e4f53b3..58974eec838b 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -27,6 +27,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.ParcelUuid;
+import android.provider.DeviceConfig;
import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.util.IndentingPrintWriter;
@@ -57,6 +58,9 @@ import java.util.concurrent.ConcurrentHashMap;
public final class AnomalyReporter {
private static final String TAG = "AnomalyReporter";
+ private static final String KEY_IS_TELEPHONY_ANOMALY_REPORT_ENABLED =
+ "is_telephony_anomaly_report_enabled";
+
private static Context sContext = null;
private static Map<UUID, Integer> sEvents = new ConcurrentHashMap<>();
@@ -101,6 +105,12 @@ public final class AnomalyReporter {
* @param carrierId the carrier of the id associated with this event.
*/
public static void reportAnomaly(@NonNull UUID eventId, String description, int carrierId) {
+ // Don't report if the server-side flag isn't loaded, as it implies other anomaly report
+ // related config hasn't loaded.
+ boolean isAnomalyReportEnabledFromServer = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_TELEPHONY, KEY_IS_TELEPHONY_ANOMALY_REPORT_ENABLED, false);
+ if (!isAnomalyReportEnabledFromServer) return;
+
if (sContext == null) {
Rlog.w(TAG, "AnomalyReporter not yet initialized, dropping event=" + eventId);
return;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d07d8097bce4..77408458c96b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3426,10 +3426,20 @@ public class SubscriptionManager {
* Get subscriptionInfo list of subscriptions that are in the same group of given subId.
* See {@link #createSubscriptionGroup(List)} for more details.
*
- * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE}
- * permission or had carrier privilege permission on the subscription.
+ * Caller must have {@link android.Manifest.permission#READ_PHONE_STATE}
+ * or carrier privilege permission on the subscription.
* {@link TelephonyManager#hasCarrierPrivileges()}
*
+ * <p>Starting with API level 33, this method will return an empty List if the caller does
+ * not have access to device identifiers.
+ * This method can be invoked if one of the following requirements is met:
+ * <ul>
+ * <li>If the app has carrier privilege permission.
+ * {@link TelephonyManager#hasCarrierPrivileges()}
+ * <li>If the app has {@link android.Manifest.permission#READ_PHONE_STATE} permission and
+ * access to device identifiers.
+ * </ul>
+ *
* @throws IllegalStateException if Telephony service is in bad state.
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.