Merge "Update cancel device state request to require foreground for cts compat" into main
diff --git a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
index 6363e9c..25e4c43 100644
--- a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
@@ -16,7 +16,8 @@
package android.libcore;
-import android.perftests.utils.BenchmarkState;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
import android.perftests.utils.PerfStatusReporter;
import androidx.test.filters.LargeTest;
@@ -34,7 +35,8 @@
@RunWith(JUnitParamsRunner.class)
@LargeTest
public class SystemArrayCopyPerfTest {
- @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ @Rule
+ public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
public static Collection<Object[]> getData() {
return Arrays.asList(
@@ -51,7 +53,7 @@
final int len = arrayLength;
char[] src = new char[len];
char[] dst = new char[len];
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
System.arraycopy(src, 0, dst, 0, len);
}
@@ -63,7 +65,7 @@
final int len = arrayLength;
byte[] src = new byte[len];
byte[] dst = new byte[len];
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
System.arraycopy(src, 0, dst, 0, len);
}
@@ -75,7 +77,7 @@
final int len = arrayLength;
short[] src = new short[len];
short[] dst = new short[len];
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
System.arraycopy(src, 0, dst, 0, len);
}
@@ -87,7 +89,7 @@
final int len = arrayLength;
int[] src = new int[len];
int[] dst = new int[len];
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
System.arraycopy(src, 0, dst, 0, len);
}
@@ -99,7 +101,7 @@
final int len = arrayLength;
long[] src = new long[len];
long[] dst = new long[len];
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
System.arraycopy(src, 0, dst, 0, len);
}
@@ -111,7 +113,7 @@
final int len = arrayLength;
float[] src = new float[len];
float[] dst = new float[len];
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
System.arraycopy(src, 0, dst, 0, len);
}
@@ -123,7 +125,7 @@
final int len = arrayLength;
double[] src = new double[len];
double[] dst = new double[len];
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
System.arraycopy(src, 0, dst, 0, len);
}
@@ -135,7 +137,7 @@
final int len = arrayLength;
boolean[] src = new boolean[len];
boolean[] dst = new boolean[len];
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
System.arraycopy(src, 0, dst, 0, len);
}
diff --git a/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java b/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
index fbe67a4..c34936f 100644
--- a/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
@@ -19,6 +19,7 @@
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
+import android.graphics.Typeface;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
@@ -120,13 +121,34 @@
public void testSetFontVariationSettings() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final Paint paint = new Paint(PAINT);
- final Random random = new Random(0);
while (state.keepRunning()) {
state.pauseTiming();
- int weight = random.nextInt(1000);
+ paint.setTypeface(null);
+ paint.setFontVariationSettings(null);
+ Typeface.clearTypefaceCachesForTestingPurpose();
state.resumeTiming();
- paint.setFontVariationSettings("'wght' " + weight);
+ paint.setFontVariationSettings("'wght' 450");
}
+ Typeface.clearTypefaceCachesForTestingPurpose();
}
+
+ @Test
+ public void testSetFontVariationSettings_Cached() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Paint paint = new Paint(PAINT);
+ Typeface.clearTypefaceCachesForTestingPurpose();
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ paint.setTypeface(null);
+ paint.setFontVariationSettings(null);
+ state.resumeTiming();
+
+ paint.setFontVariationSettings("'wght' 450");
+ }
+
+ Typeface.clearTypefaceCachesForTestingPurpose();
+ }
+
}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index c2aeada..c1894f0 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2629,8 +2629,13 @@
for (int i=0; i<allowPowerExceptIdle.size(); i++) {
String pkg = allowPowerExceptIdle.valueAt(i);
try {
+ // On some devices (eg. HSUM), some apps may
+ // be not be pre-installed on user 0, but may be
+ // pre-installed on FULL users. Look for pre-installed system
+ // apps across all users to make sure they're properly
+ // allowlisted.
ApplicationInfo ai = pm.getApplicationInfo(pkg,
- PackageManager.MATCH_SYSTEM_ONLY);
+ PackageManager.MATCH_ANY_USER | PackageManager.MATCH_SYSTEM_ONLY);
int appid = UserHandle.getAppId(ai.uid);
mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
@@ -2641,8 +2646,13 @@
for (int i=0; i<allowPower.size(); i++) {
String pkg = allowPower.valueAt(i);
try {
+ // On some devices (eg. HSUM), some apps may
+ // be not be pre-installed on user 0, but may be
+ // pre-installed on FULL users. Look for pre-installed system
+ // apps across all users to make sure they're properly
+ // allowlisted.
ApplicationInfo ai = pm.getApplicationInfo(pkg,
- PackageManager.MATCH_SYSTEM_ONLY);
+ PackageManager.MATCH_ANY_USER | PackageManager.MATCH_SYSTEM_ONLY);
int appid = UserHandle.getAppId(ai.uid);
// These apps are on both the whitelist-except-idle as well
// as the full whitelist, so they apply in all cases.
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 7353dde..a2d24f6 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -504,6 +504,9 @@
public float dozeScreenBrightness;
public int dozeScreenStateReason;
+ // Override that makes display use normal brightness while dozing.
+ public boolean useNormalBrightnessForDoze;
+
public DisplayPowerRequest() {
policy = POLICY_BRIGHT;
useProximitySensor = false;
@@ -537,6 +540,7 @@
dozeScreenBrightness = other.dozeScreenBrightness;
dozeScreenState = other.dozeScreenState;
dozeScreenStateReason = other.dozeScreenStateReason;
+ useNormalBrightnessForDoze = other.useNormalBrightnessForDoze;
}
@Override
@@ -561,7 +565,8 @@
&& boostScreenBrightness == other.boostScreenBrightness
&& floatEquals(dozeScreenBrightness, other.dozeScreenBrightness)
&& dozeScreenState == other.dozeScreenState
- && dozeScreenStateReason == other.dozeScreenStateReason;
+ && dozeScreenStateReason == other.dozeScreenStateReason
+ && useNormalBrightnessForDoze == other.useNormalBrightnessForDoze;
}
private boolean floatEquals(float f1, float f2) {
@@ -587,7 +592,8 @@
+ ", dozeScreenBrightness=" + dozeScreenBrightness
+ ", dozeScreenState=" + Display.stateToString(dozeScreenState)
+ ", dozeScreenStateReason="
- + Display.stateReasonToString(dozeScreenStateReason);
+ + Display.stateReasonToString(dozeScreenStateReason)
+ + ", useNormalBrightnessForDoze=" + useNormalBrightnessForDoze;
}
public static String policyToString(int policy) {
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 98e11375..83f2685 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -25,7 +25,7 @@
import android.hardware.input.IInputDeviceBatteryState;
import android.hardware.input.IKeyboardBacklightListener;
import android.hardware.input.IKeyboardBacklightState;
-import android.hardware.input.IKeyboardSystemShortcutListener;
+import android.hardware.input.IKeyGestureEventListener;
import android.hardware.input.IStickyModifierStateListener;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.KeyboardLayoutSelectionResult;
@@ -241,13 +241,13 @@
KeyGlyphMap getKeyGlyphMap(int deviceId);
- @EnforcePermission("MONITOR_KEYBOARD_SYSTEM_SHORTCUTS")
+ @EnforcePermission("MANAGE_KEY_GESTURES")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
- + "android.Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)")
- void registerKeyboardSystemShortcutListener(IKeyboardSystemShortcutListener listener);
+ + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
+ void registerKeyGestureEventListener(IKeyGestureEventListener listener);
- @EnforcePermission("MONITOR_KEYBOARD_SYSTEM_SHORTCUTS")
+ @EnforcePermission("MANAGE_KEY_GESTURES")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
- + "android.Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)")
- void unregisterKeyboardSystemShortcutListener(IKeyboardSystemShortcutListener listener);
+ + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
+ void unregisterKeyGestureEventListener(IKeyGestureEventListener listener);
}
diff --git a/core/java/android/hardware/input/IKeyboardSystemShortcutListener.aidl b/core/java/android/hardware/input/IKeyGestureEventListener.aidl
similarity index 71%
rename from core/java/android/hardware/input/IKeyboardSystemShortcutListener.aidl
rename to core/java/android/hardware/input/IKeyGestureEventListener.aidl
index 8d44917..2c430f1 100644
--- a/core/java/android/hardware/input/IKeyboardSystemShortcutListener.aidl
+++ b/core/java/android/hardware/input/IKeyGestureEventListener.aidl
@@ -17,11 +17,10 @@
package android.hardware.input;
/** @hide */
-oneway interface IKeyboardSystemShortcutListener {
+oneway interface IKeyGestureEventListener {
/**
- * Called when the keyboard system shortcut is triggered.
+ * Called when a key gesture event occurs.
*/
- void onKeyboardSystemShortcutTriggered(int deviceId, in int[] keycodes, int modifierState,
- int shortcut);
+ void onKeyGestureEvent(int deviceId, in int[] keycodes, int modifierState, int shortcut);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6bc522b..04cfcd8 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1378,33 +1378,31 @@
}
/**
- * Registers a keyboard system shortcut listener for {@link KeyboardSystemShortcut} being
- * triggered.
+ * Registers a key gesture event listener for {@link KeyGestureEvent} being triggered.
*
* @param executor an executor on which the callback will be called
- * @param listener the {@link KeyboardSystemShortcutListener}
+ * @param listener the {@link KeyGestureEventListener}
* @throws IllegalArgumentException if {@code listener} has already been registered previously.
* @throws NullPointerException if {@code listener} or {@code executor} is null.
* @hide
- * @see #unregisterKeyboardSystemShortcutListener(KeyboardSystemShortcutListener)
+ * @see #unregisterKeyGestureEventListener(KeyGestureEventListener)
*/
- @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
- public void registerKeyboardSystemShortcutListener(@NonNull Executor executor,
- @NonNull KeyboardSystemShortcutListener listener) throws IllegalArgumentException {
- mGlobal.registerKeyboardSystemShortcutListener(executor, listener);
+ @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+ public void registerKeyGestureEventListener(@NonNull Executor executor,
+ @NonNull KeyGestureEventListener listener) throws IllegalArgumentException {
+ mGlobal.registerKeyGestureEventListener(executor, listener);
}
/**
- * Unregisters a previously added keyboard system shortcut listener.
+ * Unregisters a previously added key gesture event listener.
*
- * @param listener the {@link KeyboardSystemShortcutListener}
+ * @param listener the {@link KeyGestureEventListener}
* @hide
- * @see #registerKeyboardSystemShortcutListener(Executor, KeyboardSystemShortcutListener)
+ * @see #registerKeyGestureEventListener(Executor, KeyGestureEventListener)
*/
- @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
- public void unregisterKeyboardSystemShortcutListener(
- @NonNull KeyboardSystemShortcutListener listener) {
- mGlobal.unregisterKeyboardSystemShortcutListener(listener);
+ @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+ public void unregisterKeyGestureEventListener(@NonNull KeyGestureEventListener listener) {
+ mGlobal.unregisterKeyGestureEventListener(listener);
}
/**
@@ -1510,19 +1508,18 @@
}
/**
- * A callback used to be notified about keyboard system shortcuts being triggered.
+ * A callback used to notify about key gesture event on completion.
*
- * @see #registerKeyboardSystemShortcutListener(Executor, KeyboardSystemShortcutListener)
- * @see #unregisterKeyboardSystemShortcutListener(KeyboardSystemShortcutListener)
+ * @see #registerKeyGestureEventListener(Executor, KeyGestureEventListener)
+ * @see #unregisterKeyGestureEventListener(KeyGestureEventListener)
* @hide
*/
- public interface KeyboardSystemShortcutListener {
+ public interface KeyGestureEventListener {
/**
- * Called when a keyboard system shortcut is triggered.
+ * Called when a key gesture event occurs.
*
- * @param systemShortcut the shortcut info about the shortcut that was triggered.
+ * @param event the gesture event that occurred.
*/
- void onKeyboardSystemShortcutTriggered(int deviceId,
- @NonNull KeyboardSystemShortcut systemShortcut);
+ void onKeyGestureEvent(@NonNull KeyGestureEvent event);
}
}
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index f7fa557..03cf7c5 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -25,8 +25,8 @@
import android.hardware.SensorManager;
import android.hardware.input.InputManager.InputDeviceBatteryListener;
import android.hardware.input.InputManager.InputDeviceListener;
+import android.hardware.input.InputManager.KeyGestureEventListener;
import android.hardware.input.InputManager.KeyboardBacklightListener;
-import android.hardware.input.InputManager.KeyboardSystemShortcutListener;
import android.hardware.input.InputManager.OnTabletModeChangedListener;
import android.hardware.input.InputManager.StickyModifierStateListener;
import android.hardware.lights.Light;
@@ -111,13 +111,13 @@
@Nullable
private IStickyModifierStateListener mStickyModifierStateListener;
- private final Object mKeyboardSystemShortcutListenerLock = new Object();
- @GuardedBy("mKeyboardSystemShortcutListenerLock")
+ private final Object mKeyGestureEventListenerLock = new Object();
+ @GuardedBy("mKeyGestureEventListenerLock")
@Nullable
- private ArrayList<KeyboardSystemShortcutListenerDelegate> mKeyboardSystemShortcutListeners;
- @GuardedBy("mKeyboardSystemShortcutListenerLock")
+ private ArrayList<KeyGestureEventListenerDelegate> mKeyGestureEventListeners;
+ @GuardedBy("mKeyGestureEventListenerLock")
@Nullable
- private IKeyboardSystemShortcutListener mKeyboardSystemShortcutListener;
+ private IKeyGestureEventListener mKeyGestureEventListener;
// InputDeviceSensorManager gets notified synchronously from the binder thread when input
// devices change, so it must be synchronized with the input device listeners.
@@ -1064,94 +1064,92 @@
}
}
- private static final class KeyboardSystemShortcutListenerDelegate {
- final KeyboardSystemShortcutListener mListener;
+ private static final class KeyGestureEventListenerDelegate {
+ final KeyGestureEventListener mListener;
final Executor mExecutor;
- KeyboardSystemShortcutListenerDelegate(KeyboardSystemShortcutListener listener,
+ KeyGestureEventListenerDelegate(KeyGestureEventListener listener,
Executor executor) {
mListener = listener;
mExecutor = executor;
}
- void onKeyboardSystemShortcutTriggered(int deviceId,
- KeyboardSystemShortcut systemShortcut) {
- mExecutor.execute(() ->
- mListener.onKeyboardSystemShortcutTriggered(deviceId, systemShortcut));
+ void onKeyGestureEvent(KeyGestureEvent event) {
+ mExecutor.execute(() -> mListener.onKeyGestureEvent(event));
}
}
- private class LocalKeyboardSystemShortcutListener extends IKeyboardSystemShortcutListener.Stub {
+ private class LocalKeyGestureEventListener extends IKeyGestureEventListener.Stub {
@Override
- public void onKeyboardSystemShortcutTriggered(int deviceId, int[] keycodes,
- int modifierState, int shortcut) {
- synchronized (mKeyboardSystemShortcutListenerLock) {
- if (mKeyboardSystemShortcutListeners == null) return;
- final int numListeners = mKeyboardSystemShortcutListeners.size();
+ public void onKeyGestureEvent(int deviceId, int[] keycodes, int modifierState,
+ int gestureType) {
+ synchronized (mKeyGestureEventListenerLock) {
+ if (mKeyGestureEventListeners == null) return;
+ final int numListeners = mKeyGestureEventListeners.size();
for (int i = 0; i < numListeners; i++) {
- mKeyboardSystemShortcutListeners.get(i)
- .onKeyboardSystemShortcutTriggered(deviceId,
- new KeyboardSystemShortcut(keycodes, modifierState, shortcut));
+ mKeyGestureEventListeners.get(i)
+ .onKeyGestureEvent(
+ new KeyGestureEvent(deviceId, keycodes, modifierState,
+ gestureType));
}
}
}
}
/**
- * @see InputManager#registerKeyboardSystemShortcutListener(Executor,
- * KeyboardSystemShortcutListener)
+ * @see InputManager#registerKeyGestureEventListener(Executor,
+ * KeyGestureEventListener)
*/
- @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
- void registerKeyboardSystemShortcutListener(@NonNull Executor executor,
- @NonNull KeyboardSystemShortcutListener listener) throws IllegalArgumentException {
+ @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+ void registerKeyGestureEventListener(@NonNull Executor executor,
+ @NonNull KeyGestureEventListener listener) throws IllegalArgumentException {
Objects.requireNonNull(executor, "executor should not be null");
Objects.requireNonNull(listener, "listener should not be null");
- synchronized (mKeyboardSystemShortcutListenerLock) {
- if (mKeyboardSystemShortcutListener == null) {
- mKeyboardSystemShortcutListeners = new ArrayList<>();
- mKeyboardSystemShortcutListener = new LocalKeyboardSystemShortcutListener();
+ synchronized (mKeyGestureEventListenerLock) {
+ if (mKeyGestureEventListener == null) {
+ mKeyGestureEventListeners = new ArrayList<>();
+ mKeyGestureEventListener = new LocalKeyGestureEventListener();
try {
- mIm.registerKeyboardSystemShortcutListener(mKeyboardSystemShortcutListener);
+ mIm.registerKeyGestureEventListener(mKeyGestureEventListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- final int numListeners = mKeyboardSystemShortcutListeners.size();
+ final int numListeners = mKeyGestureEventListeners.size();
for (int i = 0; i < numListeners; i++) {
- if (mKeyboardSystemShortcutListeners.get(i).mListener == listener) {
+ if (mKeyGestureEventListeners.get(i).mListener == listener) {
throw new IllegalArgumentException("Listener has already been registered!");
}
}
- KeyboardSystemShortcutListenerDelegate delegate =
- new KeyboardSystemShortcutListenerDelegate(listener, executor);
- mKeyboardSystemShortcutListeners.add(delegate);
+ KeyGestureEventListenerDelegate delegate =
+ new KeyGestureEventListenerDelegate(listener, executor);
+ mKeyGestureEventListeners.add(delegate);
}
}
/**
- * @see InputManager#unregisterKeyboardSystemShortcutListener(KeyboardSystemShortcutListener)
+ * @see InputManager#unregisterKeyGestureEventListener(KeyGestureEventListener)
*/
- @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
- void unregisterKeyboardSystemShortcutListener(
- @NonNull KeyboardSystemShortcutListener listener) {
+ @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+ void unregisterKeyGestureEventListener(@NonNull KeyGestureEventListener listener) {
Objects.requireNonNull(listener, "listener should not be null");
- synchronized (mKeyboardSystemShortcutListenerLock) {
- if (mKeyboardSystemShortcutListeners == null) {
+ synchronized (mKeyGestureEventListenerLock) {
+ if (mKeyGestureEventListeners == null) {
return;
}
- mKeyboardSystemShortcutListeners.removeIf((delegate) -> delegate.mListener == listener);
- if (mKeyboardSystemShortcutListeners.isEmpty()) {
+ mKeyGestureEventListeners.removeIf((delegate) -> delegate.mListener == listener);
+ if (mKeyGestureEventListeners.isEmpty()) {
try {
- mIm.unregisterKeyboardSystemShortcutListener(mKeyboardSystemShortcutListener);
+ mIm.unregisterKeyGestureEventListener(mKeyGestureEventListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- mKeyboardSystemShortcutListeners = null;
- mKeyboardSystemShortcutListener = null;
+ mKeyGestureEventListeners = null;
+ mKeyGestureEventListener = null;
}
}
}
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
new file mode 100644
index 0000000..7a8dd33
--- /dev/null
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -0,0 +1,531 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides information about the keyboard gesture event being triggered by an external keyboard.
+ *
+ * @hide
+ */
+@DataClass(genToString = true, genEqualsHashCode = true)
+public class KeyGestureEvent {
+
+ private final int mDeviceId;
+ @NonNull
+ private final int[] mKeycodes;
+ private final int mModifierState;
+ @KeyGestureType
+ private final int mKeyGestureType;
+
+
+ public static final int KEY_GESTURE_TYPE_UNSPECIFIED =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+ public static final int KEY_GESTURE_TYPE_HOME =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
+ public static final int KEY_GESTURE_TYPE_RECENT_APPS =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS;
+ public static final int KEY_GESTURE_TYPE_BACK =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK;
+ public static final int KEY_GESTURE_TYPE_APP_SWITCH =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__APP_SWITCH;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_ASSISTANT =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_ASSISTANT;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_VOICE_ASSISTANT;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SYSTEM_SETTINGS;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_NOTIFICATION_PANEL;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_TASKBAR =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_TASKBAR;
+ public static final int KEY_GESTURE_TYPE_TAKE_SCREENSHOT =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TAKE_SCREENSHOT;
+ public static final int KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_SHORTCUT_HELPER;
+ public static final int KEY_GESTURE_TYPE_BRIGHTNESS_UP =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_UP;
+ public static final int KEY_GESTURE_TYPE_BRIGHTNESS_DOWN =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_DOWN;
+ public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_UP;
+ public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_DOWN;
+ public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_TOGGLE;
+ public static final int KEY_GESTURE_TYPE_VOLUME_UP =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_UP;
+ public static final int KEY_GESTURE_TYPE_VOLUME_DOWN =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_DOWN;
+ public static final int KEY_GESTURE_TYPE_VOLUME_MUTE =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_MUTE;
+ public static final int KEY_GESTURE_TYPE_ALL_APPS =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ALL_APPS;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_SEARCH =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SEARCH;
+ public static final int KEY_GESTURE_TYPE_LANGUAGE_SWITCH =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LANGUAGE_SWITCH;
+ public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ACCESSIBILITY_ALL_APPS;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK;
+ public static final int KEY_GESTURE_TYPE_SYSTEM_MUTE =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE;
+ public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION;
+ public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS;
+ public static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT;
+ public static final int KEY_GESTURE_TYPE_LOCK_SCREEN =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LOCK_SCREEN;
+ public static final int KEY_GESTURE_TYPE_OPEN_NOTES =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_NOTES;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_POWER =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_POWER;
+ public static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_NAVIGATION;
+ public static final int KEY_GESTURE_TYPE_SLEEP =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SLEEP;
+ public static final int KEY_GESTURE_TYPE_WAKEUP =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__WAKEUP;
+ public static final int KEY_GESTURE_TYPE_MEDIA_KEY =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MEDIA_KEY;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+ public static final int KEY_GESTURE_TYPE_DESKTOP_MODE =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
+ public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/input/KeyGestureEvent.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @IntDef(prefix = "KEY_GESTURE_TYPE_", value = {
+ KEY_GESTURE_TYPE_UNSPECIFIED,
+ KEY_GESTURE_TYPE_HOME,
+ KEY_GESTURE_TYPE_RECENT_APPS,
+ KEY_GESTURE_TYPE_BACK,
+ KEY_GESTURE_TYPE_APP_SWITCH,
+ KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+ KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT,
+ KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+ KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+ KEY_GESTURE_TYPE_TOGGLE_TASKBAR,
+ KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+ KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
+ KEY_GESTURE_TYPE_BRIGHTNESS_UP,
+ KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
+ KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
+ KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
+ KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
+ KEY_GESTURE_TYPE_VOLUME_UP,
+ KEY_GESTURE_TYPE_VOLUME_DOWN,
+ KEY_GESTURE_TYPE_VOLUME_MUTE,
+ KEY_GESTURE_TYPE_ALL_APPS,
+ KEY_GESTURE_TYPE_LAUNCH_SEARCH,
+ KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+ KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+ KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+ KEY_GESTURE_TYPE_SYSTEM_MUTE,
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS,
+ KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
+ KEY_GESTURE_TYPE_LOCK_SCREEN,
+ KEY_GESTURE_TYPE_OPEN_NOTES,
+ KEY_GESTURE_TYPE_TOGGLE_POWER,
+ KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
+ KEY_GESTURE_TYPE_SLEEP,
+ KEY_GESTURE_TYPE_WAKEUP,
+ KEY_GESTURE_TYPE_MEDIA_KEY,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER,
+ KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS,
+ KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
+ KEY_GESTURE_TYPE_DESKTOP_MODE,
+ KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface KeyGestureType {}
+
+ @DataClass.Generated.Member
+ public static String keyGestureTypeToString(@KeyGestureType int value) {
+ switch (value) {
+ case KEY_GESTURE_TYPE_UNSPECIFIED:
+ return "KEY_GESTURE_TYPE_UNSPECIFIED";
+ case KEY_GESTURE_TYPE_HOME:
+ return "KEY_GESTURE_TYPE_HOME";
+ case KEY_GESTURE_TYPE_RECENT_APPS:
+ return "KEY_GESTURE_TYPE_RECENT_APPS";
+ case KEY_GESTURE_TYPE_BACK:
+ return "KEY_GESTURE_TYPE_BACK";
+ case KEY_GESTURE_TYPE_APP_SWITCH:
+ return "KEY_GESTURE_TYPE_APP_SWITCH";
+ case KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+ return "KEY_GESTURE_TYPE_LAUNCH_ASSISTANT";
+ case KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
+ return "KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT";
+ case KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
+ return "KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS";
+ case KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
+ return "KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL";
+ case KEY_GESTURE_TYPE_TOGGLE_TASKBAR:
+ return "KEY_GESTURE_TYPE_TOGGLE_TASKBAR";
+ case KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
+ return "KEY_GESTURE_TYPE_TAKE_SCREENSHOT";
+ case KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
+ return "KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER";
+ case KEY_GESTURE_TYPE_BRIGHTNESS_UP:
+ return "KEY_GESTURE_TYPE_BRIGHTNESS_UP";
+ case KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
+ return "KEY_GESTURE_TYPE_BRIGHTNESS_DOWN";
+ case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+ return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP";
+ case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+ return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN";
+ case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+ return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE";
+ case KEY_GESTURE_TYPE_VOLUME_UP:
+ return "KEY_GESTURE_TYPE_VOLUME_UP";
+ case KEY_GESTURE_TYPE_VOLUME_DOWN:
+ return "KEY_GESTURE_TYPE_VOLUME_DOWN";
+ case KEY_GESTURE_TYPE_VOLUME_MUTE:
+ return "KEY_GESTURE_TYPE_VOLUME_MUTE";
+ case KEY_GESTURE_TYPE_ALL_APPS:
+ return "KEY_GESTURE_TYPE_ALL_APPS";
+ case KEY_GESTURE_TYPE_LAUNCH_SEARCH:
+ return "KEY_GESTURE_TYPE_LAUNCH_SEARCH";
+ case KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
+ return "KEY_GESTURE_TYPE_LANGUAGE_SWITCH";
+ case KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
+ return "KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS";
+ case KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+ return "KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK";
+ case KEY_GESTURE_TYPE_SYSTEM_MUTE:
+ return "KEY_GESTURE_TYPE_SYSTEM_MUTE";
+ case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION:
+ return "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION";
+ case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS:
+ return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS";
+ case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
+ return "KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT";
+ case KEY_GESTURE_TYPE_LOCK_SCREEN:
+ return "KEY_GESTURE_TYPE_LOCK_SCREEN";
+ case KEY_GESTURE_TYPE_OPEN_NOTES:
+ return "KEY_GESTURE_TYPE_OPEN_NOTES";
+ case KEY_GESTURE_TYPE_TOGGLE_POWER:
+ return "KEY_GESTURE_TYPE_TOGGLE_POWER";
+ case KEY_GESTURE_TYPE_SYSTEM_NAVIGATION:
+ return "KEY_GESTURE_TYPE_SYSTEM_NAVIGATION";
+ case KEY_GESTURE_TYPE_SLEEP:
+ return "KEY_GESTURE_TYPE_SLEEP";
+ case KEY_GESTURE_TYPE_WAKEUP:
+ return "KEY_GESTURE_TYPE_WAKEUP";
+ case KEY_GESTURE_TYPE_MEDIA_KEY:
+ return "KEY_GESTURE_TYPE_MEDIA_KEY";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER";
+ case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
+ return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS";
+ case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
+ return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME";
+ case KEY_GESTURE_TYPE_DESKTOP_MODE:
+ return "KEY_GESTURE_TYPE_DESKTOP_MODE";
+ case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
+ return "KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ public KeyGestureEvent(
+ int deviceId,
+ @NonNull int[] keycodes,
+ int modifierState,
+ @KeyGestureType int keyGestureType) {
+ this.mDeviceId = deviceId;
+ this.mKeycodes = keycodes;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mKeycodes);
+ this.mModifierState = modifierState;
+ this.mKeyGestureType = keyGestureType;
+
+ if (!(mKeyGestureType == KEY_GESTURE_TYPE_UNSPECIFIED)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_HOME)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_RECENT_APPS)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_BACK)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_APP_SWITCH)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_ASSISTANT)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_TASKBAR)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_TAKE_SCREENSHOT)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_BRIGHTNESS_UP)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_BRIGHTNESS_DOWN)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_VOLUME_UP)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_VOLUME_DOWN)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_VOLUME_MUTE)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_ALL_APPS)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_SEARCH)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LANGUAGE_SWITCH)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_SYSTEM_MUTE)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LOCK_SCREEN)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_OPEN_NOTES)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_POWER)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_SYSTEM_NAVIGATION)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_SLEEP)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_WAKEUP)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_MEDIA_KEY)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_DESKTOP_MODE)
+ && !(mKeyGestureType == KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION)) {
+ throw new java.lang.IllegalArgumentException(
+ "keyGestureType was " + mKeyGestureType + " but must be one of: "
+ + "KEY_GESTURE_TYPE_UNSPECIFIED(" + KEY_GESTURE_TYPE_UNSPECIFIED + "), "
+ + "KEY_GESTURE_TYPE_HOME(" + KEY_GESTURE_TYPE_HOME + "), "
+ + "KEY_GESTURE_TYPE_RECENT_APPS(" + KEY_GESTURE_TYPE_RECENT_APPS + "), "
+ + "KEY_GESTURE_TYPE_BACK(" + KEY_GESTURE_TYPE_BACK + "), "
+ + "KEY_GESTURE_TYPE_APP_SWITCH(" + KEY_GESTURE_TYPE_APP_SWITCH + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_ASSISTANT(" + KEY_GESTURE_TYPE_LAUNCH_ASSISTANT + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT(" + KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS(" + KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS + "), "
+ + "KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL(" + KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL + "), "
+ + "KEY_GESTURE_TYPE_TOGGLE_TASKBAR(" + KEY_GESTURE_TYPE_TOGGLE_TASKBAR + "), "
+ + "KEY_GESTURE_TYPE_TAKE_SCREENSHOT(" + KEY_GESTURE_TYPE_TAKE_SCREENSHOT + "), "
+ + "KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER(" + KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER + "), "
+ + "KEY_GESTURE_TYPE_BRIGHTNESS_UP(" + KEY_GESTURE_TYPE_BRIGHTNESS_UP + "), "
+ + "KEY_GESTURE_TYPE_BRIGHTNESS_DOWN(" + KEY_GESTURE_TYPE_BRIGHTNESS_DOWN + "), "
+ + "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP(" + KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP + "), "
+ + "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN(" + KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN + "), "
+ + "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE(" + KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE + "), "
+ + "KEY_GESTURE_TYPE_VOLUME_UP(" + KEY_GESTURE_TYPE_VOLUME_UP + "), "
+ + "KEY_GESTURE_TYPE_VOLUME_DOWN(" + KEY_GESTURE_TYPE_VOLUME_DOWN + "), "
+ + "KEY_GESTURE_TYPE_VOLUME_MUTE(" + KEY_GESTURE_TYPE_VOLUME_MUTE + "), "
+ + "KEY_GESTURE_TYPE_ALL_APPS(" + KEY_GESTURE_TYPE_ALL_APPS + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_SEARCH(" + KEY_GESTURE_TYPE_LAUNCH_SEARCH + "), "
+ + "KEY_GESTURE_TYPE_LANGUAGE_SWITCH(" + KEY_GESTURE_TYPE_LANGUAGE_SWITCH + "), "
+ + "KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS(" + KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS + "), "
+ + "KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK(" + KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK + "), "
+ + "KEY_GESTURE_TYPE_SYSTEM_MUTE(" + KEY_GESTURE_TYPE_SYSTEM_MUTE + "), "
+ + "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION(" + KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION + "), "
+ + "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS(" + KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS + "), "
+ + "KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT(" + KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT + "), "
+ + "KEY_GESTURE_TYPE_LOCK_SCREEN(" + KEY_GESTURE_TYPE_LOCK_SCREEN + "), "
+ + "KEY_GESTURE_TYPE_OPEN_NOTES(" + KEY_GESTURE_TYPE_OPEN_NOTES + "), "
+ + "KEY_GESTURE_TYPE_TOGGLE_POWER(" + KEY_GESTURE_TYPE_TOGGLE_POWER + "), "
+ + "KEY_GESTURE_TYPE_SYSTEM_NAVIGATION(" + KEY_GESTURE_TYPE_SYSTEM_NAVIGATION + "), "
+ + "KEY_GESTURE_TYPE_SLEEP(" + KEY_GESTURE_TYPE_SLEEP + "), "
+ + "KEY_GESTURE_TYPE_WAKEUP(" + KEY_GESTURE_TYPE_WAKEUP + "), "
+ + "KEY_GESTURE_TYPE_MEDIA_KEY(" + KEY_GESTURE_TYPE_MEDIA_KEY + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS + "), "
+ + "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME(" + KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME + "), "
+ + "KEY_GESTURE_TYPE_DESKTOP_MODE(" + KEY_GESTURE_TYPE_DESKTOP_MODE + "), "
+ + "KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION(" + KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public int getDeviceId() {
+ return mDeviceId;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull int[] getKeycodes() {
+ return mKeycodes;
+ }
+
+ @DataClass.Generated.Member
+ public int getModifierState() {
+ return mModifierState;
+ }
+
+ @DataClass.Generated.Member
+ public @KeyGestureType int getKeyGestureType() {
+ return mKeyGestureType;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "KeyGestureEvent { " +
+ "deviceId = " + mDeviceId + ", " +
+ "keycodes = " + java.util.Arrays.toString(mKeycodes) + ", " +
+ "modifierState = " + mModifierState + ", " +
+ "keyGestureType = " + keyGestureTypeToString(mKeyGestureType) +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(KeyGestureEvent other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ KeyGestureEvent that = (KeyGestureEvent) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mDeviceId == that.mDeviceId
+ && java.util.Arrays.equals(mKeycodes, that.mKeycodes)
+ && mModifierState == that.mModifierState
+ && mKeyGestureType == that.mKeyGestureType;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mDeviceId;
+ _hash = 31 * _hash + java.util.Arrays.hashCode(mKeycodes);
+ _hash = 31 * _hash + mModifierState;
+ _hash = 31 * _hash + mKeyGestureType;
+ return _hash;
+ }
+
+ @DataClass.Generated(
+ time = 1723409092192L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/hardware/input/KeyGestureEvent.java",
+ inputSignatures = "private final int mDeviceId\nprivate final @android.annotation.NonNull int[] mKeycodes\nprivate final int mModifierState\nprivate final @android.hardware.input.KeyGestureEvent.KeyGestureType int mKeyGestureType\npublic static final int KEY_GESTURE_TYPE_UNSPECIFIED\npublic static final int KEY_GESTURE_TYPE_HOME\npublic static final int KEY_GESTURE_TYPE_RECENT_APPS\npublic static final int KEY_GESTURE_TYPE_BACK\npublic static final int KEY_GESTURE_TYPE_APP_SWITCH\npublic static final int KEY_GESTURE_TYPE_LAUNCH_ASSISTANT\npublic static final int KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT\npublic static final int KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS\npublic static final int KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL\npublic static final int KEY_GESTURE_TYPE_TOGGLE_TASKBAR\npublic static final int KEY_GESTURE_TYPE_TAKE_SCREENSHOT\npublic static final int KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER\npublic static final int KEY_GESTURE_TYPE_BRIGHTNESS_UP\npublic static final int KEY_GESTURE_TYPE_BRIGHTNESS_DOWN\npublic static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP\npublic static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN\npublic static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE\npublic static final int KEY_GESTURE_TYPE_VOLUME_UP\npublic static final int KEY_GESTURE_TYPE_VOLUME_DOWN\npublic static final int KEY_GESTURE_TYPE_VOLUME_MUTE\npublic static final int KEY_GESTURE_TYPE_ALL_APPS\npublic static final int KEY_GESTURE_TYPE_LAUNCH_SEARCH\npublic static final int KEY_GESTURE_TYPE_LANGUAGE_SWITCH\npublic static final int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS\npublic static final int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK\npublic static final int KEY_GESTURE_TYPE_SYSTEM_MUTE\npublic static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION\npublic static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS\npublic static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT\npublic static final int KEY_GESTURE_TYPE_LOCK_SCREEN\npublic static final int KEY_GESTURE_TYPE_OPEN_NOTES\npublic static final int KEY_GESTURE_TYPE_TOGGLE_POWER\npublic static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION\npublic static final int KEY_GESTURE_TYPE_SLEEP\npublic static final int KEY_GESTURE_TYPE_WAKEUP\npublic static final int KEY_GESTURE_TYPE_MEDIA_KEY\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER\npublic static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS\npublic static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME\npublic static final int KEY_GESTURE_TYPE_DESKTOP_MODE\npublic static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION\nclass KeyGestureEvent extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/hardware/input/KeyboardSystemShortcut.java b/core/java/android/hardware/input/KeyboardSystemShortcut.java
deleted file mode 100644
index 89cf877..0000000
--- a/core/java/android/hardware/input/KeyboardSystemShortcut.java
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.input;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.android.internal.util.DataClass;
-import com.android.internal.util.FrameworkStatsLog;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Provides information about the keyboard shortcut being triggered by an external keyboard.
- *
- * @hide
- */
-@DataClass(genToString = true, genEqualsHashCode = true)
-public class KeyboardSystemShortcut {
-
- private static final String TAG = "KeyboardSystemShortcut";
-
- @NonNull
- private final int[] mKeycodes;
- private final int mModifierState;
- @SystemShortcut
- private final int mSystemShortcut;
-
-
- public static final int SYSTEM_SHORTCUT_UNSPECIFIED =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
- public static final int SYSTEM_SHORTCUT_HOME =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
- public static final int SYSTEM_SHORTCUT_RECENT_APPS =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS;
- public static final int SYSTEM_SHORTCUT_BACK =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK;
- public static final int SYSTEM_SHORTCUT_APP_SWITCH =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__APP_SWITCH;
- public static final int SYSTEM_SHORTCUT_LAUNCH_ASSISTANT =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_ASSISTANT;
- public static final int SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_VOICE_ASSISTANT;
- public static final int SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SYSTEM_SETTINGS;
- public static final int SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_NOTIFICATION_PANEL;
- public static final int SYSTEM_SHORTCUT_TOGGLE_TASKBAR =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_TASKBAR;
- public static final int SYSTEM_SHORTCUT_TAKE_SCREENSHOT =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TAKE_SCREENSHOT;
- public static final int SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_SHORTCUT_HELPER;
- public static final int SYSTEM_SHORTCUT_BRIGHTNESS_UP =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_UP;
- public static final int SYSTEM_SHORTCUT_BRIGHTNESS_DOWN =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_DOWN;
- public static final int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_UP;
- public static final int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_DOWN;
- public static final int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_TOGGLE;
- public static final int SYSTEM_SHORTCUT_VOLUME_UP =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_UP;
- public static final int SYSTEM_SHORTCUT_VOLUME_DOWN =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_DOWN;
- public static final int SYSTEM_SHORTCUT_VOLUME_MUTE =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_MUTE;
- public static final int SYSTEM_SHORTCUT_ALL_APPS =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ALL_APPS;
- public static final int SYSTEM_SHORTCUT_LAUNCH_SEARCH =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SEARCH;
- public static final int SYSTEM_SHORTCUT_LANGUAGE_SWITCH =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LANGUAGE_SWITCH;
- public static final int SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ACCESSIBILITY_ALL_APPS;
- public static final int SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK;
- public static final int SYSTEM_SHORTCUT_SYSTEM_MUTE =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE;
- public static final int SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION;
- public static final int SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS;
- public static final int SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT;
- public static final int SYSTEM_SHORTCUT_LOCK_SCREEN =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LOCK_SCREEN;
- public static final int SYSTEM_SHORTCUT_OPEN_NOTES =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_NOTES;
- public static final int SYSTEM_SHORTCUT_TOGGLE_POWER =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_POWER;
- public static final int SYSTEM_SHORTCUT_SYSTEM_NAVIGATION =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_NAVIGATION;
- public static final int SYSTEM_SHORTCUT_SLEEP =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SLEEP;
- public static final int SYSTEM_SHORTCUT_WAKEUP =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__WAKEUP;
- public static final int SYSTEM_SHORTCUT_MEDIA_KEY =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MEDIA_KEY;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
- public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
- public static final int SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
- public static final int SYSTEM_SHORTCUT_DESKTOP_MODE =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
- public static final int SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION =
- FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
-
-
-
- // Code below generated by codegen v1.0.23.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/input/KeyboardSystemShortcut.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- @IntDef(prefix = "SYSTEM_SHORTCUT_", value = {
- SYSTEM_SHORTCUT_UNSPECIFIED,
- SYSTEM_SHORTCUT_HOME,
- SYSTEM_SHORTCUT_RECENT_APPS,
- SYSTEM_SHORTCUT_BACK,
- SYSTEM_SHORTCUT_APP_SWITCH,
- SYSTEM_SHORTCUT_LAUNCH_ASSISTANT,
- SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT,
- SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS,
- SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
- SYSTEM_SHORTCUT_TOGGLE_TASKBAR,
- SYSTEM_SHORTCUT_TAKE_SCREENSHOT,
- SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER,
- SYSTEM_SHORTCUT_BRIGHTNESS_UP,
- SYSTEM_SHORTCUT_BRIGHTNESS_DOWN,
- SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP,
- SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN,
- SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE,
- SYSTEM_SHORTCUT_VOLUME_UP,
- SYSTEM_SHORTCUT_VOLUME_DOWN,
- SYSTEM_SHORTCUT_VOLUME_MUTE,
- SYSTEM_SHORTCUT_ALL_APPS,
- SYSTEM_SHORTCUT_LAUNCH_SEARCH,
- SYSTEM_SHORTCUT_LANGUAGE_SWITCH,
- SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS,
- SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK,
- SYSTEM_SHORTCUT_SYSTEM_MUTE,
- SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION,
- SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS,
- SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT,
- SYSTEM_SHORTCUT_LOCK_SCREEN,
- SYSTEM_SHORTCUT_OPEN_NOTES,
- SYSTEM_SHORTCUT_TOGGLE_POWER,
- SYSTEM_SHORTCUT_SYSTEM_NAVIGATION,
- SYSTEM_SHORTCUT_SLEEP,
- SYSTEM_SHORTCUT_WAKEUP,
- SYSTEM_SHORTCUT_MEDIA_KEY,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER,
- SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS,
- SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
- SYSTEM_SHORTCUT_DESKTOP_MODE,
- SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION
- })
- @Retention(RetentionPolicy.SOURCE)
- @DataClass.Generated.Member
- public @interface SystemShortcut {}
-
- @DataClass.Generated.Member
- public static String systemShortcutToString(@SystemShortcut int value) {
- switch (value) {
- case SYSTEM_SHORTCUT_UNSPECIFIED:
- return "SYSTEM_SHORTCUT_UNSPECIFIED";
- case SYSTEM_SHORTCUT_HOME:
- return "SYSTEM_SHORTCUT_HOME";
- case SYSTEM_SHORTCUT_RECENT_APPS:
- return "SYSTEM_SHORTCUT_RECENT_APPS";
- case SYSTEM_SHORTCUT_BACK:
- return "SYSTEM_SHORTCUT_BACK";
- case SYSTEM_SHORTCUT_APP_SWITCH:
- return "SYSTEM_SHORTCUT_APP_SWITCH";
- case SYSTEM_SHORTCUT_LAUNCH_ASSISTANT:
- return "SYSTEM_SHORTCUT_LAUNCH_ASSISTANT";
- case SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT:
- return "SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT";
- case SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS:
- return "SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS";
- case SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL:
- return "SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL";
- case SYSTEM_SHORTCUT_TOGGLE_TASKBAR:
- return "SYSTEM_SHORTCUT_TOGGLE_TASKBAR";
- case SYSTEM_SHORTCUT_TAKE_SCREENSHOT:
- return "SYSTEM_SHORTCUT_TAKE_SCREENSHOT";
- case SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER:
- return "SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER";
- case SYSTEM_SHORTCUT_BRIGHTNESS_UP:
- return "SYSTEM_SHORTCUT_BRIGHTNESS_UP";
- case SYSTEM_SHORTCUT_BRIGHTNESS_DOWN:
- return "SYSTEM_SHORTCUT_BRIGHTNESS_DOWN";
- case SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP:
- return "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP";
- case SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN:
- return "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN";
- case SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE:
- return "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE";
- case SYSTEM_SHORTCUT_VOLUME_UP:
- return "SYSTEM_SHORTCUT_VOLUME_UP";
- case SYSTEM_SHORTCUT_VOLUME_DOWN:
- return "SYSTEM_SHORTCUT_VOLUME_DOWN";
- case SYSTEM_SHORTCUT_VOLUME_MUTE:
- return "SYSTEM_SHORTCUT_VOLUME_MUTE";
- case SYSTEM_SHORTCUT_ALL_APPS:
- return "SYSTEM_SHORTCUT_ALL_APPS";
- case SYSTEM_SHORTCUT_LAUNCH_SEARCH:
- return "SYSTEM_SHORTCUT_LAUNCH_SEARCH";
- case SYSTEM_SHORTCUT_LANGUAGE_SWITCH:
- return "SYSTEM_SHORTCUT_LANGUAGE_SWITCH";
- case SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS:
- return "SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS";
- case SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK:
- return "SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK";
- case SYSTEM_SHORTCUT_SYSTEM_MUTE:
- return "SYSTEM_SHORTCUT_SYSTEM_MUTE";
- case SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION:
- return "SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION";
- case SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS:
- return "SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS";
- case SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT:
- return "SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT";
- case SYSTEM_SHORTCUT_LOCK_SCREEN:
- return "SYSTEM_SHORTCUT_LOCK_SCREEN";
- case SYSTEM_SHORTCUT_OPEN_NOTES:
- return "SYSTEM_SHORTCUT_OPEN_NOTES";
- case SYSTEM_SHORTCUT_TOGGLE_POWER:
- return "SYSTEM_SHORTCUT_TOGGLE_POWER";
- case SYSTEM_SHORTCUT_SYSTEM_NAVIGATION:
- return "SYSTEM_SHORTCUT_SYSTEM_NAVIGATION";
- case SYSTEM_SHORTCUT_SLEEP:
- return "SYSTEM_SHORTCUT_SLEEP";
- case SYSTEM_SHORTCUT_WAKEUP:
- return "SYSTEM_SHORTCUT_WAKEUP";
- case SYSTEM_SHORTCUT_MEDIA_KEY:
- return "SYSTEM_SHORTCUT_MEDIA_KEY";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER";
- case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS:
- return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS";
- case SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
- return "SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME";
- case SYSTEM_SHORTCUT_DESKTOP_MODE:
- return "SYSTEM_SHORTCUT_DESKTOP_MODE";
- case SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION:
- return "SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION";
- default: return Integer.toHexString(value);
- }
- }
-
- @DataClass.Generated.Member
- public KeyboardSystemShortcut(
- @NonNull int[] keycodes,
- int modifierState,
- @SystemShortcut int systemShortcut) {
- this.mKeycodes = keycodes;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mKeycodes);
- this.mModifierState = modifierState;
- this.mSystemShortcut = systemShortcut;
-
- if (!(mSystemShortcut == SYSTEM_SHORTCUT_UNSPECIFIED)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_HOME)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_RECENT_APPS)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_BACK)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_APP_SWITCH)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_ASSISTANT)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_TOGGLE_TASKBAR)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_TAKE_SCREENSHOT)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_BRIGHTNESS_UP)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_BRIGHTNESS_DOWN)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_VOLUME_UP)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_VOLUME_DOWN)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_VOLUME_MUTE)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_ALL_APPS)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_SEARCH)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LANGUAGE_SWITCH)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_SYSTEM_MUTE)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LOCK_SCREEN)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_OPEN_NOTES)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_TOGGLE_POWER)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_SYSTEM_NAVIGATION)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_SLEEP)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_WAKEUP)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_MEDIA_KEY)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_DESKTOP_MODE)
- && !(mSystemShortcut == SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION)) {
- throw new java.lang.IllegalArgumentException(
- "systemShortcut was " + mSystemShortcut + " but must be one of: "
- + "SYSTEM_SHORTCUT_UNSPECIFIED(" + SYSTEM_SHORTCUT_UNSPECIFIED + "), "
- + "SYSTEM_SHORTCUT_HOME(" + SYSTEM_SHORTCUT_HOME + "), "
- + "SYSTEM_SHORTCUT_RECENT_APPS(" + SYSTEM_SHORTCUT_RECENT_APPS + "), "
- + "SYSTEM_SHORTCUT_BACK(" + SYSTEM_SHORTCUT_BACK + "), "
- + "SYSTEM_SHORTCUT_APP_SWITCH(" + SYSTEM_SHORTCUT_APP_SWITCH + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_ASSISTANT(" + SYSTEM_SHORTCUT_LAUNCH_ASSISTANT + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT(" + SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS(" + SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS + "), "
- + "SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL(" + SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL + "), "
- + "SYSTEM_SHORTCUT_TOGGLE_TASKBAR(" + SYSTEM_SHORTCUT_TOGGLE_TASKBAR + "), "
- + "SYSTEM_SHORTCUT_TAKE_SCREENSHOT(" + SYSTEM_SHORTCUT_TAKE_SCREENSHOT + "), "
- + "SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER(" + SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER + "), "
- + "SYSTEM_SHORTCUT_BRIGHTNESS_UP(" + SYSTEM_SHORTCUT_BRIGHTNESS_UP + "), "
- + "SYSTEM_SHORTCUT_BRIGHTNESS_DOWN(" + SYSTEM_SHORTCUT_BRIGHTNESS_DOWN + "), "
- + "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP(" + SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP + "), "
- + "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN(" + SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN + "), "
- + "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE(" + SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE + "), "
- + "SYSTEM_SHORTCUT_VOLUME_UP(" + SYSTEM_SHORTCUT_VOLUME_UP + "), "
- + "SYSTEM_SHORTCUT_VOLUME_DOWN(" + SYSTEM_SHORTCUT_VOLUME_DOWN + "), "
- + "SYSTEM_SHORTCUT_VOLUME_MUTE(" + SYSTEM_SHORTCUT_VOLUME_MUTE + "), "
- + "SYSTEM_SHORTCUT_ALL_APPS(" + SYSTEM_SHORTCUT_ALL_APPS + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_SEARCH(" + SYSTEM_SHORTCUT_LAUNCH_SEARCH + "), "
- + "SYSTEM_SHORTCUT_LANGUAGE_SWITCH(" + SYSTEM_SHORTCUT_LANGUAGE_SWITCH + "), "
- + "SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS(" + SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS + "), "
- + "SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK(" + SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK + "), "
- + "SYSTEM_SHORTCUT_SYSTEM_MUTE(" + SYSTEM_SHORTCUT_SYSTEM_MUTE + "), "
- + "SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION(" + SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION + "), "
- + "SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS(" + SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS + "), "
- + "SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT(" + SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT + "), "
- + "SYSTEM_SHORTCUT_LOCK_SCREEN(" + SYSTEM_SHORTCUT_LOCK_SCREEN + "), "
- + "SYSTEM_SHORTCUT_OPEN_NOTES(" + SYSTEM_SHORTCUT_OPEN_NOTES + "), "
- + "SYSTEM_SHORTCUT_TOGGLE_POWER(" + SYSTEM_SHORTCUT_TOGGLE_POWER + "), "
- + "SYSTEM_SHORTCUT_SYSTEM_NAVIGATION(" + SYSTEM_SHORTCUT_SYSTEM_NAVIGATION + "), "
- + "SYSTEM_SHORTCUT_SLEEP(" + SYSTEM_SHORTCUT_SLEEP + "), "
- + "SYSTEM_SHORTCUT_WAKEUP(" + SYSTEM_SHORTCUT_WAKEUP + "), "
- + "SYSTEM_SHORTCUT_MEDIA_KEY(" + SYSTEM_SHORTCUT_MEDIA_KEY + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS + "), "
- + "SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME(" + SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME + "), "
- + "SYSTEM_SHORTCUT_DESKTOP_MODE(" + SYSTEM_SHORTCUT_DESKTOP_MODE + "), "
- + "SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION(" + SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION + ")");
- }
-
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public @NonNull int[] getKeycodes() {
- return mKeycodes;
- }
-
- @DataClass.Generated.Member
- public int getModifierState() {
- return mModifierState;
- }
-
- @DataClass.Generated.Member
- public @SystemShortcut int getSystemShortcut() {
- return mSystemShortcut;
- }
-
- @Override
- @DataClass.Generated.Member
- public String toString() {
- // You can override field toString logic by defining methods like:
- // String fieldNameToString() { ... }
-
- return "KeyboardSystemShortcut { " +
- "keycodes = " + java.util.Arrays.toString(mKeycodes) + ", " +
- "modifierState = " + mModifierState + ", " +
- "systemShortcut = " + systemShortcutToString(mSystemShortcut) +
- " }";
- }
-
- @Override
- @DataClass.Generated.Member
- public boolean equals(@Nullable Object o) {
- // You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(KeyboardSystemShortcut other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
- KeyboardSystemShortcut that = (KeyboardSystemShortcut) o;
- //noinspection PointlessBooleanExpression
- return true
- && java.util.Arrays.equals(mKeycodes, that.mKeycodes)
- && mModifierState == that.mModifierState
- && mSystemShortcut == that.mSystemShortcut;
- }
-
- @Override
- @DataClass.Generated.Member
- public int hashCode() {
- // You can override field hashCode logic by defining methods like:
- // int fieldNameHashCode() { ... }
-
- int _hash = 1;
- _hash = 31 * _hash + java.util.Arrays.hashCode(mKeycodes);
- _hash = 31 * _hash + mModifierState;
- _hash = 31 * _hash + mSystemShortcut;
- return _hash;
- }
-
- @DataClass.Generated(
- time = 1722890917041L,
- codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/core/java/android/hardware/input/KeyboardSystemShortcut.java",
- inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull int[] mKeycodes\nprivate final int mModifierState\nprivate final @android.hardware.input.KeyboardSystemShortcut.SystemShortcut int mSystemShortcut\npublic static final int SYSTEM_SHORTCUT_UNSPECIFIED\npublic static final int SYSTEM_SHORTCUT_HOME\npublic static final int SYSTEM_SHORTCUT_RECENT_APPS\npublic static final int SYSTEM_SHORTCUT_BACK\npublic static final int SYSTEM_SHORTCUT_APP_SWITCH\npublic static final int SYSTEM_SHORTCUT_LAUNCH_ASSISTANT\npublic static final int SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT\npublic static final int SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS\npublic static final int SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL\npublic static final int SYSTEM_SHORTCUT_TOGGLE_TASKBAR\npublic static final int SYSTEM_SHORTCUT_TAKE_SCREENSHOT\npublic static final int SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER\npublic static final int SYSTEM_SHORTCUT_BRIGHTNESS_UP\npublic static final int SYSTEM_SHORTCUT_BRIGHTNESS_DOWN\npublic static final int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP\npublic static final int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN\npublic static final int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE\npublic static final int SYSTEM_SHORTCUT_VOLUME_UP\npublic static final int SYSTEM_SHORTCUT_VOLUME_DOWN\npublic static final int SYSTEM_SHORTCUT_VOLUME_MUTE\npublic static final int SYSTEM_SHORTCUT_ALL_APPS\npublic static final int SYSTEM_SHORTCUT_LAUNCH_SEARCH\npublic static final int SYSTEM_SHORTCUT_LANGUAGE_SWITCH\npublic static final int SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS\npublic static final int SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK\npublic static final int SYSTEM_SHORTCUT_SYSTEM_MUTE\npublic static final int SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION\npublic static final int SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS\npublic static final int SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT\npublic static final int SYSTEM_SHORTCUT_LOCK_SCREEN\npublic static final int SYSTEM_SHORTCUT_OPEN_NOTES\npublic static final int SYSTEM_SHORTCUT_TOGGLE_POWER\npublic static final int SYSTEM_SHORTCUT_SYSTEM_NAVIGATION\npublic static final int SYSTEM_SHORTCUT_SLEEP\npublic static final int SYSTEM_SHORTCUT_WAKEUP\npublic static final int SYSTEM_SHORTCUT_MEDIA_KEY\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER\npublic static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS\npublic static final int SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME\npublic static final int SYSTEM_SHORTCUT_DESKTOP_MODE\npublic static final int SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION\nclass KeyboardSystemShortcut extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 1fb7937..29ccb85 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -145,10 +145,11 @@
* @param screenBrightnessInt The overridden screen brightness between 1 and 255, or
* {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override. Not used if
* screenBrightnessFloat is provided (is not NaN).
+ * @param useNormalBrightnessForDoze Whether use normal brightness while device is dozing.
*/
public abstract void setDozeOverrideFromDreamManager(
int screenState, @Display.StateReason int reason, float screenBrightnessFloat,
- int screenBrightnessInt);
+ int screenBrightnessInt, boolean useNormalBrightnessForDoze);
/**
* Used by sidekick manager to tell the power manager if it shouldn't change the display state
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 3fe063d..4c4aa6c 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -223,3 +223,14 @@
description: "Show access entry of location bypass permission in the Privacy Dashboard"
bug: "325536053"
}
+
+flag {
+ name: "dont_remove_existing_uid_states"
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "Double check if the uid still exists before attempting to remove its appops state"
+ bug: "353474742"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 133b3d1..9c281f3 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -17,8 +17,8 @@
package android.service.dreams;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
import static android.service.dreams.Flags.dreamHandlesBeingObscured;
+import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
import static android.service.dreams.Flags.startAndStopDozingInBackground;
import android.annotation.FlaggedApi;
@@ -272,6 +272,9 @@
private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
private float mDozeScreenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ // This variable being true means dozing device expecting normal(non-doze) brightness.
+ private boolean mUseNormalBrightnessForDoze;
+
private boolean mDebug = false;
private ComponentName mDreamComponent;
@@ -935,13 +938,14 @@
if (startAndStopDozingInBackground()) {
mDreamManager.startDozingOneway(
mDreamToken, mDozeScreenState, mDozeScreenStateReason,
- mDozeScreenBrightnessFloat, mDozeScreenBrightness);
+ mDozeScreenBrightnessFloat, mDozeScreenBrightness,
+ mUseNormalBrightnessForDoze);
} else {
mDreamManager.startDozing(
mDreamToken, mDozeScreenState, mDozeScreenStateReason,
- mDozeScreenBrightnessFloat, mDozeScreenBrightness);
+ mDozeScreenBrightnessFloat, mDozeScreenBrightness,
+ mUseNormalBrightnessForDoze);
}
-
} catch (RemoteException ex) {
// system server died
}
@@ -1010,7 +1014,8 @@
*/
@UnsupportedAppUsage
public void setDozeScreenState(int state) {
- setDozeScreenState(state, Display.STATE_REASON_UNKNOWN);
+ setDozeScreenState(state, Display.STATE_REASON_UNKNOWN,
+ /* useNormalBrightnessForDoze= */ false);
}
/**
@@ -1048,21 +1053,43 @@
* {@link Display#STATE_ON_SUSPEND}, {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN}
* for the default behavior.
* @param reason the reason for setting the specified screen state.
- *
- * @hide For use by system UI components only.
+ * @param useNormalBrightnessForDoze False means the default case where doze brightness is
+ * expected when device is dozing. True means display expects normal brightness for next doze
+ * request. Noted: unlike {@link #setDozeScreenBrightness} that sets a real brightness value for
+ * doze screen, this parameter only indicates whether the doze brightness is intended on next
+ * doze screen. The actual brightness value will be computed by {@link DisplayManager}
+ * internally.
+ * @hide For use by System UI components only.
*/
@UnsupportedAppUsage
- public void setDozeScreenState(int state, @Display.StateReason int reason) {
+ public void setDozeScreenState(int state, @Display.StateReason int reason,
+ boolean useNormalBrightnessForDoze) {
synchronized (this) {
- if (mDozeScreenState != state) {
+ if (mDozeScreenState != state
+ || mUseNormalBrightnessForDoze != useNormalBrightnessForDoze) {
mDozeScreenState = state;
mDozeScreenStateReason = reason;
+ mUseNormalBrightnessForDoze = useNormalBrightnessForDoze;
updateDoze();
}
}
}
/**
+ * Returns whether we want to use the normal brightness setting while in doze. This is useful
+ * on devices like Wear; when we allow the user to interact with a device that remains in
+ * doze (looking at time).
+ *
+ * @return a boolean that informs {@link DisplayManager} whether to adjust the display for the
+ * interacting user e.g. brightening the display.
+ * @hide For use by System UI components only.
+ */
+ @UnsupportedAppUsage
+ public boolean getUseNormalBrightnessForDoze() {
+ return mUseNormalBrightnessForDoze;
+ }
+
+ /**
* Gets the screen brightness to use while dozing.
*
* @return The screen brightness while dozing as a value between
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 611e791..1c0a2c0 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -43,7 +43,7 @@
void finishSelf(in IBinder token, boolean immediate);
/** @deprecated Please use startDozingOneway instead. */
void startDozing(in IBinder token, int screenState, int reason, float screenBrightnessFloat,
- int screenBrightnessInt);
+ int screenBrightnessInt, boolean useNormalBrightnessForDoze);
void stopDozing(in IBinder token);
void forceAmbientDisplayEnabled(boolean enabled);
ComponentName[] getDreamComponentsForUser(int userId);
@@ -54,6 +54,7 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)")
oneway void setDreamIsObscured(in boolean isObscured);
oneway void startDozingOneway(in IBinder token, int screenState, int reason,
- float screenBrightnessFloat, int screenBrightnessInt);
+ float screenBrightnessFloat, int screenBrightnessInt,
+ boolean useNormalBrightnessForDoze);
oneway void finishSelfOneway(in IBinder token, boolean immediate);
}
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 3d190fe..5c5a2f6 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -267,3 +267,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "typeface_cache_for_var_settings"
+ namespace: "text"
+ description: "Cache Typeface instance for font variation settings."
+ bug: "355462362"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 3adbd68..9f54d9f 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -24,7 +24,7 @@
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.Network;
-import android.net.NetworkInfo;
+import android.net.NetworkCapabilities;
import android.net.SntpClient;
import android.os.Build;
import android.os.SystemClock;
@@ -687,8 +687,16 @@
if (connectivityManager == null) {
return false;
}
- final NetworkInfo ni = connectivityManager.getNetworkInfo(network);
-
+ final NetworkCapabilities networkCapabilities =
+ connectivityManager.getNetworkCapabilities(network);
+ if (networkCapabilities == null) {
+ if (LOGD) Log.d(TAG, "getNetwork: failed to get network capabilities");
+ return false;
+ }
+ final boolean isConnectedToInternet = networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ && networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_VALIDATED);
// This connectivity check is to avoid performing a DNS lookup for the time server on a
// unconnected network. There are races to obtain time in Android when connectivity
// changes, which means that forceRefresh() can be called by various components before
@@ -698,8 +706,8 @@
// A side effect of check is that tests that run a fake NTP server on the device itself
// will only be able to use it if the active network is connected, even though loopback
// addresses are actually reachable.
- if (ni == null || !ni.isConnected()) {
- if (LOGD) Log.d(TAG, "getNetwork: no connectivity");
+ if (!isConnectedToInternet) {
+ if (LOGD) Log.d(TAG, "getNetwork: no internet connectivity");
return false;
}
return true;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1c0700f..e97f603 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -133,6 +133,7 @@
import static com.android.window.flags.Flags.insetsControlChangedItem;
import static com.android.window.flags.Flags.insetsControlSeq;
import static com.android.window.flags.Flags.setScPropertiesInClient;
+import static com.android.window.flags.Flags.systemUiImmersiveConfirmationDialog;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -378,7 +379,7 @@
* @hide
*/
public static final boolean CLIENT_IMMERSIVE_CONFIRMATION =
- SystemProperties.getBoolean("persist.wm.debug.client_immersive_confirmation", false);
+ systemUiImmersiveConfirmationDialog();
/**
* Set this system property to true to force the view hierarchy to render
diff --git a/core/java/android/view/contentprotection/OWNERS b/core/java/android/view/contentprotection/OWNERS
index b3583a7..48052c6 100644
--- a/core/java/android/view/contentprotection/OWNERS
+++ b/core/java/android/view/contentprotection/OWNERS
@@ -1,4 +1,6 @@
-# Bug component: 544200
+# Bug component: 1040349
-include /core/java/android/view/contentcapture/OWNERS
+njagar@google.com
+williamluh@google.com
+aaronjosephs@google.com
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index d12eda3..fe26510 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -741,9 +741,17 @@
public abstract boolean getUseWideViewPort();
/**
- * Sets whether the WebView whether supports multiple windows. If set to
- * true, {@link WebChromeClient#onCreateWindow} must be implemented by the
- * host application. The default is {@code false}.
+ * Sets whether the WebView should support multiple windows.
+ *
+ * <p>If set to {@code true}, the {@link WebChromeClient#onCreateWindow}
+ * callback must be implemented by the application to handle the
+ * creation of new windows.
+ *
+ * <p>The default is {@code false}. When multiple window support is disabled,
+ * requests to open new windows (either from the {@code window.open()}
+ * JavaScript API or from links with {@code target="_blank"}) will instead
+ * be treated as top-level navigations, replacing the current page in the
+ * same WebView.
*
* @param support whether to support multiple windows
*/
@@ -1338,18 +1346,24 @@
}
/**
- * Tells JavaScript to open windows automatically. This applies to the
- * JavaScript function {@code window.open()}. The default is {@code false}.
+ * Allows JavaScript to open windows without a user gesture. This applies to
+ * the JavaScript function {@code window.open()}. The default is
+ * {@code false}: attempts without a user gesture will fail and do nothing.
+ * <p>
+ * This is not affected by the {@link #setSupportMultipleWindows} setting;
+ * the user gesture requirement is enforced even if multiple windows are
+ * disabled.
*
- * @param flag {@code true} if JavaScript can open windows automatically
+ * @param flag {@code true} if JavaScript can open windows without a user
+ * gesture.
*/
public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean flag);
/**
- * Gets whether JavaScript can open windows automatically.
+ * Gets whether JavaScript can open windows without a user gesture.
*
- * @return {@code true} if JavaScript can open windows automatically during
- * {@code window.open()}
+ * @return {@code true} if JavaScript can open windows without a user
+ * gesture using {@code window.open()}
* @see #setJavaScriptCanOpenWindowsAutomatically
*/
public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 9aeccf4..61ee13a 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -91,6 +91,14 @@
}
flag {
+ name: "transit_tracker_plumbing"
+ namespace: "windowing_frontend"
+ description: "Plumb and collect on transition tracking object instead of singleton"
+ bug: "325114242"
+ is_fixed_read_only: true
+}
+
+flag {
name: "transit_ready_tracking"
namespace: "windowing_frontend"
description: "Enable accurate transition readiness tracking"
@@ -202,6 +210,14 @@
}
flag {
+ name: "system_ui_immersive_confirmation_dialog"
+ namespace: "windowing_frontend"
+ description: "Enable the implementation of the immersive confirmation dialog on system UI side by default"
+ bug: "359713629"
+ is_fixed_read_only: true
+}
+
+flag {
name: "ensure_wallpaper_in_transitions"
namespace: "windowing_frontend"
description: "Ensure that wallpaper window tokens are always present/available for collection in transitions"
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 27eebbe..ce17d78 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -74,6 +74,7 @@
* @param locale the locale picked.
*/
void onLocaleSelected(LocaleStore.LocaleInfo locale);
+ default void onParentLocaleSelected(LocaleStore.LocaleInfo locale) {}
}
/**
@@ -292,7 +293,7 @@
mListener, locale, mTranslatedOnly /* translate only */,
mOnActionExpandListener, this.mLocalePickerCollector);
}
-
+ mListener.onParentLocaleSelected(locale);
if (selector != null) {
getFragmentManager().beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
diff --git a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
index 572a599..fcc3023 100644
--- a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
@@ -48,6 +48,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
@@ -423,6 +424,14 @@
for (IProtoLogGroup group : protoLogGroups) {
mLogGroups.put(group.name(), group);
}
+
+ final var hasGroupsLoggingToLogcat = Arrays.stream(protoLogGroups)
+ .anyMatch(IProtoLogGroup::isLogToLogcat);
+
+ final ILogger logger = (msg) -> Slog.i(TAG, msg);
+ if (hasGroupsLoggingToLogcat) {
+ mViewerConfig.loadViewerConfig(logger, mLegacyViewerConfigFilename);
+ }
}
}
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index cb20ceb..14786e1 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -16,6 +16,7 @@
package com.android.internal.protolog;
+import static android.content.Context.PROTOLOG_SERVICE;
import static android.internal.perfetto.protos.InternedDataOuterClass.InternedData.PROTOLOG_STACKTRACE;
import static android.internal.perfetto.protos.InternedDataOuterClass.InternedData.PROTOLOG_STRING_ARGS;
import static android.internal.perfetto.protos.ProfileCommon.InternedString.IID;
@@ -46,6 +47,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.text.TextUtils;
@@ -76,6 +79,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
@@ -103,36 +107,45 @@
private final ProtoLogViewerConfigReader mViewerConfigReader;
@Nullable
private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
+ @NonNull
private final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>();
+ @NonNull
private final Runnable mCacheUpdater;
+ @Nullable // null when the flag android.tracing.client_side_proto_logging is not flipped
+ private final IProtoLogService mProtoLogService;
+
+ @NonNull
private final int[] mDefaultLogLevelCounts = new int[LogLevel.values().length];
+ @NonNull
private final Map<String, int[]> mLogLevelCounts = new ArrayMap<>();
+ @NonNull
private final Map<String, Integer> mCollectStackTraceGroupCounts = new ArrayMap<>();
private final Lock mBackgroundServiceLock = new ReentrantLock();
private ExecutorService mBackgroundLoggingService = Executors.newSingleThreadExecutor();
- public PerfettoProtoLogImpl(@NonNull String viewerConfigFilePath, Runnable cacheUpdater) {
- this(() -> {
- try {
- return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
- } catch (FileNotFoundException e) {
- throw new RuntimeException("Failed to load viewer config file " + viewerConfigFilePath, e);
- }
- }, cacheUpdater);
+ public PerfettoProtoLogImpl() {
+ this(null, null, null, () -> {});
}
- public PerfettoProtoLogImpl() {
- this(null, null, () -> {});
+ public PerfettoProtoLogImpl(@NonNull Runnable cacheUpdater) {
+ this(null, null, null, cacheUpdater);
}
public PerfettoProtoLogImpl(
- @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
- Runnable cacheUpdater
- ) {
- this(viewerConfigInputStreamProvider,
- new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider),
+ @NonNull String viewerConfigFilePath,
+ @NonNull Runnable cacheUpdater) {
+ this(viewerConfigFilePath,
+ null,
+ new ProtoLogViewerConfigReader(() -> {
+ try {
+ return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(
+ "Failed to load viewer config file " + viewerConfigFilePath, e);
+ }
+ }),
cacheUpdater);
}
@@ -140,8 +153,18 @@
public PerfettoProtoLogImpl(
@Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
@Nullable ProtoLogViewerConfigReader viewerConfigReader,
- Runnable cacheUpdater
- ) {
+ @NonNull Runnable cacheUpdater) {
+ this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater);
+ }
+
+ private PerfettoProtoLogImpl(
+ @Nullable String viewerConfigFilePath,
+ @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
+ @Nullable ProtoLogViewerConfigReader viewerConfigReader,
+ @NonNull Runnable cacheUpdater) {
+ assert (viewerConfigFilePath == null || viewerConfigInputStreamProvider == null) :
+ "Only one of viewerConfigFilePath and viewerConfigInputStreamProvider can be set";
+
Producer.init(InitArguments.DEFAULTS);
DataSourceParams params =
new DataSourceParams.Builder()
@@ -153,6 +176,27 @@
this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider;
this.mViewerConfigReader = viewerConfigReader;
this.mCacheUpdater = cacheUpdater;
+
+ if (android.tracing.Flags.clientSideProtoLogging()) {
+ mProtoLogService =
+ IProtoLogService.Stub.asInterface(ServiceManager.getService(PROTOLOG_SERVICE));
+ Objects.requireNonNull(mProtoLogService,
+ "ServiceManager returned a null ProtoLog service");
+
+ try {
+ var args = new ProtoLogService.RegisterClientArgs();
+
+ if (viewerConfigFilePath != null) {
+ args.setViewerConfigFile(viewerConfigFilePath);
+ }
+
+ mProtoLogService.registerClient(this, args);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to register ProtoLog client");
+ }
+ } else {
+ mProtoLogService = null;
+ }
}
/**
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 77ca7ce..8659a8f 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -111,7 +111,7 @@
// TODO(b/353530422): Remove - temporary fix to unblock b/352290057
// In so tests the viewer config file might not exist in which we don't
// want to provide config path to the user
- sServiceInstance = new PerfettoProtoLogImpl(null, null, sCacheUpdater);
+ sServiceInstance = new PerfettoProtoLogImpl(sCacheUpdater);
} else {
sServiceInstance = new PerfettoProtoLogImpl(sViewerConfigPath, sCacheUpdater);
}
diff --git a/core/jni/android_database_SQLiteRawStatement.cpp b/core/jni/android_database_SQLiteRawStatement.cpp
index 9614864..85a6bdf 100644
--- a/core/jni/android_database_SQLiteRawStatement.cpp
+++ b/core/jni/android_database_SQLiteRawStatement.cpp
@@ -72,14 +72,17 @@
// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is out
-// of bounds.
-static void throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
+// of bounds. It returns true if an exception was thrown.
+static bool throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
if (col < 0 || col >= sqlite3_data_count(stmt(stmtPtr))) {
int count = sqlite3_data_count(stmt(stmtPtr));
std::string message = android::base::StringPrintf(
"column index %d out of bounds [0,%d]", col, count - 1);
char const * errmsg = sqlite3_errstr(SQLITE_RANGE);
throw_sqlite3_exception(env, SQLITE_RANGE, errmsg, message.c_str());
+ return true;
+ } else {
+ return false;
}
}
@@ -216,12 +219,16 @@
static jint columnType(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
- throwIfInvalidColumn(env, stmtPtr, col);
+ if (throwIfInvalidColumn(env, stmtPtr, col)) {
+ return 0;
+ }
return sqlite3_column_type(stmt(stmtPtr), col);
}
static jstring columnName(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
- throwIfInvalidColumn(env, stmtPtr, col);
+ if (throwIfInvalidColumn(env, stmtPtr, col)) {
+ return nullptr;
+ }
const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(stmt(stmtPtr), col));
if (name == nullptr) {
throw_sqlite3_exception(env, db(stmtPtr), "error fetching columnName()");
@@ -232,14 +239,18 @@
}
static jint columnBytes(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
- throwIfInvalidColumn(env, stmtPtr, col);
+ if (throwIfInvalidColumn(env, stmtPtr, col)) {
+ return 0;
+ }
int r = sqlite3_column_bytes16(stmt(stmtPtr), col);
throwIfError(env, stmtPtr);
return r;
}
static jbyteArray columnBlob(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
- throwIfInvalidColumn(env, stmtPtr, col);
+ if (throwIfInvalidColumn(env, stmtPtr, col)) {
+ return nullptr;
+ }
const void* blob = sqlite3_column_blob(stmt(stmtPtr), col);
if (blob == nullptr) {
if (throwIfError(env, stmtPtr)) {
@@ -262,7 +273,9 @@
static int columnBuffer(JNIEnv* env, jclass, jlong stmtPtr, jint col,
jbyteArray buffer, jint offset, jint length, jint srcOffset) {
- throwIfInvalidColumn(env, stmtPtr, col);
+ if (throwIfInvalidColumn(env, stmtPtr, col)) {
+ return 0;
+ }
const void* blob = sqlite3_column_blob(stmt(stmtPtr), col);
if (blob == nullptr) {
throwIfError(env, stmtPtr);
@@ -281,22 +294,30 @@
}
static jdouble columnDouble(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
- throwIfInvalidColumn(env, stmtPtr, col);
+ if (throwIfInvalidColumn(env, stmtPtr, col)) {
+ return 0;
+ }
return sqlite3_column_double(stmt(stmtPtr), col);
}
static jint columnInt(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
- throwIfInvalidColumn(env, stmtPtr, col);
+ if (throwIfInvalidColumn(env, stmtPtr, col)) {
+ return 0;
+ }
return sqlite3_column_int(stmt(stmtPtr), col);
}
static jlong columnLong(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
- throwIfInvalidColumn(env, stmtPtr, col);
+ if (throwIfInvalidColumn(env, stmtPtr, col)) {
+ return 0;
+ }
return sqlite3_column_int64(stmt(stmtPtr), col);
}
static jstring columnText(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
- throwIfInvalidColumn(env, stmtPtr, col);
+ if (throwIfInvalidColumn(env, stmtPtr, col)) {
+ return nullptr;
+ }
const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(stmt(stmtPtr), col));
if (text == nullptr) {
throwIfError(env, stmtPtr);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7aeabee..117041a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8132,10 +8132,10 @@
<permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE"
android:protectionLevel="signature" />
- <!-- Allows low-level access to monitor keyboard system shortcuts
+ <!-- Allows low-level access to manage key gestures.
<p>Not for use by third-party applications.
@hide -->
- <permission android:name="android.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS"
+ <permission android:name="android.permission.MANAGE_KEY_GESTURES"
android:protectionLevel="signature" />
<uses-permission android:name="android.permission.HANDLE_QUERY_PACKAGE_RESTART" />
diff --git a/core/res/res/drawable-car/car_activity_resolver_list_background.xml b/core/res/res/drawable-car/car_activity_resolver_list_background.xml
deleted file mode 100644
index dbbadd8..0000000
--- a/core/res/res/drawable-car/car_activity_resolver_list_background.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="?attr/colorBackgroundFloating" />
- <corners android:radius="@dimen/car_activity_resolver_corner_radius" />
-</shape>
\ No newline at end of file
diff --git a/core/res/res/layout-car/car_resolver_list.xml b/core/res/res/layout-car/car_resolver_list.xml
deleted file mode 100644
index 08c9861..0000000
--- a/core/res/res/layout-car/car_resolver_list.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright 2019, 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.
-*/
--->
-<com.android.internal.widget.ResolverDrawerLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/car_activity_resolver_width"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:id="@id/contentPanel">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/car_activity_resolver_list_background">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/car_activity_resolver_list_background"
- android:orientation="horizontal"
- android:paddingVertical="@dimen/car_padding_4"
- android:paddingHorizontal="@dimen/car_padding_4" >
- <TextView
- android:id="@+id/profile_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start"
- android:textAppearance="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
- </LinearLayout>
-
- <FrameLayout
- android:id="@+id/stub"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-
- <TabHost
- android:id="@+id/profile_tabhost"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:background="?android:attr/colorBackgroundFloating">
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TabWidget
- android:id="@android:id/tabs"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- </TabWidget>
- <View
- android:id="@+id/resolver_tab_divider"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- <FrameLayout
- android:id="@android:id/tabcontent"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <com.android.internal.app.ResolverViewPager
- android:id="@+id/profile_pager"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </FrameLayout>
- </LinearLayout>
- </TabHost>
-
- <LinearLayout
- android:id="@+id/button_bar"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginVertical="@dimen/car_padding_4"
- android:layout_marginHorizontal="@dimen/car_padding_4"
- android:padding="0dp"
- android:gravity="center"
- android:background="@drawable/car_activity_resolver_list_background"
- android:orientation="vertical">
-
- <Button
- android:id="@+id/button_once"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_button_height"
- android:enabled="false"
- android:layout_gravity="center"
- android:layout_marginBottom="@dimen/car_padding_2"
- android:text="@string/activity_resolver_use_once"
- android:onClick="onButtonClick"/>
-
- <Button
- android:id="@+id/button_always"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_button_height"
- android:enabled="false"
- android:layout_gravity="center"
- android:text="@string/activity_resolver_use_always"
- android:onClick="onButtonClick"/>
- </LinearLayout>
-
- </LinearLayout>
-
-</com.android.internal.widget.ResolverDrawerLayout>
\ No newline at end of file
diff --git a/core/res/res/layout-car/car_resolver_list_with_default.xml b/core/res/res/layout-car/car_resolver_list_with_default.xml
deleted file mode 100644
index 08cc7ff..0000000
--- a/core/res/res/layout-car/car_resolver_list_with_default.xml
+++ /dev/null
@@ -1,159 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright 2019, 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.
-*/
--->
-<com.android.internal.widget.ResolverDrawerLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/car_activity_resolver_width"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:id="@id/contentPanel">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="center"
- android:background="@drawable/car_activity_resolver_list_background">
-
- <FrameLayout
- android:id="@+id/stub"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/car_activity_resolver_list_background"/>
-
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/car_activity_resolver_list_item_height"
- android:orientation="horizontal">
-
- <RadioButton
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="false"
- android:clickable="false"
- android:layout_marginStart="?attr/listPreferredItemPaddingStart"
- android:layout_gravity="start|center_vertical"
- android:checked="true"/>
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/car_icon_size"
- android:layout_height="@dimen/car_icon_size"
- android:layout_gravity="start|center_vertical"
- android:layout_marginStart="@dimen/car_padding_4"
- android:src="@drawable/resolver_icon_placeholder"
- android:scaleType="fitCenter"/>
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginHorizontal="?attr/listPreferredItemPaddingStart"
- style="?android:attr/textAppearanceListItem"
- android:layout_gravity="start|center_vertical" />
-
- <LinearLayout
- android:id="@+id/profile_button"
- android:visibility="gone"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <ImageView
- android:id="@+id/icon"
- android:visibility="gone"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <TextView
- android:id="@id/text1"
- android:visibility="gone"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </LinearLayout>
- </LinearLayout>
-
- <TabHost
- android:id="@+id/profile_tabhost"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:background="?attr/colorBackgroundFloating">
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TabWidget
- android:id="@android:id/tabs"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone">
- </TabWidget>
- <View
- android:id="@+id/resolver_tab_divider"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- <FrameLayout
- android:id="@android:id/tabcontent"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <com.android.internal.app.ResolverViewPager
- android:id="@+id/profile_pager"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- </com.android.internal.app.ResolverViewPager>
- </FrameLayout>
- </LinearLayout>
- </TabHost>
-
- <LinearLayout
- android:id="@+id/button_bar"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginVertical="@dimen/car_padding_4"
- android:layout_marginHorizontal="@dimen/car_padding_4"
- android:gravity="center"
- android:background="@drawable/car_activity_resolver_list_background"
- android:orientation="vertical">
-
- <Button
- android:id="@+id/button_once"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_button_height"
- android:enabled="false"
- android:layout_gravity="center"
- android:layout_marginBottom="@dimen/car_padding_2"
- android:text="@string/activity_resolver_use_once"
- android:onClick="onButtonClick"/>
-
- <Button
- android:id="@+id/button_always"
- android:layout_width="match_parent"
- android:layout_height="@dimen/car_button_height"
- android:enabled="false"
- android:layout_gravity="center"
- android:text="@string/activity_resolver_use_always"
- android:onClick="onButtonClick"/>
- </LinearLayout>
- </LinearLayout>
-
-</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
index 832ebe5..6dad3b7 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
@@ -999,4 +999,31 @@
mDatabase.endTransaction();
}
}
+
+ /**
+ * This test verifies that the JNI exception thrown because of a bad column is actually thrown
+ * and does not crash the VM.
+ */
+ @Test
+ public void testJniExceptions() {
+ // Create the t1 table.
+ mDatabase.beginTransaction();
+ try {
+ mDatabase.execSQL("CREATE TABLE t1 (i int, j int);");
+ mDatabase.setTransactionSuccessful();
+ } finally {
+ mDatabase.endTransaction();
+ }
+
+ mDatabase.beginTransactionReadOnly();
+ try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
+ s.step();
+ s.getColumnText(5); // out-of-range column
+ fail("JNI exception not thrown");
+ } catch (SQLiteBindOrColumnIndexOutOfRangeException e) {
+ // Passing case.
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java b/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java
new file mode 100644
index 0000000..8a54e5b
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.test.InstrumentationTestCase;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.text.flags.Flags;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * PaintTest tests {@link Paint}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PaintFontVariationTest extends InstrumentationTestCase {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
+ @Test
+ public void testDerivedFromSameTypeface() {
+ final Paint p = new Paint();
+
+ p.setTypeface(Typeface.SANS_SERIF);
+ assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
+ Typeface first = p.getTypeface();
+
+ p.setTypeface(Typeface.SANS_SERIF);
+ assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
+ Typeface second = p.getTypeface();
+
+ assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
+ @Test
+ public void testDerivedFromChained() {
+ final Paint p = new Paint();
+
+ p.setTypeface(Typeface.SANS_SERIF);
+ assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
+ Typeface first = p.getTypeface();
+
+ assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
+ Typeface second = p.getTypeface();
+
+ assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
+ }
+}
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index 0dec756..878ba70 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -16,13 +16,22 @@
package android.graphics;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertNotEquals;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.test.InstrumentationTestCase;
import android.text.TextUtils;
import androidx.test.filters.SmallTest;
+import com.android.text.flags.Flags;
+
+import org.junit.Rule;
+
import java.util.Arrays;
import java.util.HashSet;
@@ -30,6 +39,9 @@
* PaintTest tests {@link Paint}.
*/
public class PaintTest extends InstrumentationTestCase {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final String FONT_PATH = "fonts/HintedAdvanceWidthTest-Regular.ttf";
static void assertEquals(String message, float[] expected, float[] actual) {
@@ -403,4 +415,33 @@
assertEquals(6, getClusterCount(p, rtlStr + ltrStr));
assertEquals(9, getClusterCount(p, ltrStr + rtlStr + ltrStr));
}
+
+ @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
+ public void testDerivedFromSameTypeface() {
+ final Paint p = new Paint();
+
+ p.setTypeface(Typeface.SANS_SERIF);
+ assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
+ Typeface first = p.getTypeface();
+
+ p.setTypeface(Typeface.SANS_SERIF);
+ assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
+ Typeface second = p.getTypeface();
+
+ assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
+ public void testDerivedFromChained() {
+ final Paint p = new Paint();
+
+ p.setTypeface(Typeface.SANS_SERIF);
+ assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
+ Typeface first = p.getTypeface();
+
+ assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
+ Typeface second = p.getTypeface();
+
+ assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
+ }
}
diff --git a/core/tests/coretests/src/android/view/contentprotection/OWNERS b/core/tests/coretests/src/android/view/contentprotection/OWNERS
index b3583a7..3d09da3 100644
--- a/core/tests/coretests/src/android/view/contentprotection/OWNERS
+++ b/core/tests/coretests/src/android/view/contentprotection/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 544200
+# Bug component: 1040349
-include /core/java/android/view/contentcapture/OWNERS
+include /core/java/android/view/contentprotection/OWNERS
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index fd78816..889a7785 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -56,6 +56,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.text.flags.Flags;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -74,6 +75,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -143,6 +145,23 @@
private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
private static final Object sDynamicCacheLock = new Object();
+ private static final LruCache<Long, LruCache<String, Typeface>> sVariableCache =
+ new LruCache<>(16);
+ private static final Object sVariableCacheLock = new Object();
+
+ /** @hide */
+ @VisibleForTesting
+ public static void clearTypefaceCachesForTestingPurpose() {
+ synchronized (sWeightCacheLock) {
+ sWeightTypefaceCache.clear();
+ }
+ synchronized (sDynamicCacheLock) {
+ sDynamicTypefaceCache.evictAll();
+ }
+ synchronized (sVariableCacheLock) {
+ sVariableCache.evictAll();
+ }
+ }
@GuardedBy("SYSTEM_FONT_MAP_LOCK")
static Typeface sDefaultTypeface;
@@ -195,6 +214,8 @@
@UnsupportedAppUsage
public final long native_instance;
+ private final Typeface mDerivedFrom;
+
private final String mSystemFontFamilyName;
private final Runnable mCleaner;
@@ -274,6 +295,18 @@
}
/**
+ * Returns the Typeface used for creating this Typeface.
+ *
+ * Maybe null if this is not derived from other Typeface.
+ * TODO(b/357707916): Make this public API.
+ * @hide
+ */
+ @VisibleForTesting
+ public final @Nullable Typeface getDerivedFrom() {
+ return mDerivedFrom;
+ }
+
+ /**
* Returns the system font family name if the typeface was created from a system font family,
* otherwise returns null.
*/
@@ -1021,9 +1054,51 @@
return typeface;
}
- /** @hide */
+ private static String axesToVarKey(@NonNull List<FontVariationAxis> axes) {
+ // The given list can be mutated because it is allocated in Paint#setFontVariationSettings.
+ // Currently, Paint#setFontVariationSettings is the only code path reaches this method.
+ axes.sort(Comparator.comparingInt(FontVariationAxis::getOpenTypeTagValue));
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < axes.size(); ++i) {
+ final FontVariationAxis fva = axes.get(i);
+ sb.append(fva.getTag());
+ sb.append(fva.getStyleValue());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * TODO(b/357707916): Make this public API.
+ * @hide
+ */
public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
@NonNull List<FontVariationAxis> axes) {
+ if (Flags.typefaceCacheForVarSettings()) {
+ final Typeface target = (family == null) ? Typeface.DEFAULT : family;
+ final Typeface base = (target.mDerivedFrom == null) ? target : target.mDerivedFrom;
+
+ final String key = axesToVarKey(axes);
+
+ synchronized (sVariableCacheLock) {
+ LruCache<String, Typeface> innerCache = sVariableCache.get(base.native_instance);
+ if (innerCache == null) {
+ // Cache up to 16 var instance per root Typeface
+ innerCache = new LruCache<>(16);
+ sVariableCache.put(base.native_instance, innerCache);
+ } else {
+ Typeface cached = innerCache.get(key);
+ if (cached != null) {
+ return cached;
+ }
+ }
+ Typeface typeface = new Typeface(
+ nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
+ base.getSystemFontFamilyName(), base);
+ innerCache.put(key, typeface);
+ return typeface;
+ }
+ }
+
final Typeface base = family == null ? Typeface.DEFAULT : family;
Typeface typeface = new Typeface(
nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
@@ -1184,11 +1259,19 @@
// don't allow clients to call this directly
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Typeface(long ni) {
- this(ni, null);
+ this(ni, null, null);
+ }
+
+
+ // don't allow clients to call this directly
+ // This is kept for robolectric.
+ private Typeface(long ni, @Nullable String systemFontFamilyName) {
+ this(ni, systemFontFamilyName, null);
}
// don't allow clients to call this directly
- private Typeface(long ni, @Nullable String systemFontFamilyName) {
+ private Typeface(long ni, @Nullable String systemFontFamilyName,
+ @Nullable Typeface derivedFrom) {
if (ni == 0) {
throw new RuntimeException("native typeface cannot be made");
}
@@ -1198,6 +1281,7 @@
mStyle = nativeGetStyle(ni);
mWeight = nativeGetWeight(ni);
mSystemFontFamilyName = systemFontFamilyName;
+ mDerivedFrom = derivedFrom;
}
/**
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 56ea7c2..9de10c0 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -128,3 +128,13 @@
description: "Enable bubble bar to be shown in the persistent task bar"
bug: "346391377"
}
+
+flag {
+ name: "bubble_view_info_executors"
+ namespace: "multitasking"
+ description: "Use executors to inflate bubble views"
+ bug: "353894869"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 8627c0b..53ab2d5 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -539,20 +539,23 @@
<!-- The size of the icon shown in the resize veil. -->
<dimen name="desktop_mode_resize_veil_icon_size">96dp</dimen>
- <!-- The with of the border around the app task for edge resizing, when
+ <!-- The width of the border outside the app task eligible for edge resizing, when
enable_windowing_edge_drag_resize is enabled. -->
- <dimen name="desktop_mode_edge_handle">12dp</dimen>
+ <dimen name="freeform_edge_handle_outset">10dp</dimen>
+
+ <!-- The size of the border inside the app task eligible for edge resizing, when
+ enable_windowing_edge_drag_resize is enabled. -->
+ <dimen name="freeform_edge_handle_inset">2dp</dimen>
<!-- The original width of the border around the app task for edge resizing, when
enable_windowing_edge_drag_resize is disabled. -->
<dimen name="freeform_resize_handle">15dp</dimen>
<!-- The size of the corner region for drag resizing with touch, when a larger touch region is
- appropriate. Applied when enable_windowing_edge_drag_resize is enabled. -->
+ appropriate. -->
<dimen name="desktop_mode_corner_resize_large">48dp</dimen>
- <!-- The original size of the corner region for darg resizing, when
- enable_windowing_edge_drag_resize is disabled. -->
+ <!-- The size of the corner region for drag resizing with a cursor or a stylus. -->
<dimen name="freeform_resize_corner">44dp</dimen>
<!-- The thickness in dp for all desktop drag transition regions. -->
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index 282385a..341ca0e 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -24,6 +24,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.window.flags.Flags;
+import java.io.PrintWriter;
+
/**
* Constants for desktop mode feature
*/
@@ -203,4 +205,19 @@
private static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
return !enforceDeviceRestrictions() || isDesktopModeSupported(context);
}
+
+ /** Dumps DesktopModeStatus flags and configs. */
+ public static void dump(PrintWriter pw, String prefix, Context context) {
+ String innerPrefix = prefix + " ";
+ pw.print(prefix); pw.println(TAG);
+ pw.print(innerPrefix); pw.print("maxTaskLimit="); pw.println(getMaxTaskLimit(context));
+
+ pw.print(innerPrefix); pw.print("maxTaskLimit config override=");
+ pw.println(context.getResources().getInteger(
+ R.integer.config_maxDesktopWindowingActiveTasks));
+
+ SystemProperties.Handle maxTaskLimitHandle = SystemProperties.find(MAX_TASK_LIMIT_SYS_PROP);
+ pw.print(innerPrefix); pw.print("maxTaskLimit sysprop=");
+ pw.println(maxTaskLimitHandle == null ? "null" : maxTaskLimitHandle.getInt(/* def= */ -1));
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 1242287..a0a9451 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -146,7 +146,8 @@
private final Handler mBgHandler;
private final WindowManager mWindowManager;
private final Transitions mTransitions;
- private final BackTransitionHandler mBackTransitionHandler;
+ @VisibleForTesting
+ final BackTransitionHandler mBackTransitionHandler;
@VisibleForTesting
final Rect mTouchableArea = new Rect();
@@ -174,7 +175,8 @@
@Nullable
private IOnBackInvokedCallback mActiveCallback;
@Nullable
- private RemoteAnimationTarget[] mApps;
+ @VisibleForTesting
+ RemoteAnimationTarget[] mApps;
@VisibleForTesting
final RemoteCallback mNavigationObserver = new RemoteCallback(
@@ -1448,7 +1450,8 @@
* Check whether this transition is prepare for predictive back animation, which could
* happen when core make an activity become visible.
*/
- private boolean handlePrepareTransition(
+ @VisibleForTesting
+ boolean handlePrepareTransition(
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction st,
@NonNull SurfaceControl.Transaction ft,
@@ -1491,7 +1494,8 @@
* Check whether this transition is triggered from back gesture commitment.
* Reparent the transition targets to animation leashes, so the animation won't be broken.
*/
- private boolean handleCloseTransition(@NonNull TransitionInfo info,
+ @VisibleForTesting
+ boolean handleCloseTransition(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction st,
@NonNull SurfaceControl.Transaction ft,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index f54b44b..544c2dd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -1045,6 +1045,17 @@
wct.reorder(task.token, true)
return wct
}
+ // If task is already visible, it must have been handled already and added to desktop mode.
+ // Cascade task only if it's not visible yet.
+ if (DesktopModeFlags.CASCADING_WINDOWS.isEnabled(context)
+ && !taskRepository.isVisibleTask(task.taskId)) {
+ val displayLayout = displayController.getDisplayLayout(task.displayId)
+ if (displayLayout != null) {
+ val initialBounds = Rect(task.configuration.windowConfiguration.bounds)
+ cascadeWindow(task, initialBounds, displayLayout)
+ wct.setBounds(task.token, initialBounds)
+ }
+ }
if (useDesktopOverrideDensity()) {
wct.setDensityDpi(task.token, DESKTOP_DENSITY_OVERRIDE)
}
@@ -1097,16 +1108,21 @@
/** Handle task closing by removing wallpaper activity if it's the last active task */
private fun handleTaskClosing(task: RunningTaskInfo): WindowContainerTransaction? {
logV("handleTaskClosing")
+ if (!isDesktopModeShowing(task.displayId))
+ return null
+
val wct = WindowContainerTransaction()
if (taskRepository.isOnlyVisibleNonClosingTask(task.taskId)
- && taskRepository.wallpaperActivityToken != null) {
+ && taskRepository.wallpaperActivityToken != null
+ ) {
// Remove wallpaper activity when the last active task is removed
removeWallpaperActivity(wct)
}
taskRepository.addClosingTask(task.displayId, task.taskId)
// If a CLOSE or TO_BACK is triggered on a desktop task, remove the task.
if (DesktopModeFlags.BACK_NAVIGATION.isEnabled(context) &&
- taskRepository.isVisibleTask(task.taskId)) {
+ taskRepository.isVisibleTask(task.taskId)
+ ) {
wct.removeTask(task.token)
}
return if (wct.isEmpty) null else wct
@@ -1134,18 +1150,9 @@
}
if (DesktopModeFlags.CASCADING_WINDOWS.isEnabled(context)) {
- val stableBounds = Rect()
- displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
- val activeTasks = taskRepository
- .getActiveNonMinimizedOrderedTasks(taskInfo.displayId)
- activeTasks.firstOrNull()?.let { activeTask ->
- shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let {
- cascadeWindow(context.resources, stableBounds,
- it.configuration.windowConfiguration.bounds, initialBounds)
- }
- }
+ cascadeWindow(taskInfo, initialBounds, displayLayout)
}
+
if (canChangeTaskPosition(taskInfo)) {
wct.setBounds(taskInfo.token, initialBounds)
}
@@ -1180,6 +1187,19 @@
}
}
+ private fun cascadeWindow(task: TaskInfo, bounds: Rect, displayLayout: DisplayLayout) {
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(task.displayId)
+ activeTasks.firstOrNull()?.let { activeTask ->
+ shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let {
+ cascadeWindow(context.resources, stableBounds,
+ it.configuration.windowConfiguration.bounds, bounds)
+ }
+ }
+ }
+
/**
* Adds split screen changes to a transaction. Note that bounds are not reset here due to
* animation; see {@link onDesktopSplitSelectAnimComplete}
@@ -1513,6 +1533,7 @@
private fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopTasksController")
+ DesktopModeStatus.dump(pw, innerPrefix, context)
taskRepository.dump(pw, innerPrefix)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 37e2fd0..7ba6ec4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -290,6 +290,20 @@
// Entering PIP.
if (isEnteringPip(info)) {
+ if (!mPipTransitionState.isInPip() && TransitionUtil.hasDisplayChange(info)) {
+ final TransitionInfo.Change pipChange = getPipChange(info);
+ if (pipChange != null) {
+ // Clear old crop.
+ updatePipForUnhandledTransition(pipChange, startTransaction, finishTransaction);
+ }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: ignore exited PiP with display change", TAG);
+ // This should be an exited pip. E.g. a display change transition happens when
+ // the exiting pip is animating, then mergeAnimation -> end -> onFinishResize ->
+ // onExitPipFinished was called, i.e. pip state is UNDEFINED. So do not handle
+ // the incoming transition as entering pip.
+ return false;
+ }
if (handleEnteringPipWithDisplayChange(transition, info, startTransaction,
finishTransaction, finishCallback)) {
// The destination position is applied directly and let default transition handler
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 401b78d..39260f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -19,6 +19,7 @@
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeHandleEdgeInset;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -286,7 +287,8 @@
final Resources res = mResult.mRootView.getResources();
mDragResizeListener.setGeometry(new DragResizeWindowGeometry(0 /* taskCornerRadius */,
new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(mContext, res),
- getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop);
+ getResizeHandleEdgeInset(res), getFineResizeCornerSize(res),
+ getLargeResizeCornerSize(res)), touchSlop);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 538d0fb..b5f5bb9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -31,6 +31,7 @@
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeHandleEdgeInset;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -515,8 +516,8 @@
if (mDragResizeListener.setGeometry(
new DragResizeWindowGeometry(mRelayoutParams.mCornerRadius,
new Size(mResult.mWidth, mResult.mHeight),
- getResizeEdgeHandleSize(mContext, res), getFineResizeCornerSize(res),
- getLargeResizeCornerSize(res)), touchSlop)
+ getResizeEdgeHandleSize(mContext, res), getResizeHandleEdgeInset(res),
+ getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop)
|| !mTaskInfo.positionInParent.equals(mPositionInParent)) {
updateExclusionRegion();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
index 014d61d..fd7bed7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
@@ -44,27 +44,32 @@
final class DragResizeWindowGeometry {
private final int mTaskCornerRadius;
private final Size mTaskSize;
- // The size of the handle applied to the edges of the window, for the user to drag resize.
- private final int mResizeHandleThickness;
+ // The size of the handle outside the task window applied to the edges of the window, for the
+ // user to drag resize.
+ private final int mResizeHandleEdgeOutset;
+ // The size of the handle inside the task window applied to the edges of the window, for the
+ // user to drag resize.
+ private final int mResizeHandleEdgeInset;
// The task corners to permit drag resizing with a course input, such as touch.
-
private final @NonNull TaskCorners mLargeTaskCorners;
// The task corners to permit drag resizing with a fine input, such as stylus or cursor.
private final @NonNull TaskCorners mFineTaskCorners;
// The bounds for each edge drag region, which can resize the task in one direction.
- private final @NonNull TaskEdges mTaskEdges;
+ final @NonNull TaskEdges mTaskEdges;
DragResizeWindowGeometry(int taskCornerRadius, @NonNull Size taskSize,
- int resizeHandleThickness, int fineCornerSize, int largeCornerSize) {
+ int resizeHandleEdgeOutset, int resizeHandleEdgeInset, int fineCornerSize,
+ int largeCornerSize) {
mTaskCornerRadius = taskCornerRadius;
mTaskSize = taskSize;
- mResizeHandleThickness = resizeHandleThickness;
+ mResizeHandleEdgeOutset = resizeHandleEdgeOutset;
+ mResizeHandleEdgeInset = resizeHandleEdgeInset;
mLargeTaskCorners = new TaskCorners(mTaskSize, largeCornerSize);
mFineTaskCorners = new TaskCorners(mTaskSize, fineCornerSize);
// Save touch areas for each edge.
- mTaskEdges = new TaskEdges(mTaskSize, mResizeHandleThickness);
+ mTaskEdges = new TaskEdges(mTaskSize, mResizeHandleEdgeOutset, mResizeHandleEdgeInset);
}
/**
@@ -72,11 +77,18 @@
*/
static int getResizeEdgeHandleSize(@NonNull Context context, @NonNull Resources res) {
return EDGE_DRAG_RESIZE.isEnabled(context)
- ? res.getDimensionPixelSize(R.dimen.desktop_mode_edge_handle)
+ ? res.getDimensionPixelSize(R.dimen.freeform_edge_handle_outset)
: res.getDimensionPixelSize(R.dimen.freeform_resize_handle);
}
/**
+ * Returns the resource value to use for the edge resize handle inside the task bounds.
+ */
+ static int getResizeHandleEdgeInset(@NonNull Resources res) {
+ return res.getDimensionPixelSize(R.dimen.freeform_edge_handle_inset);
+ }
+
+ /**
* Returns the resource value to use for course input, such as touch, that benefits from a large
* square on each of the window's corners.
*/
@@ -95,7 +107,8 @@
/**
* Returns the size of the task this geometry is calculated for.
*/
- @NonNull Size getTaskSize() {
+ @NonNull
+ Size getTaskSize() {
// Safe to return directly since size is immutable.
return mTaskSize;
}
@@ -217,13 +230,15 @@
ctrlType |= CTRL_TYPE_BOTTOM;
}
// If the touch is within one of the four corners, check if it is within the bounds of the
- // // handle.
+ // handle.
if ((ctrlType & (CTRL_TYPE_LEFT | CTRL_TYPE_RIGHT)) != 0
&& (ctrlType & (CTRL_TYPE_TOP | CTRL_TYPE_BOTTOM)) != 0) {
return checkDistanceFromCenter(ctrlType, x, y);
}
- // Otherwise, we should make sure we don't resize tasks inside task bounds.
- return (x < 0 || y < 0 || x >= mTaskSize.getWidth() || y >= mTaskSize.getHeight())
+ // Allow a small resize handle inside the task bounds defined by the edge inset.
+ return (x <= mResizeHandleEdgeInset || y <= mResizeHandleEdgeInset
+ || x >= mTaskSize.getWidth() - mResizeHandleEdgeInset
+ || y >= mTaskSize.getHeight() - mResizeHandleEdgeInset)
? ctrlType : CTRL_TYPE_UNDEFINED;
}
@@ -237,7 +252,7 @@
final Point cornerRadiusCenter = calculateCenterForCornerRadius(ctrlType);
double distanceFromCenter = Math.hypot(x - cornerRadiusCenter.x, y - cornerRadiusCenter.y);
- if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness
+ if (distanceFromCenter < mTaskCornerRadius + mResizeHandleEdgeOutset
&& distanceFromCenter >= mTaskCornerRadius) {
return ctrlType;
}
@@ -288,7 +303,8 @@
return this.mTaskCornerRadius == other.mTaskCornerRadius
&& this.mTaskSize.equals(other.mTaskSize)
- && this.mResizeHandleThickness == other.mResizeHandleThickness
+ && this.mResizeHandleEdgeOutset == other.mResizeHandleEdgeOutset
+ && this.mResizeHandleEdgeInset == other.mResizeHandleEdgeInset
&& this.mFineTaskCorners.equals(other.mFineTaskCorners)
&& this.mLargeTaskCorners.equals(other.mLargeTaskCorners)
&& this.mTaskEdges.equals(other.mTaskEdges);
@@ -299,7 +315,8 @@
return Objects.hash(
mTaskCornerRadius,
mTaskSize,
- mResizeHandleThickness,
+ mResizeHandleEdgeOutset,
+ mResizeHandleEdgeInset,
mFineTaskCorners,
mLargeTaskCorners,
mTaskEdges);
@@ -421,26 +438,27 @@
private final @NonNull Rect mBottomEdgeBounds;
private final @NonNull Region mRegion;
- private TaskEdges(@NonNull Size taskSize, int resizeHandleThickness) {
+ private TaskEdges(@NonNull Size taskSize, int resizeHandleThickness,
+ int resizeHandleEdgeInset) {
// Save touch areas for each edge.
mTopEdgeBounds = new Rect(
-resizeHandleThickness,
-resizeHandleThickness,
taskSize.getWidth() + resizeHandleThickness,
- 0);
+ resizeHandleThickness);
mLeftEdgeBounds = new Rect(
-resizeHandleThickness,
0,
- 0,
+ resizeHandleEdgeInset,
taskSize.getHeight());
mRightEdgeBounds = new Rect(
- taskSize.getWidth(),
+ taskSize.getWidth() - resizeHandleEdgeInset,
0,
taskSize.getWidth() + resizeHandleThickness,
taskSize.getHeight());
mBottomEdgeBounds = new Rect(
-resizeHandleThickness,
- taskSize.getHeight(),
+ taskSize.getHeight() - resizeHandleEdgeInset,
taskSize.getWidth() + resizeHandleThickness,
taskSize.getHeight() + resizeHandleThickness);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 56fad95..311b1c5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -16,8 +16,19 @@
package com.android.wm.shell.back;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.window.BackNavigationInfo.KEY_NAVIGATION_FINISHED;
+import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -25,6 +36,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -32,6 +44,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.app.ActivityManager;
import android.app.IActivityTaskManager;
import android.app.WindowConfiguration;
import android.content.pm.ApplicationInfo;
@@ -40,6 +53,7 @@
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
@@ -51,11 +65,16 @@
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.window.BackEvent;
import android.window.BackMotionEvent;
import android.window.BackNavigationInfo;
import android.window.IBackAnimationFinishedCallback;
import android.window.IOnBackInvokedCallback;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -128,6 +147,8 @@
private ShellBackAnimationRegistry mShellBackAnimationRegistry;
private Rect mTouchableRegion;
+ private BackAnimationController.BackTransitionHandler mBackTransitionHandler;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -165,6 +186,8 @@
mShellExecutor.flushAll();
mTouchableRegion = new Rect(0, 0, 100, 100);
mController.mTouchableArea.set(mTouchableRegion);
+ mBackTransitionHandler = mController.mBackTransitionHandler;
+ spyOn(mBackTransitionHandler);
}
private void createNavigationInfo(int backType,
@@ -606,6 +629,198 @@
mCrossTaskBackAnimation.getRunner());
}
+ @Test
+ public void testCloseAsExpectTransition() {
+ final int openTaskId = 1;
+ final int closeTaskId = 2;
+ mController.mApps = createAppAnimationTargets(openTaskId, closeTaskId);
+ final IBinder mockBinder = mock(IBinder.class);
+ final SurfaceControl.Transaction st = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction ft = mock(SurfaceControl.Transaction.class);
+ // Single close
+ final TransitionInfo.Change open = createAppChange(openTaskId, TRANSIT_OPEN,
+ FLAG_BACK_GESTURE_ANIMATED | FLAG_MOVED_TO_TOP);
+ final TransitionInfo.Change close = createAppChange(closeTaskId, TRANSIT_CLOSE,
+ FLAG_BACK_GESTURE_ANIMATED);
+
+ TransitionInfo tInfo = createTransitionInfo(TRANSIT_CLOSE, open, close);
+ mBackTransitionHandler.mCloseTransitionRequested = true;
+ Transitions.TransitionFinishCallback callback =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+ verify(mBackTransitionHandler).handleCloseTransition(
+ eq(tInfo), eq(st), eq(ft), eq(callback));
+ mBackTransitionHandler.onAnimationFinished();
+ verify(callback).onTransitionFinished(any());
+ mBackTransitionHandler.mCloseTransitionRequested = false;
+
+ // PREPARE + CLOSE
+ tInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open);
+ callback = mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+ verify(mBackTransitionHandler).handlePrepareTransition(
+ eq(tInfo), eq(st), eq(ft), eq(callback));
+ mBackTransitionHandler.mCloseTransitionRequested = true;
+ TransitionInfo tInfo2 = createTransitionInfo(TRANSIT_CLOSE, close);
+ Transitions.TransitionFinishCallback mergeCallback =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.mergeAnimation(
+ mock(IBinder.class), tInfo2, st, mock(IBinder.class), mergeCallback);
+ mBackTransitionHandler.onAnimationFinished();
+ verify(callback).onTransitionFinished(any());
+ verify(mergeCallback).onTransitionFinished(any());
+ mBackTransitionHandler.mCloseTransitionRequested = false;
+
+ // PREPARE contains close info
+ tInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open, close);
+ callback = mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.mCloseTransitionRequested = true;
+ mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+ verify(mBackTransitionHandler).handleCloseTransition(
+ eq(tInfo), eq(st), eq(ft), eq(callback));
+ mBackTransitionHandler.onAnimationFinished();
+ verify(callback).onTransitionFinished(any());
+ mBackTransitionHandler.mCloseTransitionRequested = false;
+
+ // PREPARE then Cancel
+ tInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open);
+ callback = mock(Transitions.TransitionFinishCallback.class);
+ final TransitionRequestInfo requestInfo = new TransitionRequestInfo(
+ TRANSIT_PREPARE_BACK_NAVIGATION, null /* triggerTask */,
+ null /* remoteTransition */);
+ mBackTransitionHandler.handleRequest(mockBinder, requestInfo);
+ mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+ verify(mBackTransitionHandler).handlePrepareTransition(
+ eq(tInfo), eq(st), eq(ft), eq(callback));
+ final TransitionInfo.Change openToClose = createAppChange(openTaskId, TRANSIT_CLOSE,
+ FLAG_BACK_GESTURE_ANIMATED);
+ tInfo2 = createTransitionInfo(TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION, openToClose);
+ mBackTransitionHandler.mClosePrepareTransition = mock(IBinder.class);
+ mergeCallback = mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.mergeAnimation(mBackTransitionHandler.mClosePrepareTransition,
+ tInfo2, st, mock(IBinder.class), mergeCallback);
+ assertTrue("Change should be consumed", tInfo2.getChanges().isEmpty());
+ mBackTransitionHandler.onAnimationFinished();
+ verify(callback).onTransitionFinished(any());
+ }
+
+ @Test
+ public void testCancelUnexpectedTransition() {
+ final int openTaskId = 1;
+ final int closeTaskId = 2;
+ mController.mApps = createAppAnimationTargets(openTaskId, closeTaskId);
+ final IBinder mockBinder = mock(IBinder.class);
+ final SurfaceControl.Transaction st = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction ft = mock(SurfaceControl.Transaction.class);
+ final TransitionInfo.Change open = createAppChange(openTaskId, TRANSIT_OPEN,
+ FLAG_BACK_GESTURE_ANIMATED | FLAG_MOVED_TO_TOP);
+ final TransitionInfo.Change close = createAppChange(closeTaskId, TRANSIT_CLOSE,
+ FLAG_BACK_GESTURE_ANIMATED);
+
+ // Didn't trigger close transition
+ mBackTransitionHandler.mCloseTransitionRequested = false;
+ TransitionInfo prepareInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION,
+ open, close);
+ final Transitions.TransitionFinishCallback callback =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.handleRequest(mockBinder, mock(TransitionRequestInfo.class));
+ boolean canHandle = mBackTransitionHandler.startAnimation(
+ mockBinder, prepareInfo, st, ft, callback);
+ assertFalse("Should not handle transition", canHandle);
+ assertNull(mBackTransitionHandler.mOnAnimationFinishCallback);
+
+ // Didn't trigger close transition, but receive close target.
+ final TransitionRequestInfo requestInfo = new TransitionRequestInfo(
+ TRANSIT_PREPARE_BACK_NAVIGATION, null /* triggerTask */,
+ null /* remoteTransition */);
+ prepareInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open);
+ final Transitions.TransitionFinishCallback callback2 =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.handleRequest(mockBinder, requestInfo);
+ canHandle = mBackTransitionHandler.startAnimation(mockBinder,
+ prepareInfo, st, ft, callback2);
+ assertTrue("Handle prepare transition" , canHandle);
+ verify(mBackTransitionHandler).handlePrepareTransition(
+ eq(prepareInfo), eq(st), eq(ft), eq(callback2));
+ final TransitionInfo closeInfo = createTransitionInfo(TRANSIT_CLOSE, close);
+ Transitions.TransitionFinishCallback mergeCallback =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.mergeAnimation(mock(IBinder.class), closeInfo, ft,
+ mock(IBinder.class), mergeCallback);
+ verify(callback2).onTransitionFinished(any());
+ verify(mergeCallback, never()).onTransitionFinished(any());
+
+ // Didn't trigger close transition, but contains open target.
+ final int openTaskId2 = 3;
+ final Transitions.TransitionFinishCallback callback3 =
+ mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.handleRequest(mockBinder, requestInfo);
+ canHandle = mBackTransitionHandler.startAnimation(
+ mockBinder, prepareInfo, st, ft, callback3);
+ assertTrue("Handle prepare transition" , canHandle);
+ verify(mBackTransitionHandler).handlePrepareTransition(
+ eq(prepareInfo), eq(st), eq(ft), eq(callback3));
+ final TransitionInfo.Change open2 = createAppChange(
+ openTaskId2, TRANSIT_OPEN, FLAG_MOVED_TO_TOP);
+ final TransitionInfo openInfo = createTransitionInfo(TRANSIT_OPEN, open2, close);
+ mergeCallback = mock(Transitions.TransitionFinishCallback.class);
+ mBackTransitionHandler.mergeAnimation(mock(IBinder.class), openInfo, ft,
+ mock(IBinder.class), mergeCallback);
+ verify(callback3).onTransitionFinished(any());
+ verify(mergeCallback, never()).onTransitionFinished(any());
+ }
+
+ private RemoteAnimationTarget[] createAppAnimationTargets(int openTaskId, int closeTaskId) {
+ final RemoteAnimationTarget openT = createSingleAnimationTarget(openTaskId,
+ RemoteAnimationTarget.MODE_OPENING);
+ final RemoteAnimationTarget closeT = createSingleAnimationTarget(closeTaskId,
+ RemoteAnimationTarget.MODE_CLOSING);
+ return new RemoteAnimationTarget[]{openT, closeT};
+ }
+
+ private RemoteAnimationTarget createSingleAnimationTarget(int taskId, int mode) {
+ final Rect fakeR = new Rect();
+ final Point fakeP = new Point();
+ final ActivityManager.RunningTaskInfo openTaskInfo = new ActivityManager.RunningTaskInfo();
+ openTaskInfo.taskId = taskId;
+ openTaskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+ return new RemoteAnimationTarget(
+ taskId, mode, mock(SurfaceControl.class), false, fakeR, fakeR,
+ 0, fakeP, fakeR, fakeR, new WindowConfiguration(), false,
+ mock(SurfaceControl.class), fakeR, openTaskInfo, false);
+ }
+ private TransitionInfo.Change createAppChange(
+ int taskId, @TransitionInfo.TransitionMode int mode,
+ @TransitionInfo.ChangeFlags int flags) {
+ final TransitionInfo.Change change;
+ SurfaceControl.Builder b = new SurfaceControl.Builder()
+ .setName("test task");
+ if (taskId != INVALID_TASK_ID) {
+ final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+ change = new TransitionInfo.Change(
+ taskInfo.token, b.build());
+ change.setTaskInfo(taskInfo);
+ } else {
+ change = new TransitionInfo.Change(
+ null, b.build());
+
+ }
+ change.setMode(mode);
+ change.setFlags(flags);
+ return change;
+ }
+
+ private static TransitionInfo createTransitionInfo(
+ @WindowManager.TransitionType int type, TransitionInfo.Change ... changes) {
+ final TransitionInfo info = new TransitionInfo(type, 0);
+ for (int i = 0; i < changes.length; ++i) {
+ info.addChange(changes[i]);
+ }
+ return info;
+ }
+
private void verifySystemBackBehavior(int type, BackAnimationRunner animation)
throws RemoteException {
final BackAnimationRunner animationRunner = spy(animation);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 7bb5449..a630cef 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -641,6 +641,41 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun handleRequest_newFreeformTaskLaunch_cascadeApplied() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+ val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS, active = false)
+
+ val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+ assertNotNull(wct, "should handle request")
+ val finalBounds = findBoundsChange(wct, freeformTask)
+ assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+ .isEqualTo(DesktopTaskPosition.BottomRight)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+ fun handleRequest_freeformTaskAlreadyExistsInDesktopMode_cascadeNotApplied() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+ setUpLandscapeDisplay()
+ val stableBounds = Rect()
+ displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+ setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+ val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+
+ val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+ assertNull(wct, "should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
fun addMoveToDesktopChanges_positionBottomRight() {
setUpLandscapeDisplay()
val stableBounds = Rect()
@@ -1784,6 +1819,19 @@
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ )
+ fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_notInDesktop_doesNotHandle() {
+ val task = setUpFreeformTask()
+ markTaskHidden(task)
+
+ val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun handleRequest_backTransition_singleTaskNoToken_noBackNav_doesNotHandle() {
@@ -2816,14 +2864,17 @@
private fun setUpFreeformTask(
displayId: Int = DEFAULT_DISPLAY,
- bounds: Rect? = null
+ bounds: Rect? = null,
+ active: Boolean = true
): RunningTaskInfo {
val task = createFreeformTask(displayId, bounds)
val activityInfo = ActivityInfo()
task.topActivityInfo = activityInfo
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
- taskRepository.addActiveTask(displayId, task.taskId)
- taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true)
+ if (active) {
+ taskRepository.addActiveTask(displayId, task.taskId)
+ taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true)
+ }
taskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
runningTasks.add(task)
return task
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
index d8f395d..1691f07 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
@@ -45,6 +45,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Tests for {@link DragResizeWindowGeometry}.
*
@@ -57,11 +60,12 @@
private static final Size TASK_SIZE = new Size(500, 1000);
private static final int TASK_CORNER_RADIUS = 10;
private static final int EDGE_RESIZE_THICKNESS = 12;
+ private static final int EDGE_RESIZE_HANDLE_INSET = 4;
private static final int FINE_CORNER_SIZE = EDGE_RESIZE_THICKNESS * 2 + 10;
private static final int LARGE_CORNER_SIZE = FINE_CORNER_SIZE + 10;
private static final DragResizeWindowGeometry GEOMETRY = new DragResizeWindowGeometry(
- TASK_CORNER_RADIUS, TASK_SIZE, EDGE_RESIZE_THICKNESS, FINE_CORNER_SIZE,
- LARGE_CORNER_SIZE);
+ TASK_CORNER_RADIUS, TASK_SIZE, EDGE_RESIZE_THICKNESS, EDGE_RESIZE_HANDLE_INSET,
+ FINE_CORNER_SIZE, LARGE_CORNER_SIZE);
// Points in the edge resize handle. Note that coordinates start from the top left.
private static final Point TOP_EDGE_POINT = new Point(TASK_SIZE.getWidth() / 2,
-EDGE_RESIZE_THICKNESS / 2);
@@ -71,6 +75,16 @@
TASK_SIZE.getWidth() + EDGE_RESIZE_THICKNESS / 2, TASK_SIZE.getHeight() / 2);
private static final Point BOTTOM_EDGE_POINT = new Point(TASK_SIZE.getWidth() / 2,
TASK_SIZE.getHeight() + EDGE_RESIZE_THICKNESS / 2);
+ // Points in the inset of the task bounds still within the edge resize handle.
+ // Note that coordinates start from the top left.
+ private static final Point TOP_INSET_POINT = new Point(TASK_SIZE.getWidth() / 2,
+ EDGE_RESIZE_HANDLE_INSET / 2);
+ private static final Point LEFT_INSET_POINT = new Point(EDGE_RESIZE_HANDLE_INSET / 2,
+ TASK_SIZE.getHeight() / 2);
+ private static final Point RIGHT_INSET_POINT = new Point(
+ TASK_SIZE.getWidth() - EDGE_RESIZE_HANDLE_INSET / 2, TASK_SIZE.getHeight() / 2);
+ private static final Point BOTTOM_INSET_POINT = new Point(TASK_SIZE.getWidth() / 2,
+ TASK_SIZE.getHeight() - EDGE_RESIZE_HANDLE_INSET / 2);
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -85,18 +99,23 @@
.addEqualityGroup(
GEOMETRY,
new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
- EDGE_RESIZE_THICKNESS, FINE_CORNER_SIZE, LARGE_CORNER_SIZE))
+ EDGE_RESIZE_THICKNESS, EDGE_RESIZE_HANDLE_INSET, FINE_CORNER_SIZE,
+ LARGE_CORNER_SIZE))
.addEqualityGroup(
new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
- EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE, LARGE_CORNER_SIZE),
+ EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
+ FINE_CORNER_SIZE, LARGE_CORNER_SIZE),
new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
- EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE, LARGE_CORNER_SIZE))
+ EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
+ FINE_CORNER_SIZE, LARGE_CORNER_SIZE))
.addEqualityGroup(
new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
- EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE,
+ EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
+ FINE_CORNER_SIZE,
LARGE_CORNER_SIZE + 5),
new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
- EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE,
+ EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
+ FINE_CORNER_SIZE,
LARGE_CORNER_SIZE + 5))
.testEquals();
}
@@ -127,7 +146,7 @@
assertThat(region.contains(point.x - EDGE_RESIZE_THICKNESS, point.y)).isTrue();
// Vertically along the edge is not contained.
assertThat(region.contains(point.x, point.y - EDGE_RESIZE_THICKNESS)).isFalse();
- assertThat(region.contains(point.x, point.y + EDGE_RESIZE_THICKNESS)).isFalse();
+ assertThat(region.contains(point.x, point.y + EDGE_RESIZE_THICKNESS + 10)).isFalse();
}
private static void verifyVerticalEdge(@NonNull Region region, @NonNull Point point) {
@@ -188,18 +207,18 @@
}
private void validateCtrlTypeForEdges(boolean isTouchscreen, boolean isEdgeResizePermitted) {
- assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
- LEFT_EDGE_POINT.x, LEFT_EDGE_POINT.y)).isEqualTo(
- isEdgeResizePermitted ? CTRL_TYPE_LEFT : CTRL_TYPE_UNDEFINED);
- assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
- TOP_EDGE_POINT.x, TOP_EDGE_POINT.y)).isEqualTo(
- isEdgeResizePermitted ? CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
- assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
- RIGHT_EDGE_POINT.x, RIGHT_EDGE_POINT.y)).isEqualTo(
- isEdgeResizePermitted ? CTRL_TYPE_RIGHT : CTRL_TYPE_UNDEFINED);
- assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
- BOTTOM_EDGE_POINT.x, BOTTOM_EDGE_POINT.y)).isEqualTo(
- isEdgeResizePermitted ? CTRL_TYPE_BOTTOM : CTRL_TYPE_UNDEFINED);
+ List<Point> points = Arrays.asList(LEFT_EDGE_POINT, TOP_EDGE_POINT, RIGHT_EDGE_POINT,
+ BOTTOM_EDGE_POINT, LEFT_INSET_POINT, TOP_INSET_POINT, RIGHT_INSET_POINT,
+ BOTTOM_INSET_POINT);
+ List<Integer> expectedCtrlType = Arrays.asList(CTRL_TYPE_LEFT, CTRL_TYPE_TOP,
+ CTRL_TYPE_RIGHT, CTRL_TYPE_BOTTOM, CTRL_TYPE_LEFT, CTRL_TYPE_TOP, CTRL_TYPE_RIGHT,
+ CTRL_TYPE_BOTTOM);
+
+ for (int i = 0; i < points.size(); i++) {
+ assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+ points.get(i).x, points.get(i).y)).isEqualTo(
+ isEdgeResizePermitted ? expectedCtrlType.get(i) : CTRL_TYPE_UNDEFINED);
+ }
}
@Test
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 3375e18c..ae63e19 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -57,6 +57,7 @@
@FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension {
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference();
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List<java.lang.String> getActiveNfceeList();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback);
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState();
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index 90ca92f..6c0f933 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -88,7 +88,7 @@
boolean isReaderOptionEnabled();
boolean isReaderOptionSupported();
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
- boolean enableReaderOption(boolean enable);
+ boolean enableReaderOption(boolean enable, in String pkg);
boolean isObserveModeSupported();
boolean isObserveModeEnabled();
boolean setObserveMode(boolean enabled, String pkg);
@@ -113,4 +113,5 @@
void clearPreference();
void setScreenState();
void checkFirmware();
+ List<String> fetchActiveNfceeList();
}
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index b36b705..525e2c5 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -2011,7 +2011,8 @@
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
- return callServiceReturn(() -> sService.enableReaderOption(enable), false);
+ return callServiceReturn(() ->
+ sService.enableReaderOption(enable, mContext.getPackageName()), false);
}
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 2ec819c..204ba9f 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -26,6 +26,8 @@
import android.os.RemoteException;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -153,6 +155,19 @@
NfcAdapter.callService(() -> NfcAdapter.sService.checkFirmware());
}
+ /**
+ * Get the Active NFCEE (NFC Execution Environment) List
+ *
+ * @return List of activated secure elements on success
+ * which can contain "eSE" and "UICC", otherwise empty list.
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ public List<String> getActiveNfceeList() {
+ return NfcAdapter.callServiceReturn(() ->
+ NfcAdapter.sService.fetchActiveNfceeList(), new ArrayList<String>());
+ }
+
private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
@Override
public void onTagConnected(boolean connected, Tag tag) throws RemoteException {
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 0d124e8..2b3862f 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -119,3 +119,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "audio_sharing_hysteresis_mode_fix"
+ namespace: "cross_device_experiences"
+ description: "Gates whether to enable fix for hysteresis mode"
+ bug: "355222285"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
index 2f4b2ef..4b141e7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
@@ -220,6 +220,16 @@
return getTriggerDescription();
}
+ /**
+ * Returns an icon "key" that is guaranteed to be different if the icon is different. Note that
+ * the inverse is not true, i.e. two keys can be different and the icon still be visually the
+ * same.
+ */
+ @NonNull
+ public String getIconKey() {
+ return mRule.getType() + ":" + mRule.getPackageName() + ":" + mRule.getIconResId();
+ }
+
@NonNull
public ListenableFuture<Drawable> getIcon(@NonNull Context context,
@NonNull ZenIconLoader iconLoader) {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
index c881e07..c60eb61 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
@@ -36,7 +36,7 @@
"androidx.coordinatorlayout_coordinatorlayout",
"androidx.core_core",
"androidx.preference_preference",
- "androidx.viewpager_viewpager",
+ "androidx.viewpager2_viewpager2",
"com_android_systemui_flags_lib",
"SettingsLibDisplayUtils",
"SettingsLibSettingsTheme",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
index 462c90b..e1b6e63 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
@@ -22,7 +22,7 @@
android:orientation="horizontal">
<ImageButton
- android:id="@+id/menu_prev_button"
+ android:id="@+id/menu_left_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
@@ -38,7 +38,7 @@
android:background="?android:attr/listDivider"/>
<ImageButton
- android:id="@+id/menu_next_button"
+ android:id="@+id/menu_right_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml
index c198443..c0aa1b3 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml
@@ -2,7 +2,7 @@
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridview"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:horizontalSpacing="@dimen/a11ymenu_grid_layout_margin"
android:listSelector="@android:color/transparent"
android:numColumns="3"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml
index 28a633e..6be7655 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml
@@ -6,18 +6,16 @@
android:background="@drawable/view_background"
>
<LinearLayout
- android:layout_width="@dimen/row_width"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
- <androidx.viewpager.widget.ViewPager
+ <androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/table_margin_top"
android:paddingBottom="@dimen/a11ymenu_layout_margin"
- android:paddingLeft="@dimen/a11ymenu_layout_margin"
- android:paddingRight="@dimen/a11ymenu_layout_margin"
android:layout_gravity="center"
android:gravity="center"
/>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java
index 20c63df..78fbf01 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java
@@ -16,7 +16,11 @@
package com.android.systemui.accessibility.accessibilitymenu.view;
+import static android.view.View.LAYOUT_DIRECTION_LTR;
+
+import android.content.res.Configuration;
import android.graphics.Rect;
+import android.text.TextUtils;
import android.view.TouchDelegate;
import android.view.View;
import android.view.View.OnClickListener;
@@ -37,46 +41,61 @@
public interface A11yMenuFooterCallBack {
/** Calls back when user clicks the left button. */
- void onLeftButtonClicked();
+ void onNextButtonClicked();
/** Calls back when user clicks the right button. */
- void onRightButtonClicked();
+ void onPreviousButtonClicked();
}
private final FooterButtonClickListener mFooterButtonClickListener;
- private ImageButton mPreviousPageBtn;
- private ImageButton mNextPageBtn;
+ private ImageButton mPageLeftBtn;
+ private ImageButton mPageRightBtn;
private View mTopListDivider;
private View mBottomListDivider;
private final A11yMenuFooterCallBack mCallBack;
+ private final ViewGroup mMenuLayout;
+ private int mRightToLeftDirection = LAYOUT_DIRECTION_LTR;
public A11yMenuFooter(ViewGroup menuLayout, A11yMenuFooterCallBack callBack) {
this.mCallBack = callBack;
mFooterButtonClickListener = new FooterButtonClickListener();
configureFooterLayout(menuLayout);
+ mMenuLayout = menuLayout;
}
public @Nullable ImageButton getPreviousPageBtn() {
- return mPreviousPageBtn;
+ return mRightToLeftDirection == LAYOUT_DIRECTION_LTR
+ ? mPageLeftBtn : mPageRightBtn;
}
public @Nullable ImageButton getNextPageBtn() {
- return mNextPageBtn;
+ return mRightToLeftDirection == LAYOUT_DIRECTION_LTR
+ ? mPageRightBtn : mPageLeftBtn;
+ }
+
+ /** Sets right to left direction of footer. */
+ public void updateRightToLeftDirection(Configuration configuration) {
+ mRightToLeftDirection = TextUtils.getLayoutDirectionFromLocale(
+ configuration.getLocales().get(0));
+ getPreviousPageBtn().setContentDescription(mMenuLayout.getResources().getString(
+ R.string.previous_button_content_description));
+ getNextPageBtn().setContentDescription(mMenuLayout.getResources().getString(
+ R.string.next_button_content_description));
}
private void configureFooterLayout(ViewGroup menuLayout) {
ViewGroup footerContainer = menuLayout.findViewById(R.id.footerlayout);
footerContainer.setVisibility(View.VISIBLE);
- mPreviousPageBtn = menuLayout.findViewById(R.id.menu_prev_button);
- mNextPageBtn = menuLayout.findViewById(R.id.menu_next_button);
+ mPageLeftBtn = menuLayout.findViewById(R.id.menu_left_button);
+ mPageRightBtn = menuLayout.findViewById(R.id.menu_right_button);
mTopListDivider = menuLayout.findViewById(R.id.top_listDivider);
mBottomListDivider = menuLayout.findViewById(R.id.bottom_listDivider);
// Registers listeners for footer buttons.
- setListener(mPreviousPageBtn);
- setListener(mNextPageBtn);
+ setListener(mPageLeftBtn);
+ setListener(mPageRightBtn);
menuLayout
.getViewTreeObserver()
@@ -85,8 +104,8 @@
@Override
public void onGlobalLayout() {
menuLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- expandBtnTouchArea(mPreviousPageBtn, menuLayout);
- expandBtnTouchArea(mNextPageBtn, (View) mNextPageBtn.getParent());
+ expandBtnTouchArea(mPageLeftBtn, menuLayout);
+ expandBtnTouchArea(mPageRightBtn, (View) mPageRightBtn.getParent());
}
});
}
@@ -115,10 +134,10 @@
private class FooterButtonClickListener implements OnClickListener {
@Override
public void onClick(View view) {
- if (view.getId() == R.id.menu_prev_button) {
- mCallBack.onLeftButtonClicked();
- } else if (view.getId() == R.id.menu_next_button) {
- mCallBack.onRightButtonClicked();
+ if (view.getId() == getPreviousPageBtn().getId()) {
+ mCallBack.onPreviousButtonClicked();
+ } else if (view.getId() == getNextPageBtn().getId()) {
+ mCallBack.onNextButtonClicked();
}
}
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index 6bea30f..de3c472 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -145,13 +145,14 @@
final Display display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
final Context context = mService.createDisplayContext(display).createWindowContext(
TYPE_ACCESSIBILITY_OVERLAY, null);
- mLayout = new FrameLayout(context);
+ mLayout = new A11yMenuFrameLayout(context);
updateLayoutPosition();
inflateLayoutAndSetOnTouchListener(mLayout, context);
mA11yMenuViewPager = new A11yMenuViewPager(mService, context);
mA11yMenuViewPager.configureViewPagerAndFooter(mLayout, createShortcutList(), pageIndex);
mWindowManager.addView(mLayout, mLayoutParameter);
mLayout.setVisibility(lastVisibilityState);
+ mA11yMenuViewPager.updateFooterState();
return mLayout;
}
@@ -393,4 +394,16 @@
}
}), timeoutDurationMs);
}
+
+ private class A11yMenuFrameLayout extends FrameLayout {
+ A11yMenuFrameLayout(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ public void dispatchConfigurationChanged(Configuration newConfig) {
+ super.dispatchConfigurationChanged(newConfig);
+ mA11yMenuViewPager.mA11yMenuFooter.updateRightToLeftDirection(newConfig);
+ }
+ }
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
index b969017..08bbf19 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
@@ -20,7 +20,6 @@
import android.content.res.Configuration;
import android.graphics.Insets;
import android.util.DisplayMetrics;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -29,7 +28,7 @@
import android.view.WindowMetrics;
import android.widget.GridView;
-import androidx.viewpager.widget.ViewPager;
+import androidx.viewpager2.widget.ViewPager2;
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
import com.android.systemui.accessibility.accessibilitymenu.R;
@@ -133,9 +132,9 @@
* The pager widget, which handles animation and allows swiping horizontally to access previous
* and next gridView pages.
*/
- protected ViewPager mViewPager;
+ protected ViewPager2 mViewPager;
- private ViewPagerAdapter<GridView> mViewPagerAdapter;
+ private ViewPagerAdapter mViewPagerAdapter;
private final List<GridView> mGridPageList = new ArrayList<>();
/** The footer, which provides buttons to switch between pages */
@@ -169,6 +168,8 @@
initViewPager();
initChildPage();
mA11yMenuFooter = new A11yMenuFooter(a11yMenuLayout, mFooterCallbacks);
+ mA11yMenuFooter.updateRightToLeftDirection(
+ a11yMenuLayout.getResources().getConfiguration());
updateFooterState();
registerOnGlobalLayoutListener();
goToPage(pageIndex);
@@ -177,18 +178,12 @@
/** Initializes viewPager and its adapter. */
private void initViewPager() {
mViewPager = mA11yMenuLayout.findViewById(R.id.view_pager);
- mViewPagerAdapter = new ViewPagerAdapter<>();
+ mViewPagerAdapter = new ViewPagerAdapter(mService);
+ mViewPager.setOffscreenPageLimit(2);
mViewPager.setAdapter(mViewPagerAdapter);
mViewPager.setOverScrollMode(View.OVER_SCROLL_NEVER);
- mViewPager.addOnPageChangeListener(
- new ViewPager.OnPageChangeListener() {
- @Override
- public void onPageScrollStateChanged(int state) {}
-
- @Override
- public void onPageScrolled(
- int position, float positionOffset, int positionOffsetPixels) {}
-
+ mViewPager.registerOnPageChangeCallback(
+ new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
updateFooterState();
@@ -206,31 +201,14 @@
mGridPageList.clear();
}
- // Generate pages by calculating # of items per grid.
- for (List<A11yMenuShortcut> page : GridViewParams.generateShortcutSubLists(
- GridViewParams.getGridItemCount(mService), mA11yMenuShortcutList)
- ) {
- addGridPage(page);
- }
-
- mViewPagerAdapter.set(mGridPageList);
- }
-
- private void addGridPage(List<A11yMenuShortcut> shortcutDataListInPage) {
- LayoutInflater inflater = LayoutInflater.from(mDisplayContext);
- View view = inflater.inflate(R.layout.grid_view, null);
- GridView gridView = view.findViewById(R.id.gridview);
- A11yMenuAdapter adapter = new A11yMenuAdapter(
- mService, mDisplayContext, shortcutDataListInPage);
- gridView.setNumColumns(GridViewParams.getGridColumnCount(mService));
- gridView.setAdapter(adapter);
- mGridPageList.add(gridView);
+ mViewPagerAdapter.set(GridViewParams.generateShortcutSubLists(
+ GridViewParams.getGridItemCount(mService), mA11yMenuShortcutList));
}
/** Updates footer's state by index of current page in view pager. */
- private void updateFooterState() {
+ public void updateFooterState() {
int currentPage = mViewPager.getCurrentItem();
- int lastPage = mViewPager.getAdapter().getCount() - 1;
+ int lastPage = mViewPager.getAdapter().getItemCount() - 1;
mA11yMenuFooter.getPreviousPageBtn().setEnabled(currentPage > 0);
mA11yMenuFooter.getNextPageBtn().setEnabled(currentPage < lastPage);
}
@@ -239,7 +217,7 @@
if (mViewPager == null) {
return;
}
- if ((pageIndex >= 0) && (pageIndex < mViewPager.getAdapter().getCount())) {
+ if ((pageIndex >= 0) && (pageIndex < mViewPager.getAdapter().getItemCount())) {
mViewPager.setCurrentItem(pageIndex);
}
}
@@ -341,7 +319,7 @@
protected A11yMenuFooterCallBack mFooterCallbacks =
new A11yMenuFooterCallBack() {
@Override
- public void onLeftButtonClicked() {
+ public void onPreviousButtonClicked() {
// Moves to previous page.
int targetPage = mViewPager.getCurrentItem() - 1;
goToPage(targetPage);
@@ -349,7 +327,7 @@
}
@Override
- public void onRightButtonClicked() {
+ public void onNextButtonClicked() {
// Moves to next page.
int targetPage = mViewPager.getCurrentItem() + 1;
goToPage(targetPage);
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
index 5670d72..43ec956 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
@@ -16,55 +16,64 @@
package com.android.systemui.accessibility.accessibilitymenu.view;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.GridView;
-import androidx.viewpager.widget.PagerAdapter;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
+import com.android.systemui.accessibility.accessibilitymenu.R;
+import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut;
import java.util.List;
/** The pager adapter, which provides the pages to the view pager widget. */
-class ViewPagerAdapter<T extends View> extends PagerAdapter {
+class ViewPagerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
- /** The widget list in each page of view pager. */
- private List<T> mWidgetList;
+ /** List of shortcuts, split into sub lists per page */
+ private List<List<A11yMenuShortcut>> mShortcutList;
+ private final AccessibilityMenuService mService;
- ViewPagerAdapter() {}
+ ViewPagerAdapter(AccessibilityMenuService service) {
+ mService = service;
+ }
- public void set(List<T> tList) {
- mWidgetList = tList;
+ public void set(List<List<A11yMenuShortcut>> tList) {
+ mShortcutList = tList;
notifyDataSetChanged();
}
+ @NonNull
@Override
- public int getCount() {
- if (mWidgetList == null) {
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+ View view = inflater.inflate(R.layout.grid_view, parent, false);
+ return new MenuViewHolder(view.findViewById(R.id.gridview));
+ }
+
+ @Override
+ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+ A11yMenuAdapter adapter = new A11yMenuAdapter(
+ mService, holder.itemView.getContext(), mShortcutList.get(position));
+ GridView gridView = (GridView) holder.itemView;
+ gridView.setNumColumns(A11yMenuViewPager.GridViewParams.getGridColumnCount(mService));
+ gridView.setAdapter(adapter);
+ }
+
+ @Override
+ public int getItemCount() {
+ if (mShortcutList == null) {
return 0;
}
- return mWidgetList.size();
+ return mShortcutList.size();
}
- @Override
- public int getItemPosition(Object object) {
- return POSITION_NONE;
- }
-
- @Override
- public boolean isViewFromObject(View view, Object object) {
- return view == object;
- }
-
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- if (mWidgetList == null) {
- return null;
+ static class MenuViewHolder extends RecyclerView.ViewHolder {
+ MenuViewHolder(View itemView) {
+ super(itemView);
}
- container.addView(mWidgetList.get(position));
- return mWidgetList.get(position);
}
-
- @Override
- public void destroyItem(ViewGroup container, int position, Object object) {
- container.removeView((View) object);
- }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 8860452..5251246 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -4,6 +4,16 @@
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
flag {
+ name: "add_black_background_for_window_magnifier"
+ namespace: "accessibility"
+ description: "Set the background for SurfaceView in window magnification black."
+ bug: "299981434"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "create_windowless_window_magnifier"
namespace: "accessibility"
description: "Uses SurfaceControlViewHost to create the magnifier for window magnification."
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index cdbac33..5632e30 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -281,13 +281,6 @@
}
flag {
- name: "qs_new_pipeline"
- namespace: "systemui"
- description: "Use the new pipeline for Quick Settings. Should have no behavior changes."
- bug: "241772429"
-}
-
-flag {
name: "qs_new_tiles"
namespace: "systemui"
description: "Use the new tiles in the Quick Settings. Should have no behavior changes."
@@ -998,6 +991,16 @@
}
flag {
+ name: "communal_widget_trampoline_fix"
+ namespace: "systemui"
+ description: "fixes activity starts caused by non-activity trampolines from widgets."
+ bug: "350468769"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "app_clips_backlinks"
namespace: "systemui"
description: "Enables Backlinks improvement feature in App Clips"
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
index a18b460..a5f8057 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
@@ -21,8 +21,7 @@
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
@@ -32,7 +31,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.theme.LocalAndroidColorScheme
@@ -42,11 +40,10 @@
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: ButtonColors = filledButtonColors(),
- verticalPadding: Dp = DefaultPlatformButtonVerticalPadding,
content: @Composable RowScope.() -> Unit,
) {
androidx.compose.material3.Button(
- modifier = modifier.padding(vertical = verticalPadding).height(36.dp),
+ modifier = modifier.heightIn(min = 36.dp),
colors = colors,
contentPadding = ButtonPaddings,
onClick = onClick,
@@ -63,11 +60,10 @@
enabled: Boolean = true,
colors: ButtonColors = outlineButtonColors(),
border: BorderStroke? = outlineButtonBorder(),
- verticalPadding: Dp = DefaultPlatformButtonVerticalPadding,
content: @Composable RowScope.() -> Unit,
) {
androidx.compose.material3.OutlinedButton(
- modifier = modifier.padding(vertical = verticalPadding).height(36.dp),
+ modifier = modifier.heightIn(min = 36.dp),
enabled = enabled,
colors = colors,
border = border,
@@ -118,7 +114,6 @@
}
}
-private val DefaultPlatformButtonVerticalPadding = 6.dp
private val ButtonPaddings = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
@Composable
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index f655ac1..d164eab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -95,7 +95,7 @@
import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerMessageViewModel
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.MessageViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
@@ -114,7 +114,7 @@
@Composable
fun BouncerContent(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
dialogFactory: BouncerDialogFactory,
modifier: Modifier = Modifier,
) {
@@ -128,7 +128,7 @@
@VisibleForTesting
fun BouncerContent(
layout: BouncerSceneLayout,
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
dialogFactory: BouncerDialogFactory,
modifier: Modifier
) {
@@ -173,7 +173,7 @@
*/
@Composable
private fun StandardLayout(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
modifier: Modifier = Modifier,
) {
val isHeightExpanded =
@@ -235,7 +235,7 @@
*/
@Composable
private fun SplitLayout(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
modifier: Modifier = Modifier,
) {
val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()
@@ -326,7 +326,7 @@
*/
@Composable
private fun BesideUserSwitcherLayout(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
modifier: Modifier = Modifier,
) {
val layoutDirection = LocalLayoutDirection.current
@@ -461,7 +461,7 @@
/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
@Composable
private fun BelowUserSwitcherLayout(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
modifier: Modifier = Modifier,
) {
Column(
@@ -506,7 +506,7 @@
@Composable
private fun FoldAware(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
aboveFold: @Composable BoxScope.() -> Unit,
belowFold: @Composable BoxScope.() -> Unit,
modifier: Modifier = Modifier,
@@ -649,7 +649,7 @@
*/
@Composable
private fun OutputArea(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
modifier: Modifier = Modifier,
) {
val authMethodViewModel: AuthMethodBouncerViewModel? by
@@ -677,7 +677,7 @@
*/
@Composable
private fun InputArea(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
pinButtonRowVerticalSpacing: Dp,
centerPatternDotsVertically: Boolean,
modifier: Modifier = Modifier,
@@ -706,7 +706,7 @@
@Composable
private fun ActionArea(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
modifier: Modifier = Modifier,
) {
val actionButton: BouncerActionButtonModel? by
@@ -774,7 +774,7 @@
@Composable
private fun Dialog(
- bouncerViewModel: BouncerViewModel,
+ bouncerViewModel: BouncerSceneContentViewModel,
dialogFactory: BouncerDialogFactory,
) {
val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsStateWithLifecycle()
@@ -803,7 +803,7 @@
/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
@Composable
private fun UserSwitcher(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
modifier: Modifier = Modifier,
) {
if (!viewModel.isUserSwitcherVisible) {
@@ -884,7 +884,7 @@
@Composable
private fun UserSwitcherDropdownMenu(
isExpanded: Boolean,
- items: List<BouncerViewModel.UserSwitcherDropdownItemViewModel>,
+ items: List<BouncerSceneContentViewModel.UserSwitcherDropdownItemViewModel>,
onDismissed: () -> Unit,
) {
val context = LocalContext.current
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 9fd30b4..3a46882 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -27,9 +27,11 @@
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneActionsViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
import javax.inject.Inject
@@ -51,23 +53,37 @@
class BouncerScene
@Inject
constructor(
- private val viewModel: BouncerViewModel,
+ private val actionsViewModelFactory: BouncerSceneActionsViewModel.Factory,
+ private val contentViewModelFactory: BouncerSceneContentViewModel.Factory,
private val dialogFactory: BouncerDialogFactory,
) : ComposableScene {
override val key = Scenes.Bouncer
+ private val actionsViewModel: BouncerSceneActionsViewModel by lazy {
+ actionsViewModelFactory.create()
+ }
+
override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- viewModel.destinationScenes
+ actionsViewModel.actions
+
+ override suspend fun activate() {
+ actionsViewModel.activate()
+ }
@Composable
override fun SceneScope.Content(
modifier: Modifier,
- ) = BouncerScene(viewModel, dialogFactory, modifier)
+ ) =
+ BouncerScene(
+ viewModel = rememberViewModel { contentViewModelFactory.create() },
+ dialogFactory = dialogFactory,
+ modifier = modifier,
+ )
}
@Composable
private fun SceneScope.BouncerScene(
- viewModel: BouncerViewModel,
+ viewModel: BouncerSceneContentViewModel,
dialogFactory: BouncerDialogFactory,
modifier: Modifier = Modifier,
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 12edd04..9db8bf1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -92,7 +92,8 @@
import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQS
-import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneActionsViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneContentViewModel
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
@@ -120,8 +121,9 @@
constructor(
private val shadeSession: SaveableSession,
private val notificationStackScrollView: Lazy<NotificationScrollView>,
- private val viewModel: QuickSettingsSceneViewModel,
private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
+ private val actionsViewModelFactory: QuickSettingsSceneActionsViewModel.Factory,
+ private val contentViewModelFactory: QuickSettingsSceneContentViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
@@ -130,8 +132,16 @@
) : ComposableScene {
override val key = Scenes.QuickSettings
+ private val actionsViewModel: QuickSettingsSceneActionsViewModel by lazy {
+ actionsViewModelFactory.create()
+ }
+
override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- viewModel.destinationScenes
+ actionsViewModel.actions
+
+ override suspend fun activate() {
+ actionsViewModel.activate()
+ }
@Composable
override fun SceneScope.Content(
@@ -139,7 +149,7 @@
) {
QuickSettingsScene(
notificationStackScrollView = notificationStackScrollView.get(),
- viewModel = viewModel,
+ viewModelFactory = contentViewModelFactory,
notificationsPlaceholderViewModel =
rememberViewModel { notificationsPlaceholderViewModelFactory.create() },
createTintedIconManager = tintedIconManagerFactory::create,
@@ -156,7 +166,7 @@
@Composable
private fun SceneScope.QuickSettingsScene(
notificationStackScrollView: NotificationScrollView,
- viewModel: QuickSettingsSceneViewModel,
+ viewModelFactory: QuickSettingsSceneContentViewModel.Factory,
notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
@@ -168,6 +178,7 @@
) {
val cutoutLocation = LocalDisplayCutout.current.location
+ val viewModel = rememberViewModel { viewModelFactory.create() }
val brightnessMirrorViewModel = rememberViewModel {
viewModel.brightnessMirrorViewModelFactory.create()
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index 68a6c98..920c234 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -17,7 +17,6 @@
package com.android.compose.animation.scene
import com.android.compose.animation.scene.content.state.TransitionState
-import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -63,47 +62,30 @@
if (transitionState.toScene == target) {
// The user is currently swiping to [target] but didn't release their pointer yet:
// animate the progress to `1`.
-
check(transitionState.fromScene == transitionState.currentScene)
- val progress = transitionState.progress
- if ((1f - progress).absoluteValue < ProgressVisibilityThreshold) {
- // The transition is already finished (progress ~= 1): no need to animate. We
- // finish the current transition early to make sure that the current state
- // change is committed.
- layoutState.finishTransition(transitionState, target)
- null
- } else {
- // The transition is in progress: start the canned animation at the same
- // progress as it was in.
- animateToScene(
- layoutState,
- target,
- transitionKey,
- isInitiatedByUserInput,
- replacedTransition = transitionState,
- )
- }
+
+ // The transition is in progress: start the canned animation at the same
+ // progress as it was in.
+ animateToScene(
+ layoutState,
+ target,
+ transitionKey,
+ isInitiatedByUserInput,
+ replacedTransition = transitionState,
+ )
} else if (transitionState.fromScene == target) {
// There is a transition from [target] to another scene: simply animate the same
// transition progress to `0`.
check(transitionState.toScene == transitionState.currentScene)
- val progress = transitionState.progress
- if (progress.absoluteValue < ProgressVisibilityThreshold) {
- // The transition is at progress ~= 0: no need to animate.We finish the current
- // transition early to make sure that the current state change is committed.
- layoutState.finishTransition(transitionState, target)
- null
- } else {
- animateToScene(
- layoutState,
- target,
- transitionKey,
- isInitiatedByUserInput,
- reversed = true,
- replacedTransition = transitionState,
- )
- }
+ animateToScene(
+ layoutState,
+ target,
+ transitionKey,
+ isInitiatedByUserInput,
+ reversed = true,
+ replacedTransition = transitionState,
+ )
} else {
// Generic interruption; the current transition is neither from or to [target].
val interruptionResult =
@@ -185,7 +167,7 @@
oneOffAnimation = oneOffAnimation,
targetProgress = targetProgress,
startTransition = { layoutState.startTransition(transition, chain) },
- finishTransition = { layoutState.finishTransition(transition, targetScene) },
+ finishTransition = { layoutState.finishTransition(transition) },
)
return transition
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 712fe6b..9c3896b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -777,7 +777,8 @@
fun snapToScene(scene: SceneKey) {
cancelOffsetAnimation()
- layoutState.finishTransition(this, idleScene = scene)
+ check(currentScene == scene)
+ layoutState.finishTransition(this)
}
override fun finish(): Job {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
index fe16ef751..2fbdf7c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
@@ -129,7 +129,7 @@
try {
animatable.animateTo(targetProgress)
} finally {
- state.finishTransition(this@PredictiveBackTransition, scene)
+ state.finishTransition(this@PredictiveBackTransition)
}
}
.also { animationJob = it }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index a6c6a80..44f5964f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -167,8 +167,6 @@
override val transitionState: TransitionState
get() = transitionStates.last()
- private val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
-
override val currentTransitions: List<TransitionState.Transition>
get() {
if (transitionStates.last() is TransitionState.Idle) {
@@ -180,12 +178,8 @@
}
}
- /**
- * The mapping of transitions that are finished, i.e. for which [finishTransition] was called,
- * to their idle scene.
- */
- @VisibleForTesting
- internal val finishedTransitions = mutableMapOf<TransitionState.Transition, SceneKey>()
+ /** The transitions that are finished, i.e. for which [finishTransition] was called. */
+ @VisibleForTesting internal val finishedTransitions = mutableSetOf<TransitionState.Transition>()
internal fun checkThread() {
val current = Thread.currentThread()
@@ -261,7 +255,7 @@
}
// Handle transition links.
- cancelActiveTransitionLinks()
+ currentTransition?.let { cancelActiveTransitionLinks(it) }
setupTransitionLinks(transition)
if (!enableInterruptions) {
@@ -289,8 +283,7 @@
// Force finish all transitions.
while (currentTransitions.isNotEmpty()) {
- val transition = transitionStates[0] as TransitionState.Transition
- finishTransition(transition, transition.currentScene)
+ finishTransition(transitionStates[0] as TransitionState.Transition)
}
// We finished all transitions, so we are now idle. We remove this state so that
@@ -328,18 +321,17 @@
)
}
- private fun cancelActiveTransitionLinks() {
- for ((link, linkedTransition) in activeTransitionLinks) {
- link.target.finishTransition(linkedTransition, linkedTransition.currentScene)
+ private fun cancelActiveTransitionLinks(transition: TransitionState.Transition) {
+ transition.activeTransitionLinks.forEach { (link, linkedTransition) ->
+ link.target.finishTransition(linkedTransition)
}
- activeTransitionLinks.clear()
+ transition.activeTransitionLinks.clear()
}
- private fun setupTransitionLinks(transitionState: TransitionState) {
- if (transitionState !is TransitionState.Transition) return
+ private fun setupTransitionLinks(transition: TransitionState.Transition) {
stateLinks.fastForEach { stateLink ->
val matchingLinks =
- stateLink.transitionLinks.fastFilter { it.isMatchingLink(transitionState) }
+ stateLink.transitionLinks.fastFilter { it.isMatchingLink(transition) }
if (matchingLinks.isEmpty()) return@fastForEach
if (matchingLinks.size > 1) error("More than one link matched.")
@@ -350,31 +342,27 @@
val linkedTransition =
LinkedTransition(
- originalTransition = transitionState,
+ originalTransition = transition,
fromScene = targetCurrentScene,
toScene = matchingLink.targetTo,
key = matchingLink.targetTransitionKey,
)
stateLink.target.startTransition(linkedTransition)
- activeTransitionLinks[stateLink] = linkedTransition
+ transition.activeTransitionLinks[stateLink] = linkedTransition
}
}
/**
- * Notify that [transition] was finished and that we should settle to [idleScene]. This will do
- * nothing if [transition] was interrupted since it was started.
+ * Notify that [transition] was finished and that it settled to its
+ * [currentScene][TransitionState.currentScene]. This will do nothing if [transition] was
+ * interrupted since it was started.
*/
- internal fun finishTransition(transition: TransitionState.Transition, idleScene: SceneKey) {
+ internal fun finishTransition(transition: TransitionState.Transition) {
checkThread()
- val existingIdleScene = finishedTransitions[transition]
- if (existingIdleScene != null) {
+ if (finishedTransitions.contains(transition)) {
// This transition was already finished.
- check(idleScene == existingIdleScene) {
- "Transition $transition was finished multiple times with different " +
- "idleScene ($existingIdleScene != $idleScene)"
- }
return
}
@@ -386,15 +374,15 @@
check(transitionStates.fastAll { it is TransitionState.Transition })
- // Mark this transition as finished and save the scene it is settling at.
- finishedTransitions[transition] = idleScene
+ // Mark this transition as finished.
+ finishedTransitions.add(transition)
// Finish all linked transitions.
- finishActiveTransitionLinks(idleScene)
+ finishActiveTransitionLinks(transition)
- // Keep a reference to the idle scene of the last removed transition, in case we remove all
- // transitions and should settle to Idle.
- var lastRemovedIdleScene: SceneKey? = null
+ // Keep a reference to the last transition, in case we remove all transitions and should
+ // settle to Idle.
+ val lastTransition = transitionStates.last()
// Remove all first n finished transitions.
var i = 0
@@ -407,14 +395,14 @@
}
// Remove the transition from the set of finished transitions.
- lastRemovedIdleScene = finishedTransitions.remove(t)
+ finishedTransitions.remove(t)
i++
}
// If all transitions are finished, we are idle.
if (i == nStates) {
check(finishedTransitions.isEmpty())
- this.transitionStates = listOf(TransitionState.Idle(checkNotNull(lastRemovedIdleScene)))
+ this.transitionStates = listOf(TransitionState.Idle(lastTransition.currentScene))
} else if (i > 0) {
this.transitionStates = transitionStates.subList(fromIndex = i, toIndex = nStates)
}
@@ -426,28 +414,18 @@
// Force finish all transitions.
while (currentTransitions.isNotEmpty()) {
val transition = transitionStates[0] as TransitionState.Transition
- finishTransition(transition, transition.currentScene)
+ finishTransition(transition)
}
check(transitionStates.size == 1)
transitionStates = listOf(TransitionState.Idle(scene))
}
- private fun finishActiveTransitionLinks(idleScene: SceneKey) {
- val previousTransition = this.transitionState as? TransitionState.Transition ?: return
- for ((link, linkedTransition) in activeTransitionLinks) {
- if (previousTransition.fromScene == idleScene) {
- // The transition ended by arriving at the fromScene, move link to Idle(fromScene).
- link.target.finishTransition(linkedTransition, linkedTransition.fromScene)
- } else if (previousTransition.toScene == idleScene) {
- // The transition ended by arriving at the toScene, move link to Idle(toScene).
- link.target.finishTransition(linkedTransition, linkedTransition.toScene)
- } else {
- // The transition was interrupted by something else, we reset to initial state.
- link.target.finishTransition(linkedTransition, linkedTransition.fromScene)
- }
+ private fun finishActiveTransitionLinks(transition: TransitionState.Transition) {
+ for ((link, linkedTransition) in transition.activeTransitionLinks) {
+ link.target.finishTransition(linkedTransition)
}
- activeTransitionLinks.clear()
+ transition.activeTransitionLinks.clear()
}
/**
@@ -465,31 +443,21 @@
fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold
- fun finishAllTransitions(lastTransitionIdleScene: SceneKey) {
+ fun finishAllTransitions() {
// Force finish all transitions.
while (currentTransitions.isNotEmpty()) {
- val transition = transitionStates[0] as TransitionState.Transition
- val idleScene =
- if (transitionStates.size == 1) {
- lastTransitionIdleScene
- } else {
- transition.currentScene
- }
-
- finishTransition(transition, idleScene)
+ finishTransition(transitionStates[0] as TransitionState.Transition)
}
}
- return when {
- isProgressCloseTo(0f) -> {
- finishAllTransitions(transition.fromScene)
- true
- }
- isProgressCloseTo(1f) -> {
- finishAllTransitions(transition.toScene)
- true
- }
- else -> false
+ val shouldSnap =
+ (isProgressCloseTo(0f) && transition.currentScene == transition.fromScene) ||
+ (isProgressCloseTo(1f) && transition.currentScene == transition.toScene)
+ return if (shouldSnap) {
+ finishAllTransitions()
+ true
+ } else {
+ false
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt
index add3934..0bd676b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt
@@ -29,6 +29,8 @@
import com.android.compose.animation.scene.TransformationSpec
import com.android.compose.animation.scene.TransformationSpecImpl
import com.android.compose.animation.scene.TransitionKey
+import com.android.compose.animation.scene.transition.link.LinkedTransition
+import com.android.compose.animation.scene.transition.link.StateLink
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@@ -110,6 +112,9 @@
*/
private var interruptionDecay: Animatable<Float, AnimationVector1D>? = null
+ /** The map of active links that connects this transition to other transitions. */
+ internal val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
+
init {
check(fromContent != toContent)
check(
@@ -131,7 +136,7 @@
* animation is complete or cancel it to snap the animation. Calling [finish] multiple
* times will return the same [Job].
*/
- abstract fun finish(): Job
+ internal abstract fun finish(): Job
/**
* Whether we are transitioning. If [from] or [to] is empty, we will also check that they
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 75f44ff..60cefb0 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -1380,8 +1380,8 @@
// Manually finish the transition.
rule.runOnUiThread {
- state.finishTransition(aToB, SceneB)
- state.finishTransition(bToC, SceneC)
+ state.finishTransition(aToB)
+ state.finishTransition(bToC)
}
rule.waitForIdle()
assertThat(state.transitionState).isIdle()
@@ -1482,7 +1482,7 @@
// Manually finish A => B so only B => C is remaining.
bToCInterruptionProgress = 0f
- rule.runOnUiThread { state.finishTransition(aToB, SceneB) }
+ rule.runOnUiThread { state.finishTransition(aToB) }
rule
.onNode(isElement(TestElements.Foo, SceneB))
.assertPositionInRootIsEqualTo(offsetInB.x, offsetInB.y)
@@ -2002,6 +2002,7 @@
transition(
from = SceneB,
to = SceneC,
+ current = { SceneB },
progress = { 0f },
interruptionProgress = { interruptionProgress },
onFinish = neverFinish(),
@@ -2018,8 +2019,8 @@
// 100%. We should be at (20dp, 20dp), unless the interruption deltas have not been
// correctly cleaned.
rule.runOnUiThread {
- state.finishTransition(aToB, idleScene = SceneB)
- state.finishTransition(bToC, idleScene = SceneB)
+ state.finishTransition(aToB)
+ state.finishTransition(bToC)
state.startTransition(
transition(
from = SceneB,
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index 6b417ee..c8ac580 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -18,7 +18,9 @@
import android.util.Log
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestScenes.SceneA
@@ -110,16 +112,6 @@
}
@Test
- fun setTargetScene_transitionToOriginalScene() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutState(SceneA)
- assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
-
- // Progress is 0f, so we don't animate at all and directly snap back to A.
- assertThat(state.setTargetScene(SceneA, coroutineScope = this)).isNull()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneA))
- }
-
- @Test
fun setTargetScene_coroutineScopeCancelled() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutState(SceneA)
@@ -180,7 +172,7 @@
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
- childState.finishTransition(childTransition, SceneB)
+ childState.finishTransition(childTransition)
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
}
@@ -217,7 +209,7 @@
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
assertThat(parentParentState.isTransitioning(SceneB, SceneC)).isTrue()
- childState.finishTransition(childTransition, SceneB)
+ childState.finishTransition(childTransition)
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
assertThat(parentParentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
@@ -241,13 +233,13 @@
fun linkedTransition_reverseTransitionIsNotLinked() {
val (parentState, childState) = setupLinkedStates()
- val childTransition = transition(SceneB, SceneA)
+ val childTransition = transition(SceneB, SceneA, current = { SceneB })
childState.startTransition(childTransition)
assertThat(childState.isTransitioning(SceneB, SceneA)).isTrue()
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
- childState.finishTransition(childTransition, SceneB)
+ childState.finishTransition(childTransition)
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
}
@@ -256,27 +248,15 @@
fun linkedTransition_startsLinkAndFinishesLinkInFromState() {
val (parentState, childState) = setupLinkedStates()
- val childTransition = transition(SceneA, SceneB)
+ val childTransition = transition(SceneA, SceneB, current = { SceneA })
childState.startTransition(childTransition)
- childState.finishTransition(childTransition, SceneA)
+ childState.finishTransition(childTransition)
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
}
@Test
- fun linkedTransition_startsLinkAndFinishesLinkInUnknownState() {
- val (parentState, childState) = setupLinkedStates()
-
- val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition)
-
- childState.finishTransition(childTransition, SceneD)
- assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
- assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
- }
-
- @Test
fun linkedTransition_startsLinkButLinkedStateIsTakenOver() = runTest {
val (parentState, childState) = setupLinkedStates()
@@ -295,7 +275,7 @@
childState.startTransition(childTransition)
parentState.startTransition(parentTransition)
- childState.finishTransition(childTransition, SceneB)
+ childState.finishTransition(childTransition)
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
assertThat(parentState.transitionState).isEqualTo(parentTransition)
}
@@ -341,7 +321,9 @@
@Test
fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.2f }))
+ state.startTransition(
+ transition(from = SceneA, to = SceneB, current = { SceneA }, progress = { 0.2f })
+ )
assertThat(state.isTransitioning()).isTrue()
// Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -399,6 +381,31 @@
}
@Test
+ fun snapToIdleIfClose_closeButNotCurrentScene() = runMonotonicClockTest {
+ val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
+ var progress by mutableStateOf(0f)
+ var currentScene by mutableStateOf(SceneB)
+ state.startTransition(
+ transition(
+ from = SceneA,
+ to = SceneB,
+ current = { currentScene },
+ progress = { progress }
+ )
+ )
+ assertThat(state.isTransitioning()).isTrue()
+
+ // Ignore the request if we are close to a scene that is not the current scene
+ assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
+ assertThat(state.isTransitioning()).isTrue()
+
+ progress = 1f
+ currentScene = SceneA
+ assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
+ assertThat(state.isTransitioning()).isTrue()
+ }
+
+ @Test
fun linkedTransition_fuzzyLinksAreMatchedAndStarted() {
val (parentState, childState) = setupLinkedStates(SceneC, SceneA, null, null, null, SceneD)
val childTransition = transition(SceneA, SceneB)
@@ -407,7 +414,7 @@
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
- childState.finishTransition(childTransition, SceneB)
+ childState.finishTransition(childTransition)
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
}
@@ -417,13 +424,13 @@
val (parentState, childState) =
setupLinkedStates(SceneC, SceneA, SceneA, null, null, SceneD)
- val childTransition = transition(SceneA, SceneB)
+ val childTransition = transition(SceneA, SceneB, current = { SceneA })
childState.startTransition(childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
- childState.finishTransition(childTransition, SceneA)
+ childState.finishTransition(childTransition)
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
}
@@ -595,12 +602,12 @@
// Mark bToC as finished. The list of current transitions does not change because aToB is
// still not marked as finished.
- state.finishTransition(bToC, idleScene = bToC.currentScene)
- assertThat(state.finishedTransitions).containsExactly(bToC, bToC.currentScene)
+ state.finishTransition(bToC)
+ assertThat(state.finishedTransitions).containsExactly(bToC)
assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
// Mark aToB as finished. This will remove both aToB and bToC from the list of transitions.
- state.finishTransition(aToB, idleScene = aToB.currentScene)
+ state.finishTransition(aToB)
assertThat(state.finishedTransitions).isEmpty()
assertThat(state.currentTransitions).containsExactly(cToA).inOrder()
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
index 91bd7e1..e4e4108 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
@@ -29,7 +29,7 @@
fun transition(
from: SceneKey,
to: SceneKey,
- current: () -> SceneKey = { from },
+ current: () -> SceneKey = { to },
progress: () -> Float = { 0f },
progressVelocity: () -> Float = { 0f },
previewProgress: () -> Float = { 0f },
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index c9fa671..deef652 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -22,14 +22,14 @@
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,17 +39,16 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
- private val underTest by lazy {
- PinBouncerViewModel(
- applicationContext = context,
- viewModelScope = testScope.backgroundScope,
- interactor = bouncerInteractor,
+ private val underTest =
+ kosmos.pinBouncerViewModelFactory.create(
isInputEnabled = MutableStateFlow(true),
- simBouncerInteractor = kosmos.simBouncerInteractor,
- authenticationMethod = AuthenticationMethodModel.Pin,
onIntentionalUserInput = {},
+ authenticationMethod = AuthenticationMethodModel.Pin,
)
+
+ @Before
+ fun setUp() {
+ underTest.activateIn(testScope)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
index 4f5d0e5..b83ab7e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
@@ -52,6 +52,7 @@
import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -87,6 +88,7 @@
intArrayOf(ignoreHelpMessageId)
)
underTest = kosmos.bouncerMessageViewModel
+ underTest.activateIn(testScope)
overrideResource(R.string.kg_trust_agent_disabled, "Trust agent is unavailable")
kosmos.fakeSystemPropertiesHelper.set(
DeviceUnlockedInteractor.SYS_BOOT_REASON_PROP,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModelTest.kt
new file mode 100644
index 0000000..a86a0c0
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModelTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.fakeSceneDataSource
+import com.android.systemui.testKosmos
+import com.android.systemui.truth.containsEntriesExactly
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
+class BouncerSceneActionsViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private lateinit var underTest: BouncerSceneActionsViewModel
+
+ @Before
+ fun setUp() {
+ kosmos.sceneContainerStartable.start()
+ underTest = kosmos.bouncerSceneActionsViewModel
+ underTest.activateIn(testScope)
+ }
+
+ @Test
+ fun actions() =
+ testScope.runTest {
+ val actions by collectLastValue(underTest.actions)
+ kosmos.fakeSceneDataSource.changeScene(Scenes.QuickSettings)
+ runCurrent()
+
+ kosmos.fakeSceneDataSource.changeScene(Scenes.Bouncer)
+ runCurrent()
+
+ assertThat(actions)
+ .containsEntriesExactly(
+ Back to UserActionResult(Scenes.QuickSettings),
+ Swipe(SwipeDirection.Down) to UserActionResult(Scenes.QuickSettings),
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
similarity index 88%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
index ccddc9c..9bddcd2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
@@ -18,10 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -38,11 +34,9 @@
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.scene.domain.startable.sceneContainerStartable
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
-import com.android.systemui.truth.containsEntriesExactly
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -62,17 +56,18 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableSceneContainer
-class BouncerViewModelTest : SysuiTestCase() {
+class BouncerSceneContentViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private lateinit var underTest: BouncerViewModel
+ private lateinit var underTest: BouncerSceneContentViewModel
@Before
fun setUp() {
kosmos.sceneContainerStartable.start()
- underTest = kosmos.bouncerViewModel
+ underTest = kosmos.bouncerSceneContentViewModel
+ underTest.activateIn(testScope)
}
@Test
@@ -201,23 +196,6 @@
assertThat(isFoldSplitRequired).isTrue()
}
- @Test
- fun destinationScenes() =
- testScope.runTest {
- val destinationScenes by collectLastValue(underTest.destinationScenes)
- kosmos.fakeSceneDataSource.changeScene(Scenes.QuickSettings)
- runCurrent()
-
- kosmos.fakeSceneDataSource.changeScene(Scenes.Bouncer)
- runCurrent()
-
- assertThat(destinationScenes)
- .containsEntriesExactly(
- Back to UserActionResult(Scenes.QuickSettings),
- Swipe(SwipeDirection.Down) to UserActionResult(Scenes.QuickSettings),
- )
- }
-
private fun authMethodsToTest(): List<AuthenticationMethodModel> {
return listOf(None, Pin, Password, Pattern, Sim)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index a09189e..492543f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.inputmethod.data.repository.fakeInputMethodRepository
import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
@@ -44,7 +45,6 @@
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@@ -68,12 +68,8 @@
private val isInputEnabled = MutableStateFlow(true)
private val underTest =
- PasswordBouncerViewModel(
- viewModelScope = testScope.backgroundScope,
- isInputEnabled = isInputEnabled.asStateFlow(),
- interactor = bouncerInteractor,
- inputMethodInteractor = inputMethodInteractor,
- selectedUserInteractor = selectedUserInteractor,
+ kosmos.passwordBouncerViewModelFactory.create(
+ isInputEnabled = isInputEnabled,
onIntentionalUserInput = {},
)
@@ -81,6 +77,7 @@
fun setUp() {
overrideResource(R.string.keyguard_enter_your_password, ENTER_YOUR_PASSWORD)
overrideResource(R.string.kg_wrong_password, WRONG_PASSWORD)
+ underTest.activateIn(testScope)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 14d3634..7c773a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -26,9 +26,9 @@
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate as Point
-import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
@@ -54,17 +54,12 @@
private val testScope = kosmos.testScope
private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
- private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
- private val bouncerViewModel by lazy { kosmos.bouncerViewModel }
- private val underTest by lazy {
- PatternBouncerViewModel(
- applicationContext = context,
- viewModelScope = testScope.backgroundScope,
- interactor = bouncerInteractor,
+ private val bouncerViewModel by lazy { kosmos.bouncerSceneContentViewModel }
+ private val underTest =
+ kosmos.patternBouncerViewModelFactory.create(
isInputEnabled = MutableStateFlow(true).asStateFlow(),
onIntentionalUserInput = {},
)
- }
private val containerSize = 90 // px
private val dotSize = 30 // px
@@ -73,6 +68,7 @@
fun setUp() {
overrideResource(R.string.keyguard_enter_your_pattern, ENTER_YOUR_PATTERN)
overrideResource(R.string.kg_wrong_pattern, WRONG_PATTERN)
+ underTest.activateIn(testScope)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 89bafb9..8d82e97 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -31,10 +31,9 @@
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.data.repository.fakeSimBouncerRepository
-import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
@@ -44,7 +43,6 @@
import kotlin.random.nextInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -62,24 +60,18 @@
private val testScope = kosmos.testScope
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
- private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
- private lateinit var underTest: PinBouncerViewModel
+ private val underTest =
+ kosmos.pinBouncerViewModelFactory.create(
+ isInputEnabled = MutableStateFlow(true),
+ onIntentionalUserInput = {},
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ )
@Before
fun setUp() {
- underTest =
- PinBouncerViewModel(
- applicationContext = context,
- viewModelScope = testScope.backgroundScope,
- interactor = bouncerInteractor,
- isInputEnabled = MutableStateFlow(true).asStateFlow(),
- simBouncerInteractor = kosmos.simBouncerInteractor,
- authenticationMethod = AuthenticationMethodModel.Pin,
- onIntentionalUserInput = {},
- )
-
overrideResource(R.string.keyguard_enter_your_pin, ENTER_YOUR_PIN)
overrideResource(R.string.kg_wrong_pin, WRONG_PIN)
+ underTest.activateIn(testScope)
}
@Test
@@ -96,14 +88,10 @@
fun simBouncerViewModel_simAreaIsVisible() =
testScope.runTest {
val underTest =
- PinBouncerViewModel(
- applicationContext = context,
- viewModelScope = testScope.backgroundScope,
- interactor = bouncerInteractor,
- isInputEnabled = MutableStateFlow(true).asStateFlow(),
- simBouncerInteractor = kosmos.simBouncerInteractor,
- authenticationMethod = AuthenticationMethodModel.Sim,
+ kosmos.pinBouncerViewModelFactory.create(
+ isInputEnabled = MutableStateFlow(true),
onIntentionalUserInput = {},
+ authenticationMethod = AuthenticationMethodModel.Sim,
)
assertThat(underTest.isSimAreaVisible).isTrue()
@@ -125,14 +113,10 @@
fun simBouncerViewModel_autoConfirmEnabled_hintedPinLengthIsNull() =
testScope.runTest {
val underTest =
- PinBouncerViewModel(
- applicationContext = context,
- viewModelScope = testScope.backgroundScope,
- interactor = bouncerInteractor,
- isInputEnabled = MutableStateFlow(true).asStateFlow(),
- simBouncerInteractor = kosmos.simBouncerInteractor,
- authenticationMethod = AuthenticationMethodModel.Sim,
+ kosmos.pinBouncerViewModelFactory.create(
+ isInputEnabled = MutableStateFlow(true),
onIntentionalUserInput = {},
+ authenticationMethod = AuthenticationMethodModel.Pin,
)
kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
@@ -355,6 +339,7 @@
AuthenticationMethodModel.Pin
)
kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
+ runCurrent()
underTest.onPinButtonClicked(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt
new file mode 100644
index 0000000..b3ffc71
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.usage.UsageEvents
+import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.usagestats.data.repository.fakeUsageStatsRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.shared.system.taskStackChangeListeners
+import com.android.systemui.testKosmos
+import com.android.systemui.util.time.fakeSystemClock
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.currentTime
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WidgetTrampolineInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val activityStarter = kosmos.activityStarter
+ private val usageStatsRepository = kosmos.fakeUsageStatsRepository
+ private val taskStackChangeListeners = kosmos.taskStackChangeListeners
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val userTracker = kosmos.fakeUserTracker
+ private val systemClock = kosmos.fakeSystemClock
+
+ private val underTest = kosmos.widgetTrampolineInteractor
+
+ @Before
+ fun setUp() {
+ userTracker.set(listOf(MAIN_USER), 0)
+ systemClock.setCurrentTimeMillis(testScope.currentTime)
+ }
+
+ @Test
+ fun testNewTaskStartsWhileOnHub_triggersUnlock() =
+ testScope.runTest {
+ transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+ backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+ runCurrent()
+
+ verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+ moveTaskToFront()
+
+ verify(activityStarter).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+ }
+
+ @Test
+ fun testNewTaskStartsAfterExitingHub_doesNotTriggerUnlock() =
+ testScope.runTest {
+ transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+ backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+ runCurrent()
+
+ transition(from = KeyguardState.GLANCEABLE_HUB, to = KeyguardState.LOCKSCREEN)
+ moveTaskToFront()
+
+ verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+ }
+
+ @Test
+ fun testNewTaskStartsAfterTimeout_doesNotTriggerUnlock() =
+ testScope.runTest {
+ transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+ backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+ runCurrent()
+
+ advanceTime(2.seconds)
+ moveTaskToFront()
+
+ verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+ }
+
+ @Test
+ fun testActivityResumedWhileOnHub_triggersUnlock() =
+ testScope.runTest {
+ transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+ backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+ runCurrent()
+
+ addActivityEvent(UsageEvents.Event.ACTIVITY_RESUMED)
+ advanceTime(1.seconds)
+
+ verify(activityStarter).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+ }
+
+ @Test
+ fun testActivityResumedAfterExitingHub_doesNotTriggerUnlock() =
+ testScope.runTest {
+ transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+ backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+ runCurrent()
+
+ transition(from = KeyguardState.GLANCEABLE_HUB, to = KeyguardState.LOCKSCREEN)
+ addActivityEvent(UsageEvents.Event.ACTIVITY_RESUMED)
+ advanceTime(1.seconds)
+
+ verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+ }
+
+ @Test
+ fun testActivityDestroyed_doesNotTriggerUnlock() =
+ testScope.runTest {
+ transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+ backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+ runCurrent()
+
+ addActivityEvent(UsageEvents.Event.ACTIVITY_DESTROYED)
+ advanceTime(1.seconds)
+
+ verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+ }
+
+ @Test
+ fun testMultipleActivityEvents_triggersUnlockOnlyOnce() =
+ testScope.runTest {
+ transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+ backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+ runCurrent()
+
+ addActivityEvent(UsageEvents.Event.ACTIVITY_RESUMED)
+ advanceTime(10.milliseconds)
+ addActivityEvent(UsageEvents.Event.ACTIVITY_RESUMED)
+ advanceTime(1.seconds)
+
+ verify(activityStarter, times(1)).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+ }
+
+ private fun TestScope.advanceTime(duration: Duration) {
+ systemClock.advanceTime(duration.inWholeMilliseconds)
+ advanceTimeBy(duration)
+ }
+
+ private fun TestScope.addActivityEvent(type: Int) {
+ usageStatsRepository.addEvent(
+ instanceId = 1,
+ user = MAIN_USER.userHandle,
+ packageName = "pkg.test",
+ timestamp = systemClock.currentTimeMillis(),
+ type = type,
+ )
+ runCurrent()
+ }
+
+ private fun TestScope.moveTaskToFront() {
+ taskStackChangeListeners.listenerImpl.onTaskMovedToFront(mock<RunningTaskInfo>())
+ runCurrent()
+ }
+
+ private suspend fun TestScope.transition(from: KeyguardState, to: KeyguardState) {
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ TransitionStep(
+ from = from,
+ to = to,
+ value = 0.1f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "test",
+ ),
+ TransitionStep(
+ from = from,
+ to = to,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "test",
+ ),
+ ),
+ testScope
+ )
+ runCurrent()
+ }
+
+ private companion object {
+ val MAIN_USER: UserInfo = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
index 023de52..400f736 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
@@ -27,7 +27,9 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.widgetTrampolineInteractor
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.ActivityStarter
@@ -67,9 +69,11 @@
with(kosmos) {
underTest =
WidgetInteractionHandler(
+ applicationScope = applicationCoroutineScope,
activityStarter = activityStarter,
communalSceneInteractor = communalSceneInteractor,
logBuffer = logcatLogBuffer(),
+ widgetTrampolineInteractor = widgetTrampolineInteractor,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
index 409c551..5ec566b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
+import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -49,6 +50,24 @@
val underTest = kosmos.primaryBouncerToLockscreenTransitionViewModel
@Test
+ fun lockscreenAlphaStartsFromViewStateAccessorAlpha() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor(alpha = { 0.5f })
+ val alpha by collectLastValue(underTest.lockscreenAlpha(viewState))
+
+ keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
+
+ keyguardTransitionRepository.sendTransitionStep(step(0f))
+ assertThat(alpha).isEqualTo(0.5f)
+
+ keyguardTransitionRepository.sendTransitionStep(step(0.5f))
+ assertThat(alpha).isIn(Range.open(0.5f, 1f))
+
+ keyguardTransitionRepository.sendTransitionStep(step(1f))
+ assertThat(alpha).isEqualTo(1f)
+ }
+
+ @Test
fun deviceEntryParentViewAlpha() =
testScope.runTest {
val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 3146318..8995f46 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -24,7 +24,6 @@
import android.service.quicksettings.Tile
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE
import com.android.systemui.Flags.FLAG_QS_NEW_TILES
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -107,7 +106,6 @@
fun setup() {
MockitoAnnotations.initMocks(this)
- mSetFlagsRule.enableFlags(FLAG_QS_NEW_PIPELINE)
mSetFlagsRule.enableFlags(FLAG_QS_NEW_TILES)
userRepository.setUserInfos(listOf(USER_INFO_0, USER_INFO_1))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
index e8ad038..00c7204 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
@@ -20,7 +20,6 @@
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
@@ -98,8 +97,6 @@
@Before
fun setUp() {
- mSetFlagsRule.enableFlags(Flags.FLAG_QS_NEW_PIPELINE)
-
with(kosmos) {
restoreReconciliationInteractor.start()
autoAddInteractor.init(kosmos.currentTilesInteractor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
index dffd0d7..6bcaea4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
@@ -20,7 +20,6 @@
import android.os.UserManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
-import com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
@@ -59,6 +58,7 @@
// Getter here so it can change when there is a managed profile.
private val workTileAvailable: Boolean
get() = hasManagedProfile()
+
private val currentUser: Int
get() = kosmos.userTracker.userId
@@ -67,8 +67,6 @@
@Before
fun setUp() {
- mSetFlagsRule.enableFlags(FLAG_QS_NEW_PIPELINE)
-
kosmos.qsTileFactory = FakeQSFactory(::tileCreator)
kosmos.restoreReconciliationInteractor.start()
kosmos.autoAddInteractor.init(kosmos.currentTilesInteractor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModelTest.kt
similarity index 72%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModelTest.kt
index 0363808..f26a9db 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModelTest.kt
@@ -36,11 +36,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.data.repository.mediaFilterRepository
-import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
-import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.qs.FooterActionsController
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneBackInteractor
@@ -49,41 +45,29 @@
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModelFactory
-import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
@EnableSceneContainer
-class QuickSettingsSceneViewModelTest : SysuiTestCase() {
+class QuickSettingsSceneActionsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
- private val footerActionsViewModel = mock<FooterActionsViewModel>()
- private val footerActionsViewModelFactory =
- mock<FooterActionsViewModel.Factory> {
- whenever(create(any())).thenReturn(footerActionsViewModel)
- }
- private val footerActionsController = mock<FooterActionsController>()
private val sceneInteractor = kosmos.sceneInteractor
private val sceneBackInteractor = kosmos.sceneBackInteractor
private val sceneContainerStartable = kosmos.sceneContainerStartable
- private lateinit var underTest: QuickSettingsSceneViewModel
+ private lateinit var underTest: QuickSettingsSceneActionsViewModel
@Before
fun setUp() {
@@ -91,22 +75,18 @@
sceneContainerStartable.start()
underTest =
- QuickSettingsSceneViewModel(
- brightnessMirrorViewModelFactory = kosmos.brightnessMirrorViewModelFactory,
- shadeHeaderViewModelFactory = kosmos.shadeHeaderViewModelFactory,
+ QuickSettingsSceneActionsViewModel(
qsSceneAdapter = qsFlexiglassAdapter,
- footerActionsViewModelFactory = footerActionsViewModelFactory,
- footerActionsController = footerActionsController,
sceneBackInteractor = sceneBackInteractor,
- mediaCarouselInteractor = kosmos.mediaCarouselInteractor,
)
+ underTest.activateIn(testScope)
}
@Test
fun destinations_whenNotCustomizing_unlocked() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, false)
- val destinations by collectLastValue(underTest.destinationScenes)
+ val actions by collectLastValue(underTest.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
qsFlexiglassAdapter.setCustomizing(false)
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
@@ -116,7 +96,7 @@
SuccessFingerprintAuthenticationStatus(0, true)
)
- assertThat(destinations)
+ assertThat(actions)
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
@@ -135,7 +115,7 @@
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, false)
qsFlexiglassAdapter.setCustomizing(false)
- val destinations by collectLastValue(underTest.destinationScenes)
+ val actions by collectLastValue(underTest.actions)
val currentScene by collectLastValue(sceneInteractor.currentScene)
val backScene by collectLastValue(sceneBackInteractor.backScene)
@@ -145,7 +125,7 @@
assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
assertThat(backScene).isEqualTo(Scenes.Lockscreen)
- assertThat(destinations)
+ assertThat(actions)
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Lockscreen),
@@ -164,7 +144,7 @@
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, false)
qsFlexiglassAdapter.setCustomizing(false)
- val destinations by collectLastValue(underTest.destinationScenes)
+ val actions by collectLastValue(underTest.actions)
val currentScene by collectLastValue(sceneInteractor.currentScene)
val backScene by collectLastValue(sceneBackInteractor.backScene)
@@ -176,7 +156,7 @@
assertThat(currentScene).isEqualTo(Scenes.Gone)
assertThat(backScene).isNull()
- assertThat(destinations)
+ assertThat(actions)
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
@@ -194,7 +174,7 @@
fun destinations_whenNotCustomizing_authMethodSwipe_lockscreenNotDismissed() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, false)
- val destinations by collectLastValue(underTest.destinationScenes)
+ val actions by collectLastValue(underTest.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
qsFlexiglassAdapter.setCustomizing(false)
kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
@@ -202,7 +182,7 @@
AuthenticationMethodModel.None
)
- assertThat(destinations)
+ assertThat(actions)
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
@@ -220,17 +200,17 @@
fun destinations_whenCustomizing_noDestinations() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, false)
- val destinations by collectLastValue(underTest.destinationScenes)
+ val actions by collectLastValue(underTest.actions)
qsFlexiglassAdapter.setCustomizing(true)
- assertThat(destinations).isEmpty()
+ assertThat(actions).isEmpty()
}
@Test
fun destinations_whenNotCustomizing_inSplitShade_unlocked() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, true)
- val destinations by collectLastValue(underTest.destinationScenes)
+ val actions by collectLastValue(underTest.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
qsFlexiglassAdapter.setCustomizing(false)
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
@@ -240,7 +220,7 @@
SuccessFingerprintAuthenticationStatus(0, true)
)
- assertThat(destinations)
+ assertThat(actions)
.isEqualTo(
mapOf(
Back to UserActionResult(Scenes.Shade),
@@ -258,49 +238,9 @@
fun destinations_whenCustomizing_inSplitShade_noDestinations() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, true)
- val destinations by collectLastValue(underTest.destinationScenes)
+ val actions by collectLastValue(underTest.actions)
qsFlexiglassAdapter.setCustomizing(true)
- assertThat(destinations).isEmpty()
- }
-
- @Test
- fun gettingViewModelInitializesControllerOnlyOnce() {
- underTest.getFooterActionsViewModel(mock())
- underTest.getFooterActionsViewModel(mock())
-
- verify(footerActionsController, times(1)).init()
- }
-
- @Test
- fun addAndRemoveMedia_mediaVisibilityIsUpdated() =
- testScope.runTest {
- kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
- val isMediaVisible by collectLastValue(underTest.isMediaVisible)
- val userMedia = MediaData(active = true)
-
- assertThat(isMediaVisible).isFalse()
-
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
-
- assertThat(isMediaVisible).isTrue()
-
- kosmos.mediaFilterRepository.removeSelectedUserMediaEntry(userMedia.instanceId)
-
- assertThat(isMediaVisible).isFalse()
- }
-
- @Test
- fun addInactiveMedia_mediaVisibilityIsUpdated() =
- testScope.runTest {
- kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
- val isMediaVisible by collectLastValue(underTest.isMediaVisible)
- val userMedia = MediaData(active = false)
-
- assertThat(isMediaVisible).isFalse()
-
- kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
-
- assertThat(isMediaVisible).isTrue()
+ assertThat(actions).isEmpty()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
new file mode 100644
index 0000000..9563538
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.ui.viewmodel
+
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
+import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModelFactory
+import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+@EnableSceneContainer
+class QuickSettingsSceneContentViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
+ private val footerActionsViewModel = mock<FooterActionsViewModel>()
+ private val footerActionsViewModelFactory =
+ mock<FooterActionsViewModel.Factory> {
+ whenever(create(any())).thenReturn(footerActionsViewModel)
+ }
+ private val footerActionsController = mock<FooterActionsController>()
+
+ private val sceneContainerStartable = kosmos.sceneContainerStartable
+
+ private lateinit var underTest: QuickSettingsSceneContentViewModel
+
+ @Before
+ fun setUp() {
+ kosmos.fakeFeatureFlagsClassic.set(Flags.NEW_NETWORK_SLICE_UI, false)
+
+ sceneContainerStartable.start()
+ underTest =
+ QuickSettingsSceneContentViewModel(
+ brightnessMirrorViewModelFactory = kosmos.brightnessMirrorViewModelFactory,
+ shadeHeaderViewModelFactory = kosmos.shadeHeaderViewModelFactory,
+ qsSceneAdapter = qsFlexiglassAdapter,
+ footerActionsViewModelFactory = footerActionsViewModelFactory,
+ footerActionsController = footerActionsController,
+ mediaCarouselInteractor = kosmos.mediaCarouselInteractor,
+ )
+ underTest.activateIn(testScope)
+ }
+
+ @Test
+ fun gettingViewModelInitializesControllerOnlyOnce() {
+ underTest.getFooterActionsViewModel(mock())
+ underTest.getFooterActionsViewModel(mock())
+
+ verify(footerActionsController, times(1)).init()
+ }
+
+ @Test
+ fun addAndRemoveMedia_mediaVisibilityIsUpdated() =
+ testScope.runTest {
+ kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
+ val isMediaVisible by collectLastValue(underTest.isMediaVisible)
+ val userMedia = MediaData(active = true)
+
+ assertThat(isMediaVisible).isFalse()
+
+ kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+
+ assertThat(isMediaVisible).isTrue()
+
+ kosmos.mediaFilterRepository.removeSelectedUserMediaEntry(userMedia.instanceId)
+
+ assertThat(isMediaVisible).isFalse()
+ }
+
+ @Test
+ fun addInactiveMedia_mediaVisibilityIsUpdated() =
+ testScope.runTest {
+ kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
+ val isMediaVisible by collectLastValue(underTest.isMediaVisible)
+ val userMedia = MediaData(active = false)
+
+ assertThat(isMediaVisible).isFalse()
+
+ kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+
+ assertThat(isMediaVisible).isTrue()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 66e45ab..aee3ce0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -36,10 +36,10 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
-import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModel
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.coroutines.collectLastValue
@@ -133,13 +133,14 @@
sceneInteractor = sceneInteractor,
falsingInteractor = kosmos.falsingInteractor,
powerInteractor = kosmos.powerInteractor,
+ motionEventHandlerReceiver = {},
)
.apply { setTransitionState(transitionState) }
}
private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
private lateinit var bouncerActionButtonInteractor: BouncerActionButtonInteractor
- private lateinit var bouncerViewModel: BouncerViewModel
+ private lateinit var bouncerSceneContentViewModel: BouncerSceneContentViewModel
private val lockscreenSceneActionsViewModel by lazy {
LockscreenSceneActionsViewModel(
@@ -187,7 +188,7 @@
}
bouncerActionButtonInteractor = kosmos.bouncerActionButtonInteractor
- bouncerViewModel = kosmos.bouncerViewModel
+ bouncerSceneContentViewModel = kosmos.bouncerSceneContentViewModel
shadeSceneContentViewModel = kosmos.shadeSceneContentViewModel
shadeSceneActionsViewModel = kosmos.shadeSceneActionsViewModel
@@ -198,6 +199,8 @@
lockscreenSceneActionsViewModel.activateIn(testScope)
shadeSceneContentViewModel.activateIn(testScope)
shadeSceneActionsViewModel.activateIn(testScope)
+ bouncerSceneContentViewModel.activateIn(testScope)
+ sceneContainerViewModel.activateIn(testScope)
assertWithMessage("Initial scene key mismatch!")
.that(sceneContainerViewModel.currentScene.value)
@@ -397,7 +400,7 @@
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
emulateUserDrivenTransition(to = upDestinationSceneKey)
- val bouncerActionButton by collectLastValue(bouncerViewModel.actionButton)
+ val bouncerActionButton by collectLastValue(bouncerSceneContentViewModel.actionButton)
assertWithMessage("Bouncer action button not visible")
.that(bouncerActionButton)
.isNotNull()
@@ -417,7 +420,7 @@
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
emulateUserDrivenTransition(to = upDestinationSceneKey)
- val bouncerActionButton by collectLastValue(bouncerViewModel.actionButton)
+ val bouncerActionButton by collectLastValue(bouncerSceneContentViewModel.actionButton)
assertWithMessage("Bouncer action button not visible during call")
.that(bouncerActionButton)
.isNotNull()
@@ -568,7 +571,7 @@
bouncerSceneJob =
if (to == Scenes.Bouncer) {
testScope.backgroundScope.launch {
- bouncerViewModel.authMethodViewModel.collect {
+ bouncerSceneContentViewModel.authMethodViewModel.collect {
// Do nothing. Need this to turn this otherwise cold flow, hot.
}
}
@@ -644,7 +647,8 @@
assertWithMessage("Cannot enter PIN when not on the Bouncer scene!")
.that(getCurrentSceneInUi())
.isEqualTo(Scenes.Bouncer)
- val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel)
+ val authMethodViewModel by
+ collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
.that(authMethodViewModel)
.isInstanceOf(PinBouncerViewModel::class.java)
@@ -672,7 +676,8 @@
assertWithMessage("Cannot enter PIN when not on the Bouncer scene!")
.that(getCurrentSceneInUi())
.isEqualTo(Scenes.Bouncer)
- val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel)
+ val authMethodViewModel by
+ collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
.that(authMethodViewModel)
.isInstanceOf(PinBouncerViewModel::class.java)
@@ -719,7 +724,7 @@
/** Emulates the dismissal of the IME (soft keyboard). */
private fun TestScope.dismissIme() {
- (bouncerViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
+ (bouncerSceneContentViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
it.onImeDismissed()
runCurrent()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index ea95aab..f85823a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.scene.ui.viewmodel
import android.view.MotionEvent
@@ -25,6 +27,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -37,6 +40,8 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -57,6 +62,9 @@
private lateinit var underTest: SceneContainerViewModel
+ private lateinit var activationJob: Job
+ private var motionEventHandler: SceneContainerViewModel.MotionEventHandler? = null
+
@Before
fun setUp() {
underTest =
@@ -64,10 +72,28 @@
sceneInteractor = sceneInteractor,
falsingInteractor = kosmos.falsingInteractor,
powerInteractor = kosmos.powerInteractor,
+ motionEventHandlerReceiver = { motionEventHandler ->
+ this@SceneContainerViewModelTest.motionEventHandler = motionEventHandler
+ },
)
+ activationJob = Job()
+ underTest.activateIn(testScope, activationJob)
}
@Test
+ fun activate_setsMotionEventHandler() =
+ testScope.runTest { assertThat(motionEventHandler).isNotNull() }
+
+ @Test
+ fun deactivate_clearsMotionEventHandler() =
+ testScope.runTest {
+ activationJob.cancel()
+ runCurrent()
+
+ assertThat(motionEventHandler).isNull()
+ }
+
+ @Test
fun isVisible() =
testScope.runTest {
val isVisible by collectLastValue(underTest.isVisible)
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 57fd9ea..d069c01 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -71,7 +71,7 @@
<string name="kg_prompt_after_dpm_lock" msgid="6002804765868345917">"لمزيد من الأمان، تم قفل الجهاز وفقًا لسياسة العمل."</string>
<string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"يجب إدخال رقم التعريف الشخصي بعد إلغاء الفتح الذكي."</string>
<string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"يجب إدخال كلمة المرور بعد إلغاء الفتح الذكي."</string>
- <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"يجب رسم النقش بعد إلغاء التأمين."</string>
+ <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"يجب رسم النقش بعد إلغاء الفتح الذكي."</string>
<string name="kg_prompt_unattended_update" msgid="4366635751738712452">"سيتم تثبيت التحديث عندما لا يكون الجهاز قيد الاستخدام."</string>
<string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"يجب تعزيز الأمان. لم يُستخدَم رقم PIN لبعض الوقت."</string>
<string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"يجب تعزيز الأمان. لم تستخدَم كلمة المرور لبعض الوقت."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index fd90d08..cf2057c 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -57,7 +57,7 @@
<string name="kg_wrong_pin" msgid="4160978845968732624">"Неверный PIN-код"</string>
<string name="kg_wrong_pin_try_again" msgid="3129729383303430190">"Неверный PIN-код."</string>
<string name="kg_wrong_input_try_fp_suggestion" msgid="3143861542242024833">"Повторите попытку или используйте отпечаток пальца."</string>
- <string name="kg_fp_not_recognized" msgid="5183108260932029241">"Отпечаток не распознан."</string>
+ <string name="kg_fp_not_recognized" msgid="5183108260932029241">"Отпечаток не распознан"</string>
<string name="bouncer_face_not_recognized" msgid="1666128054475597485">"Лицо не распознано."</string>
<string name="kg_bio_try_again_or_pin" msgid="4752168242723808390">"Повторите попытку или введите PIN-код."</string>
<string name="kg_bio_try_again_or_password" msgid="1473132729225398039">"Повторите попытку или введите пароль."</string>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 63ad41a..13cd2c5 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -53,6 +53,7 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.wm.shell.animation.FlingAnimationUtils;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.animation.PhysicsAnimator.SpringConfig;
@@ -63,13 +64,6 @@
public class SwipeHelper implements Gefingerpoken, Dumpable {
static final String TAG = "com.android.systemui.SwipeHelper";
private static final boolean DEBUG_INVALIDATE = false;
- private static final boolean CONSTRAIN_SWIPE = true;
- private static final boolean FADE_OUT_DURING_SWIPE = true;
- private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
-
- public static final int X = 0;
- public static final int Y = 1;
-
private static final float SWIPE_ESCAPE_VELOCITY = 500f; // dp/sec
private static final int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
private static final int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
@@ -171,10 +165,6 @@
mPagingTouchSlop = pagingTouchSlop;
}
- public void setDisableHardwareLayers(boolean disableHwLayers) {
- mDisableHwLayers = disableHwLayers;
- }
-
private float getPos(MotionEvent ev) {
return ev.getX();
}
@@ -253,13 +243,14 @@
float translation) {
float swipeProgress = getSwipeProgressForOffset(animView, translation);
if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) {
- if (FADE_OUT_DURING_SWIPE && dismissable) {
- if (!mDisableHwLayers) {
- if (swipeProgress != 0f && swipeProgress != 1f) {
- animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- } else {
- animView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
+ if (dismissable
+ || (NotificationContentAlphaOptimization.isEnabled() && translation == 0)) {
+ // We need to reset the content alpha even when the view is not dismissible (eg.
+ // when Guts is visible)
+ if (swipeProgress != 0f && swipeProgress != 1f) {
+ animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ } else {
+ animView.setLayerType(View.LAYER_TYPE_NONE, null);
}
updateSwipeProgressAlpha(animView, getSwipeAlpha(swipeProgress));
}
@@ -436,9 +427,7 @@
duration = fixedDuration;
}
- if (!mDisableHwLayers) {
- animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
+ animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
@@ -493,9 +482,7 @@
if (endAction != null) {
endAction.accept(mCancelled);
}
- if (!mDisableHwLayers) {
- animView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
+ animView.setLayerType(View.LAYER_TYPE_NONE, null);
onDismissChildWithAnimationFinished();
}
});
@@ -612,7 +599,11 @@
* view is being animated to dismiss or snap.
*/
public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) {
- updateSwipeProgressFromOffset(animView, canBeDismissed, value);
+ updateSwipeProgressFromOffset(
+ animView,
+ /* dismissable= */ canBeDismissed,
+ /* translation= */ value
+ );
}
private void snapChildInstantly(final View view) {
@@ -689,7 +680,7 @@
} else {
// don't let items that can't be dismissed be dragged more than
// maxScrollDistance
- if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(
+ if (!mCallback.canChildBeDismissedInDirection(
mTouchedView,
delta > 0)) {
float size = getSize(mTouchedView);
@@ -761,8 +752,7 @@
protected boolean swipedFarEnough() {
float translation = getTranslation(mTouchedView);
- return DISMISS_IF_SWIPED_FAR_ENOUGH
- && Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(
+ return Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(
mTouchedView);
}
@@ -822,9 +812,18 @@
}
public void forceResetSwipeState(@NonNull View view) {
- if (view.getTranslationX() == 0) return;
+ if (view.getTranslationX() == 0
+ && (!NotificationContentAlphaOptimization.isEnabled() || view.getAlpha() == 1f)
+ ) {
+ // Don't do anything when translation is 0 and alpha is 1
+ return;
+ }
setTranslation(view, 0);
- updateSwipeProgressFromOffset(view, /* dismissable= */ true, 0);
+ updateSwipeProgressFromOffset(
+ view,
+ /* dismissable= */ true,
+ /* translation= */ 0
+ );
}
/** This method resets the swipe state, and if `resetAll` is true, also resets the snap state */
@@ -893,7 +892,6 @@
pw.append("mTranslation=").println(mTranslation);
pw.append("mCanCurrViewBeDimissed=").println(mCanCurrViewBeDimissed);
pw.append("mMenuRowIntercepting=").println(mMenuRowIntercepting);
- pw.append("mDisableHwLayers=").println(mDisableHwLayers);
pw.append("mDismissPendingMap: ").println(mDismissPendingMap.size());
if (!mDismissPendingMap.isEmpty()) {
mDismissPendingMap.forEach((view, animator) -> {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 3828f9f..f0483a5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -112,6 +112,7 @@
MagnificationConstants.SCALE_MAX_VALUE);
private static final float A11Y_CHANGE_SCALE_DIFFERENCE = 1.0f;
private static final float ANIMATION_BOUNCE_EFFECT_SCALE = 1.05f;
+ private static final float[] COLOR_BLACK_ARRAY = {0f, 0f, 0f};
private final SparseArray<Float> mMagnificationSizeScaleOptions = new SparseArray<>();
private final Context mContext;
@@ -1019,6 +1020,11 @@
if (!mMirrorSurface.isValid()) {
return;
}
+ // Set the surface of the SurfaceView to black to avoid users seeing the contents below the
+ // magnifier when the mirrored surface has an alpha less than 1.
+ if (Flags.addBlackBackgroundForWindowMagnifier()) {
+ mTransaction.setColor(mMirrorSurfaceView.getSurfaceControl(), COLOR_BLACK_ARRAY);
+ }
mTransaction.show(mMirrorSurface)
.reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl());
modifyWindowMagnification(false);
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt
index aebc50f..3410782 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt
@@ -18,8 +18,6 @@
import android.app.AlertDialog
import android.content.Context
-import com.android.systemui.bouncer.ui.viewmodel.BouncerMessageViewModelModule
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModelModule
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -27,13 +25,7 @@
import dagger.Module
import dagger.Provides
-@Module(
- includes =
- [
- BouncerViewModelModule::class,
- BouncerMessageViewModelModule::class,
- ],
-)
+@Module
interface BouncerViewModule {
/** Binds BouncerView to BouncerViewImpl and makes it injectable. */
@Binds fun bindBouncerView(bouncerViewImpl: BouncerViewImpl): BouncerView
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index 78811a9..ad93a25 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -9,7 +9,7 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
@@ -40,7 +40,7 @@
@Inject
constructor(
val legacyInteractor: PrimaryBouncerInteractor,
- val viewModel: BouncerViewModel,
+ val viewModelFactory: BouncerSceneContentViewModel.Factory,
val dialogFactory: BouncerDialogFactory,
val authenticationInteractor: AuthenticationInteractor,
val viewMediatorCallback: ViewMediatorCallback?,
@@ -65,7 +65,7 @@
ComposeBouncerViewBinder.bind(
view,
deps.legacyInteractor,
- deps.viewModel,
+ deps.viewModelFactory,
deps.dialogFactory,
deps.authenticationInteractor,
deps.selectedUserInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
index eaca276..c1f7d59 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
@@ -14,7 +14,8 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.composable.BouncerContent
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
+import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import kotlinx.coroutines.flow.collectLatest
@@ -25,7 +26,7 @@
fun bind(
view: ViewGroup,
legacyInteractor: PrimaryBouncerInteractor,
- viewModel: BouncerViewModel,
+ viewModelFactory: BouncerSceneContentViewModel.Factory,
dialogFactory: BouncerDialogFactory,
authenticationInteractor: AuthenticationInteractor,
selectedUserInteractor: SelectedUserInteractor,
@@ -48,7 +49,14 @@
this@repeatWhenAttached.lifecycle
}
)
- setContent { PlatformTheme { BouncerContent(viewModel, dialogFactory) } }
+ setContent {
+ PlatformTheme {
+ BouncerContent(
+ rememberViewModel { viewModelFactory.create() },
+ dialogFactory,
+ )
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 4fbf735..e7dd974 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -17,17 +17,18 @@
package com.android.systemui.bouncer.ui.viewmodel
import android.annotation.StringRes
+import com.android.app.tracing.coroutines.flow.collectLatest
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import kotlinx.coroutines.CoroutineScope
+import com.android.systemui.lifecycle.SysUiViewModel
+import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.receiveAsFlow
sealed class AuthMethodBouncerViewModel(
- protected val viewModelScope: CoroutineScope,
protected val interactor: BouncerInteractor,
/**
@@ -37,7 +38,7 @@
* being able to attempt to unlock the device.
*/
val isInputEnabled: StateFlow<Boolean>,
-) {
+) : SysUiViewModel() {
private val _animateFailure = MutableStateFlow(false)
/**
@@ -57,6 +58,29 @@
*/
@get:StringRes abstract val lockoutMessageId: Int
+ private val authenticationRequests = Channel<AuthenticationRequest>(Channel.BUFFERED)
+
+ override suspend fun onActivated() {
+ authenticationRequests.receiveAsFlow().collectLatest { request ->
+ if (!isInputEnabled.value) {
+ return@collectLatest
+ }
+
+ val authenticationResult =
+ interactor.authenticate(
+ input = request.input,
+ tryAutoConfirm = request.useAutoConfirm,
+ )
+
+ if (authenticationResult == AuthenticationResult.SKIPPED && request.useAutoConfirm) {
+ return@collectLatest
+ }
+
+ _animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED
+ clearInput()
+ }
+ }
+
/**
* Notifies that the UI has been hidden from the user (after any transitions have completed).
*/
@@ -92,14 +116,11 @@
input: List<Any> = getInput(),
useAutoConfirm: Boolean = false,
) {
- viewModelScope.launch {
- val authenticationResult = interactor.authenticate(input, useAutoConfirm)
- if (authenticationResult == AuthenticationResult.SKIPPED && useAutoConfirm) {
- return@launch
- }
- _animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED
-
- clearInput()
- }
+ authenticationRequests.trySend(AuthenticationRequest(input, useAutoConfirm))
}
+
+ private data class AuthenticationRequest(
+ val input: List<Any>,
+ val useAutoConfirm: Boolean,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
index 31479f1..c3215b4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
@@ -27,7 +27,6 @@
import com.android.systemui.bouncer.shared.model.BouncerMessageStrings
import com.android.systemui.bouncer.shared.model.primaryMessage
import com.android.systemui.bouncer.shared.model.secondaryMessage
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.BiometricMessageInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
@@ -39,19 +38,19 @@
import com.android.systemui.deviceentry.shared.model.FaceTimeoutMessage
import com.android.systemui.deviceentry.shared.model.FingerprintFailureMessage
import com.android.systemui.deviceentry.shared.model.FingerprintLockoutMessage
+import com.android.systemui.lifecycle.SysUiViewModel
import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
-import com.android.systemui.user.ui.viewmodel.UserViewModel
import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.time.SystemClock
-import dagger.Module
-import dagger.Provides
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlin.math.ceil
import kotlin.math.max
import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -65,20 +64,21 @@
/** Holds UI state for the 2-line status message shown on the bouncer. */
@OptIn(ExperimentalCoroutinesApi::class)
-class BouncerMessageViewModel(
+class BouncerMessageViewModel
+@AssistedInject
+constructor(
@Application private val applicationContext: Context,
- @Application private val applicationScope: CoroutineScope,
private val bouncerInteractor: BouncerInteractor,
private val simBouncerInteractor: SimBouncerInteractor,
private val authenticationInteractor: AuthenticationInteractor,
- selectedUser: Flow<UserViewModel>,
+ private val userSwitcherViewModel: UserSwitcherViewModel,
private val clock: SystemClock,
private val biometricMessageInteractor: BiometricMessageInteractor,
private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
private val deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
- flags: ComposeBouncerFlags,
-) {
+ private val flags: ComposeBouncerFlags,
+) : SysUiViewModel() {
/**
* A message shown when the user has attempted the wrong credential too many times and now must
* wait a while before attempting to authenticate again.
@@ -94,6 +94,25 @@
/** The user-facing message to show in the bouncer. */
val message: MutableStateFlow<MessageViewModel?> = MutableStateFlow(null)
+ override suspend fun onActivated() {
+ if (!flags.isComposeBouncerOrSceneContainerEnabled()) {
+ return
+ }
+
+ coroutineScope {
+ launch {
+ // Update the lockout countdown whenever the selected user is switched.
+ userSwitcherViewModel.selectedUser.collect { startLockoutCountdown() }
+ }
+
+ launch { defaultBouncerMessageInitializer() }
+ launch { listenForSimBouncerEvents() }
+ launch { listenForBouncerEvents() }
+ launch { listenForFaceMessages() }
+ launch { listenForFingerprintMessages() }
+ }
+ }
+
/** Initializes the bouncer message to default whenever it is shown. */
fun onShown() {
showDefaultMessage()
@@ -108,173 +127,161 @@
private var lockoutCountdownJob: Job? = null
- private fun defaultBouncerMessageInitializer() {
- applicationScope.launch {
- resetToDefault.emit(Unit)
- authenticationInteractor.authenticationMethod
- .flatMapLatest { authMethod ->
- if (authMethod == AuthenticationMethodModel.Sim) {
- resetToDefault.map {
- MessageViewModel(simBouncerInteractor.getDefaultMessage())
- }
- } else if (authMethod.isSecure) {
- combine(
- deviceUnlockedInteractor.deviceEntryRestrictionReason,
- lockoutMessage,
- deviceEntryBiometricsAllowedInteractor
- .isFingerprintCurrentlyAllowedOnBouncer,
- resetToDefault,
- ) { deviceEntryRestrictedReason, lockoutMsg, isFpAllowedInBouncer, _ ->
- lockoutMsg
- ?: deviceEntryRestrictedReason.toMessage(
- authMethod,
- isFpAllowedInBouncer
- )
- }
- } else {
- emptyFlow()
+ private suspend fun defaultBouncerMessageInitializer() {
+ resetToDefault.emit(Unit)
+ authenticationInteractor.authenticationMethod
+ .flatMapLatest { authMethod ->
+ if (authMethod == AuthenticationMethodModel.Sim) {
+ resetToDefault.map {
+ MessageViewModel(simBouncerInteractor.getDefaultMessage())
}
+ } else if (authMethod.isSecure) {
+ combine(
+ deviceUnlockedInteractor.deviceEntryRestrictionReason,
+ lockoutMessage,
+ deviceEntryBiometricsAllowedInteractor
+ .isFingerprintCurrentlyAllowedOnBouncer,
+ resetToDefault,
+ ) { deviceEntryRestrictedReason, lockoutMsg, isFpAllowedInBouncer, _ ->
+ lockoutMsg
+ ?: deviceEntryRestrictedReason.toMessage(
+ authMethod,
+ isFpAllowedInBouncer
+ )
+ }
+ } else {
+ emptyFlow()
}
- .collectLatest { messageViewModel -> message.value = messageViewModel }
- }
+ }
+ .collectLatest { messageViewModel -> message.value = messageViewModel }
}
- private fun listenForSimBouncerEvents() {
+ private suspend fun listenForSimBouncerEvents() {
// Listen for any events from the SIM bouncer and update the message shown on the bouncer.
- applicationScope.launch {
- authenticationInteractor.authenticationMethod
- .flatMapLatest { authMethod ->
- if (authMethod == AuthenticationMethodModel.Sim) {
- simBouncerInteractor.bouncerMessageChanged.map { simMsg ->
- simMsg?.let { MessageViewModel(it) }
- }
- } else {
- emptyFlow()
+ authenticationInteractor.authenticationMethod
+ .flatMapLatest { authMethod ->
+ if (authMethod == AuthenticationMethodModel.Sim) {
+ simBouncerInteractor.bouncerMessageChanged.map { simMsg ->
+ simMsg?.let { MessageViewModel(it) }
}
+ } else {
+ emptyFlow()
}
- .collectLatest {
- if (it != null) {
- message.value = it
- } else {
- resetToDefault.emit(Unit)
- }
+ }
+ .collectLatest {
+ if (it != null) {
+ message.value = it
+ } else {
+ resetToDefault.emit(Unit)
}
- }
+ }
}
- private fun listenForFaceMessages() {
+ private suspend fun listenForFaceMessages() {
// Listen for any events from face authentication and update the message shown on the
// bouncer.
- applicationScope.launch {
- biometricMessageInteractor.faceMessage
- .sample(
- authenticationInteractor.authenticationMethod,
- deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer,
- )
- .collectLatest { (faceMessage, authMethod, fingerprintAllowedOnBouncer) ->
- val isFaceAuthStrong = faceAuthInteractor.isFaceAuthStrong()
- val defaultPrimaryMessage =
- BouncerMessageStrings.defaultMessage(
- authMethod,
- fingerprintAllowedOnBouncer
+ biometricMessageInteractor.faceMessage
+ .sample(
+ authenticationInteractor.authenticationMethod,
+ deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer,
+ )
+ .collectLatest { (faceMessage, authMethod, fingerprintAllowedOnBouncer) ->
+ val isFaceAuthStrong = faceAuthInteractor.isFaceAuthStrong()
+ val defaultPrimaryMessage =
+ BouncerMessageStrings.defaultMessage(authMethod, fingerprintAllowedOnBouncer)
+ .primaryMessage
+ .toResString()
+ message.value =
+ when (faceMessage) {
+ is FaceTimeoutMessage ->
+ MessageViewModel(
+ text = defaultPrimaryMessage,
+ secondaryText = faceMessage.message,
+ isUpdateAnimated = true
)
- .primaryMessage
- .toResString()
- message.value =
- when (faceMessage) {
- is FaceTimeoutMessage ->
- MessageViewModel(
- text = defaultPrimaryMessage,
- secondaryText = faceMessage.message,
- isUpdateAnimated = true
- )
- is FaceLockoutMessage ->
- if (isFaceAuthStrong)
- BouncerMessageStrings.class3AuthLockedOut(authMethod)
- .toMessage()
- else
- BouncerMessageStrings.faceLockedOut(
- authMethod,
- fingerprintAllowedOnBouncer
- )
- .toMessage()
- is FaceFailureMessage ->
- BouncerMessageStrings.incorrectFaceInput(
+ is FaceLockoutMessage ->
+ if (isFaceAuthStrong)
+ BouncerMessageStrings.class3AuthLockedOut(authMethod).toMessage()
+ else
+ BouncerMessageStrings.faceLockedOut(
authMethod,
fingerprintAllowedOnBouncer
)
.toMessage()
- else ->
- MessageViewModel(
- text = defaultPrimaryMessage,
- secondaryText = faceMessage.message,
- isUpdateAnimated = false
+ is FaceFailureMessage ->
+ BouncerMessageStrings.incorrectFaceInput(
+ authMethod,
+ fingerprintAllowedOnBouncer
)
- }
- delay(MESSAGE_DURATION)
- resetToDefault.emit(Unit)
- }
- }
- }
-
- private fun listenForFingerprintMessages() {
- applicationScope.launch {
- // Listen for any events from fingerprint authentication and update the message shown
- // on the bouncer.
- biometricMessageInteractor.fingerprintMessage
- .sample(
- authenticationInteractor.authenticationMethod,
- deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
- )
- .collectLatest { (fingerprintMessage, authMethod, isFingerprintAllowed) ->
- val defaultPrimaryMessage =
- BouncerMessageStrings.defaultMessage(authMethod, isFingerprintAllowed)
- .primaryMessage
- .toResString()
- message.value =
- when (fingerprintMessage) {
- is FingerprintLockoutMessage ->
- BouncerMessageStrings.class3AuthLockedOut(authMethod).toMessage()
- is FingerprintFailureMessage ->
- BouncerMessageStrings.incorrectFingerprintInput(authMethod)
- .toMessage()
- else ->
- MessageViewModel(
- text = defaultPrimaryMessage,
- secondaryText = fingerprintMessage.message,
- isUpdateAnimated = false
- )
- }
- delay(MESSAGE_DURATION)
- resetToDefault.emit(Unit)
- }
- }
- }
-
- private fun listenForBouncerEvents() {
- // Keeps the lockout message up-to-date.
- applicationScope.launch {
- bouncerInteractor.onLockoutStarted.collect { startLockoutCountdown() }
- }
-
- // Listens to relevant bouncer events
- applicationScope.launch {
- bouncerInteractor.onIncorrectBouncerInput
- .sample(
- authenticationInteractor.authenticationMethod,
- deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
- )
- .collectLatest { (_, authMethod, isFingerprintAllowed) ->
- message.emit(
- BouncerMessageStrings.incorrectSecurityInput(
- authMethod,
- isFingerprintAllowed
+ .toMessage()
+ else ->
+ MessageViewModel(
+ text = defaultPrimaryMessage,
+ secondaryText = faceMessage.message,
+ isUpdateAnimated = false
)
- .toMessage()
+ }
+ delay(MESSAGE_DURATION)
+ resetToDefault.emit(Unit)
+ }
+ }
+
+ private suspend fun listenForFingerprintMessages() {
+ // Listen for any events from fingerprint authentication and update the message shown
+ // on the bouncer.
+ biometricMessageInteractor.fingerprintMessage
+ .sample(
+ authenticationInteractor.authenticationMethod,
+ deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
+ )
+ .collectLatest { (fingerprintMessage, authMethod, isFingerprintAllowed) ->
+ val defaultPrimaryMessage =
+ BouncerMessageStrings.defaultMessage(authMethod, isFingerprintAllowed)
+ .primaryMessage
+ .toResString()
+ message.value =
+ when (fingerprintMessage) {
+ is FingerprintLockoutMessage ->
+ BouncerMessageStrings.class3AuthLockedOut(authMethod).toMessage()
+ is FingerprintFailureMessage ->
+ BouncerMessageStrings.incorrectFingerprintInput(authMethod).toMessage()
+ else ->
+ MessageViewModel(
+ text = defaultPrimaryMessage,
+ secondaryText = fingerprintMessage.message,
+ isUpdateAnimated = false
+ )
+ }
+ delay(MESSAGE_DURATION)
+ resetToDefault.emit(Unit)
+ }
+ }
+
+ private suspend fun listenForBouncerEvents() {
+ coroutineScope {
+ // Keeps the lockout message up-to-date.
+ launch { bouncerInteractor.onLockoutStarted.collect { startLockoutCountdown() } }
+
+ // Listens to relevant bouncer events
+ launch {
+ bouncerInteractor.onIncorrectBouncerInput
+ .sample(
+ authenticationInteractor.authenticationMethod,
+ deviceEntryBiometricsAllowedInteractor
+ .isFingerprintCurrentlyAllowedOnBouncer
)
- delay(MESSAGE_DURATION)
- resetToDefault.emit(Unit)
- }
+ .collectLatest { (_, authMethod, isFingerprintAllowed) ->
+ message.emit(
+ BouncerMessageStrings.incorrectSecurityInput(
+ authMethod,
+ isFingerprintAllowed
+ )
+ .toMessage()
+ )
+ delay(MESSAGE_DURATION)
+ resetToDefault.emit(Unit)
+ }
+ }
}
}
@@ -323,10 +330,10 @@
}
/** Shows the countdown message and refreshes it every second. */
- private fun startLockoutCountdown() {
+ private suspend fun startLockoutCountdown() {
lockoutCountdownJob?.cancel()
- lockoutCountdownJob =
- applicationScope.launch {
+ lockoutCountdownJob = coroutineScope {
+ launch {
authenticationInteractor.authenticationMethod.collectLatest { authMethod ->
do {
val remainingSeconds = remainingLockoutSeconds()
@@ -352,6 +359,7 @@
lockoutCountdownJob = null
}
}
+ }
}
private fun remainingLockoutSeconds(): Int {
@@ -365,20 +373,9 @@
private fun Int.toResString(): String = applicationContext.getString(this)
- init {
- if (flags.isComposeBouncerOrSceneContainerEnabled()) {
- applicationScope.launch {
- // Update the lockout countdown whenever the selected user is switched.
- selectedUser.collect { startLockoutCountdown() }
- }
-
- defaultBouncerMessageInitializer()
-
- listenForSimBouncerEvents()
- listenForBouncerEvents()
- listenForFaceMessages()
- listenForFingerprintMessages()
- }
+ @AssistedFactory
+ interface Factory {
+ fun create(): BouncerMessageViewModel
}
companion object {
@@ -398,40 +395,3 @@
*/
val isUpdateAnimated: Boolean = true,
)
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@Module
-object BouncerMessageViewModelModule {
-
- @Provides
- @SysUISingleton
- fun viewModel(
- @Application applicationContext: Context,
- @Application applicationScope: CoroutineScope,
- bouncerInteractor: BouncerInteractor,
- simBouncerInteractor: SimBouncerInteractor,
- authenticationInteractor: AuthenticationInteractor,
- clock: SystemClock,
- biometricMessageInteractor: BiometricMessageInteractor,
- faceAuthInteractor: DeviceEntryFaceAuthInteractor,
- deviceUnlockedInteractor: DeviceUnlockedInteractor,
- deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
- flags: ComposeBouncerFlags,
- userSwitcherViewModel: UserSwitcherViewModel,
- ): BouncerMessageViewModel {
- return BouncerMessageViewModel(
- applicationContext = applicationContext,
- applicationScope = applicationScope,
- bouncerInteractor = bouncerInteractor,
- simBouncerInteractor = simBouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- clock = clock,
- biometricMessageInteractor = biometricMessageInteractor,
- faceAuthInteractor = faceAuthInteractor,
- deviceUnlockedInteractor = deviceUnlockedInteractor,
- deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
- flags = flags,
- selectedUser = userSwitcherViewModel.selectedUser,
- )
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt
new file mode 100644
index 0000000..2a27271
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.ui.viewmodel
+
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.map
+
+/**
+ * Models UI state for user actions that can lead to navigation to other scenes when showing the
+ * bouncer scene.
+ */
+class BouncerSceneActionsViewModel
+@AssistedInject
+constructor(
+ private val bouncerInteractor: BouncerInteractor,
+) : SceneActionsViewModel() {
+
+ override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+ bouncerInteractor.dismissDestination
+ .map { prevScene ->
+ mapOf(
+ Back to UserActionResult(prevScene),
+ Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
+ )
+ }
+ .collectLatest { actions -> setActions(actions) }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): BouncerSceneActionsViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
new file mode 100644
index 0000000..aede63b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.ui.viewmodel
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResources
+import android.content.Context
+import android.graphics.Bitmap
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.type
+import androidx.core.graphics.drawable.toBitmap
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
+import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
+import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.lifecycle.SysUiViewModel
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/** Models UI state for the content of the bouncer scene. */
+class BouncerSceneContentViewModel
+@AssistedInject
+constructor(
+ @Application private val applicationContext: Context,
+ private val bouncerInteractor: BouncerInteractor,
+ private val authenticationInteractor: AuthenticationInteractor,
+ private val devicePolicyManager: DevicePolicyManager,
+ private val bouncerMessageViewModelFactory: BouncerMessageViewModel.Factory,
+ private val flags: ComposeBouncerFlags,
+ private val userSwitcher: UserSwitcherViewModel,
+ private val actionButtonInteractor: BouncerActionButtonInteractor,
+ private val pinViewModelFactory: PinBouncerViewModel.Factory,
+ private val patternViewModelFactory: PatternBouncerViewModel.Factory,
+ private val passwordViewModelFactory: PasswordBouncerViewModel.Factory,
+) : SysUiViewModel() {
+ private val _selectedUserImage = MutableStateFlow<Bitmap?>(null)
+ val selectedUserImage: StateFlow<Bitmap?> = _selectedUserImage.asStateFlow()
+
+ val message: BouncerMessageViewModel by lazy { bouncerMessageViewModelFactory.create() }
+
+ private val _userSwitcherDropdown =
+ MutableStateFlow<List<UserSwitcherDropdownItemViewModel>>(emptyList())
+ val userSwitcherDropdown: StateFlow<List<UserSwitcherDropdownItemViewModel>> =
+ _userSwitcherDropdown.asStateFlow()
+
+ val isUserSwitcherVisible: Boolean
+ get() = bouncerInteractor.isUserSwitcherVisible
+
+ /** View-model for the current UI, based on the current authentication method. */
+ private val _authMethodViewModel = MutableStateFlow<AuthMethodBouncerViewModel?>(null)
+ val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> =
+ _authMethodViewModel.asStateFlow()
+
+ /**
+ * A message for a dialog to show when the user has attempted the wrong credential too many
+ * times and now must wait a while before attempting again.
+ *
+ * If `null`, the lockout dialog should not be shown.
+ */
+ private val lockoutDialogMessage = MutableStateFlow<String?>(null)
+
+ /**
+ * A message for a dialog to show when the user has attempted the wrong credential too many
+ * times and their user/profile/device data is at risk of being wiped due to a Device Manager
+ * policy.
+ *
+ * If `null`, the wipe dialog should not be shown.
+ */
+ private val wipeDialogMessage = MutableStateFlow<String?>(null)
+
+ private val _dialogViewModel = MutableStateFlow<DialogViewModel?>(createDialogViewModel())
+ /**
+ * Models the dialog to be shown to the user, or `null` if no dialog should be shown.
+ *
+ * Once the dialog is shown, the UI should call [DialogViewModel.onDismiss] when the user
+ * dismisses this dialog.
+ */
+ val dialogViewModel: StateFlow<DialogViewModel?> = _dialogViewModel.asStateFlow()
+
+ private val _actionButton = MutableStateFlow<BouncerActionButtonModel?>(null)
+ /**
+ * The bouncer action button (Return to Call / Emergency Call). If `null`, the button should not
+ * be shown.
+ */
+ val actionButton: StateFlow<BouncerActionButtonModel?> = _actionButton.asStateFlow()
+
+ private val _isSideBySideSupported =
+ MutableStateFlow(isSideBySideSupported(authMethodViewModel.value))
+ /**
+ * Whether the "side-by-side" layout is supported.
+ *
+ * When presented on its own, without a user switcher (e.g. not on communal devices like
+ * tablets, for example), some authentication method UIs don't do well if they're shown in the
+ * side-by-side layout; these need to be shown with the standard layout so they can take up as
+ * much width as possible.
+ */
+ val isSideBySideSupported: StateFlow<Boolean> = _isSideBySideSupported.asStateFlow()
+
+ private val _isFoldSplitRequired =
+ MutableStateFlow(isFoldSplitRequired(authMethodViewModel.value))
+ /**
+ * Whether the splitting the UI around the fold seam (where the hinge is on a foldable device)
+ * is required.
+ */
+ val isFoldSplitRequired: StateFlow<Boolean> = _isFoldSplitRequired.asStateFlow()
+
+ private val _isInputEnabled =
+ MutableStateFlow(authenticationInteractor.lockoutEndTimestamp == null)
+ private val isInputEnabled: StateFlow<Boolean> = _isInputEnabled.asStateFlow()
+
+ override suspend fun onActivated() {
+ coroutineScope {
+ launch { message.activate() }
+ launch {
+ authenticationInteractor.authenticationMethod
+ .map(::getChildViewModel)
+ .collectLatest { childViewModelOrNull ->
+ _authMethodViewModel.value = childViewModelOrNull
+ childViewModelOrNull?.activate()
+ }
+ }
+
+ launch {
+ authenticationInteractor.upcomingWipe.collect { wipeModel ->
+ wipeDialogMessage.value = wipeModel?.message
+ }
+ }
+
+ launch {
+ userSwitcher.selectedUser
+ .map { it.image.toBitmap() }
+ .collectLatest { _selectedUserImage.value = it }
+ }
+
+ launch {
+ combine(
+ userSwitcher.users,
+ userSwitcher.menu,
+ ) { users, actions ->
+ users.map { user ->
+ UserSwitcherDropdownItemViewModel(
+ icon = Icon.Loaded(user.image, contentDescription = null),
+ text = user.name,
+ onClick = user.onClicked ?: {},
+ )
+ } +
+ actions.map { action ->
+ UserSwitcherDropdownItemViewModel(
+ icon =
+ Icon.Resource(
+ action.iconResourceId,
+ contentDescription = null
+ ),
+ text = Text.Resource(action.textResourceId),
+ onClick = action.onClicked,
+ )
+ }
+ }
+ .collectLatest { _userSwitcherDropdown.value = it }
+ }
+
+ launch {
+ combine(wipeDialogMessage, lockoutDialogMessage) { _, _ -> createDialogViewModel() }
+ .collectLatest { _dialogViewModel.value = it }
+ }
+
+ launch {
+ actionButtonInteractor.actionButton.collectLatest { _actionButton.value = it }
+ }
+
+ launch {
+ authMethodViewModel
+ .map { authMethod -> isSideBySideSupported(authMethod) }
+ .collectLatest { _isSideBySideSupported.value = it }
+ }
+
+ launch {
+ authMethodViewModel
+ .map { authMethod -> isFoldSplitRequired(authMethod) }
+ .collectLatest { _isFoldSplitRequired.value = it }
+ }
+
+ launch {
+ message.isLockoutMessagePresent
+ .map { lockoutMessagePresent -> !lockoutMessagePresent }
+ .collectLatest { _isInputEnabled.value = it }
+ }
+ }
+ }
+
+ private fun isSideBySideSupported(authMethod: AuthMethodBouncerViewModel?): Boolean {
+ return isUserSwitcherVisible || authMethod !is PasswordBouncerViewModel
+ }
+
+ private fun isFoldSplitRequired(authMethod: AuthMethodBouncerViewModel?): Boolean {
+ return authMethod !is PasswordBouncerViewModel
+ }
+
+ private fun getChildViewModel(
+ authenticationMethod: AuthenticationMethodModel,
+ ): AuthMethodBouncerViewModel? {
+ // If the current child view-model matches the authentication method, reuse it instead of
+ // creating a new instance.
+ val childViewModel = authMethodViewModel.value
+ if (authenticationMethod == childViewModel?.authenticationMethod) {
+ return childViewModel
+ }
+
+ return when (authenticationMethod) {
+ is AuthenticationMethodModel.Pin ->
+ pinViewModelFactory.create(
+ authenticationMethod = authenticationMethod,
+ onIntentionalUserInput = ::onIntentionalUserInput,
+ isInputEnabled = isInputEnabled,
+ )
+ is AuthenticationMethodModel.Sim ->
+ pinViewModelFactory.create(
+ authenticationMethod = authenticationMethod,
+ onIntentionalUserInput = ::onIntentionalUserInput,
+ isInputEnabled = isInputEnabled,
+ )
+ is AuthenticationMethodModel.Password ->
+ passwordViewModelFactory.create(
+ onIntentionalUserInput = ::onIntentionalUserInput,
+ isInputEnabled = isInputEnabled,
+ )
+ is AuthenticationMethodModel.Pattern ->
+ patternViewModelFactory.create(
+ onIntentionalUserInput = ::onIntentionalUserInput,
+ isInputEnabled = isInputEnabled,
+ )
+ else -> null
+ }
+ }
+
+ private fun onIntentionalUserInput() {
+ message.showDefaultMessage()
+ bouncerInteractor.onIntentionalUserInput()
+ }
+
+ /**
+ * @return A message warning the user that the user/profile/device will be wiped upon a further
+ * [AuthenticationWipeModel.remainingAttempts] unsuccessful authentication attempts.
+ */
+ private fun AuthenticationWipeModel.getAlmostAtWipeMessage(): String {
+ val message =
+ applicationContext.getString(
+ wipeTarget.messageIdForAlmostWipe,
+ failedAttempts,
+ remainingAttempts,
+ )
+ return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
+ devicePolicyManager.resources.getString(
+ DevicePolicyResources.Strings.SystemUi
+ .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ALMOST_ERASING_PROFILE,
+ { message },
+ failedAttempts,
+ remainingAttempts,
+ ) ?: message
+ } else {
+ message
+ }
+ }
+
+ /**
+ * @return A message informing the user that their user/profile/device will be wiped promptly.
+ */
+ private fun AuthenticationWipeModel.getWipeMessage(): String {
+ val message = applicationContext.getString(wipeTarget.messageIdForWipe, failedAttempts)
+ return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
+ devicePolicyManager.resources.getString(
+ DevicePolicyResources.Strings.SystemUi
+ .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
+ { message },
+ failedAttempts,
+ ) ?: message
+ } else {
+ message
+ }
+ }
+
+ private val AuthenticationWipeModel.message: String
+ get() = if (remainingAttempts > 0) getAlmostAtWipeMessage() else getWipeMessage()
+
+ private fun createDialogViewModel(): DialogViewModel? {
+ val wipeText = wipeDialogMessage.value
+ val lockoutText = lockoutDialogMessage.value
+ return when {
+ // The wipe dialog takes priority over the lockout dialog.
+ wipeText != null ->
+ DialogViewModel(
+ text = wipeText,
+ onDismiss = { wipeDialogMessage.value = null },
+ )
+ lockoutText != null ->
+ DialogViewModel(
+ text = lockoutText,
+ onDismiss = { lockoutDialogMessage.value = null },
+ )
+ else -> null // No dialog to show.
+ }
+ }
+
+ /**
+ * Notifies that a key event has occurred.
+ *
+ * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
+ */
+ fun onKeyEvent(keyEvent: KeyEvent): Boolean {
+ return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent(
+ keyEvent.type,
+ keyEvent.nativeKeyEvent.keyCode
+ ) ?: false
+ }
+
+ data class DialogViewModel(
+ val text: String,
+
+ /** Callback to run after the dialog has been dismissed by the user. */
+ val onDismiss: () -> Unit,
+ )
+
+ data class UserSwitcherDropdownItemViewModel(
+ val icon: Icon,
+ val text: Text,
+ val onClick: () -> Unit,
+ )
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): BouncerSceneContentViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
deleted file mode 100644
index e2089bb..0000000
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bouncer.ui.viewmodel
-
-import android.app.admin.DevicePolicyManager
-import android.app.admin.DevicePolicyResources
-import android.content.Context
-import android.graphics.Bitmap
-import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.input.key.type
-import androidx.core.graphics.drawable.toBitmap
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
-import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
-import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
-import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.shared.model.Text
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.inputmethod.domain.interactor.InputMethodInteractor
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.user.ui.viewmodel.UserActionViewModel
-import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
-import com.android.systemui.user.ui.viewmodel.UserViewModel
-import dagger.Module
-import dagger.Provides
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.job
-import kotlinx.coroutines.launch
-
-/** Holds UI state and handles user input on bouncer UIs. */
-class BouncerViewModel(
- @Application private val applicationContext: Context,
- @Deprecated("TODO(b/354270224): remove this. Injecting CoroutineScope to view-models is banned")
- @Application
- private val applicationScope: CoroutineScope,
- @Main private val mainDispatcher: CoroutineDispatcher,
- private val bouncerInteractor: BouncerInteractor,
- private val inputMethodInteractor: InputMethodInteractor,
- private val simBouncerInteractor: SimBouncerInteractor,
- private val authenticationInteractor: AuthenticationInteractor,
- private val selectedUserInteractor: SelectedUserInteractor,
- private val devicePolicyManager: DevicePolicyManager,
- bouncerMessageViewModel: BouncerMessageViewModel,
- flags: ComposeBouncerFlags,
- selectedUser: Flow<UserViewModel>,
- users: Flow<List<UserViewModel>>,
- userSwitcherMenu: Flow<List<UserActionViewModel>>,
- actionButton: Flow<BouncerActionButtonModel?>,
-) {
- val selectedUserImage: StateFlow<Bitmap?> =
- selectedUser
- .map { it.image.toBitmap() }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = null,
- )
-
- val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- bouncerInteractor.dismissDestination.map { prevScene ->
- mapOf(
- Back to UserActionResult(prevScene),
- Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
- )
- }
-
- val message: BouncerMessageViewModel = bouncerMessageViewModel
-
- val userSwitcherDropdown: StateFlow<List<UserSwitcherDropdownItemViewModel>> =
- combine(
- users,
- userSwitcherMenu,
- ) { users, actions ->
- users.map { user ->
- UserSwitcherDropdownItemViewModel(
- icon = Icon.Loaded(user.image, contentDescription = null),
- text = user.name,
- onClick = user.onClicked ?: {},
- )
- } +
- actions.map { action ->
- UserSwitcherDropdownItemViewModel(
- icon = Icon.Resource(action.iconResourceId, contentDescription = null),
- text = Text.Resource(action.textResourceId),
- onClick = action.onClicked,
- )
- }
- }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = emptyList(),
- )
-
- val isUserSwitcherVisible: Boolean
- get() = bouncerInteractor.isUserSwitcherVisible
-
- // Handle to the scope of the child ViewModel (stored in [authMethod]).
- private var childViewModelScope: CoroutineScope? = null
-
- /** View-model for the current UI, based on the current authentication method. */
- val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> =
- authenticationInteractor.authenticationMethod
- .map(::getChildViewModel)
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = null,
- )
-
- /**
- * A message for a dialog to show when the user has attempted the wrong credential too many
- * times and now must wait a while before attempting again.
- *
- * If `null`, the lockout dialog should not be shown.
- */
- private val lockoutDialogMessage = MutableStateFlow<String?>(null)
-
- /**
- * A message for a dialog to show when the user has attempted the wrong credential too many
- * times and their user/profile/device data is at risk of being wiped due to a Device Manager
- * policy.
- *
- * If `null`, the wipe dialog should not be shown.
- */
- private val wipeDialogMessage = MutableStateFlow<String?>(null)
-
- /**
- * Models the dialog to be shown to the user, or `null` if no dialog should be shown.
- *
- * Once the dialog is shown, the UI should call [DialogViewModel.onDismiss] when the user
- * dismisses this dialog.
- */
- val dialogViewModel: StateFlow<DialogViewModel?> =
- combine(wipeDialogMessage, lockoutDialogMessage) { _, _ -> createDialogViewModel() }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = createDialogViewModel(),
- )
-
- /**
- * The bouncer action button (Return to Call / Emergency Call). If `null`, the button should not
- * be shown.
- */
- val actionButton: StateFlow<BouncerActionButtonModel?> =
- actionButton.stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = null
- )
-
- /**
- * Whether the "side-by-side" layout is supported.
- *
- * When presented on its own, without a user switcher (e.g. not on communal devices like
- * tablets, for example), some authentication method UIs don't do well if they're shown in the
- * side-by-side layout; these need to be shown with the standard layout so they can take up as
- * much width as possible.
- */
- val isSideBySideSupported: StateFlow<Boolean> =
- authMethodViewModel
- .map { authMethod -> isSideBySideSupported(authMethod) }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = isSideBySideSupported(authMethodViewModel.value),
- )
-
- /**
- * Whether the splitting the UI around the fold seam (where the hinge is on a foldable device)
- * is required.
- */
- val isFoldSplitRequired: StateFlow<Boolean> =
- authMethodViewModel
- .map { authMethod -> isFoldSplitRequired(authMethod) }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = isFoldSplitRequired(authMethodViewModel.value),
- )
-
- private val isInputEnabled: StateFlow<Boolean> =
- bouncerMessageViewModel.isLockoutMessagePresent
- .map { lockoutMessagePresent -> !lockoutMessagePresent }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = authenticationInteractor.lockoutEndTimestamp == null,
- )
-
- init {
- if (flags.isComposeBouncerOrSceneContainerEnabled()) {
- // Keeps the upcoming wipe dialog up-to-date.
- applicationScope.launch {
- authenticationInteractor.upcomingWipe.collect { wipeModel ->
- wipeDialogMessage.value = wipeModel?.message
- }
- }
- }
- }
-
- private fun isSideBySideSupported(authMethod: AuthMethodBouncerViewModel?): Boolean {
- return isUserSwitcherVisible || authMethod !is PasswordBouncerViewModel
- }
-
- private fun isFoldSplitRequired(authMethod: AuthMethodBouncerViewModel?): Boolean {
- return authMethod !is PasswordBouncerViewModel
- }
-
- private fun getChildViewModel(
- authenticationMethod: AuthenticationMethodModel,
- ): AuthMethodBouncerViewModel? {
- // If the current child view-model matches the authentication method, reuse it instead of
- // creating a new instance.
- val childViewModel = authMethodViewModel.value
- if (authenticationMethod == childViewModel?.authenticationMethod) {
- return childViewModel
- }
-
- childViewModelScope?.cancel()
- val newViewModelScope = createChildCoroutineScope(applicationScope)
- childViewModelScope = newViewModelScope
- return when (authenticationMethod) {
- is AuthenticationMethodModel.Pin ->
- PinBouncerViewModel(
- applicationContext = applicationContext,
- viewModelScope = newViewModelScope,
- interactor = bouncerInteractor,
- isInputEnabled = isInputEnabled,
- simBouncerInteractor = simBouncerInteractor,
- authenticationMethod = authenticationMethod,
- onIntentionalUserInput = ::onIntentionalUserInput
- )
- is AuthenticationMethodModel.Sim ->
- PinBouncerViewModel(
- applicationContext = applicationContext,
- viewModelScope = newViewModelScope,
- interactor = bouncerInteractor,
- isInputEnabled = isInputEnabled,
- simBouncerInteractor = simBouncerInteractor,
- authenticationMethod = authenticationMethod,
- onIntentionalUserInput = ::onIntentionalUserInput
- )
- is AuthenticationMethodModel.Password ->
- PasswordBouncerViewModel(
- viewModelScope = newViewModelScope,
- isInputEnabled = isInputEnabled,
- interactor = bouncerInteractor,
- inputMethodInteractor = inputMethodInteractor,
- selectedUserInteractor = selectedUserInteractor,
- onIntentionalUserInput = ::onIntentionalUserInput
- )
- is AuthenticationMethodModel.Pattern ->
- PatternBouncerViewModel(
- applicationContext = applicationContext,
- viewModelScope = newViewModelScope,
- interactor = bouncerInteractor,
- isInputEnabled = isInputEnabled,
- onIntentionalUserInput = ::onIntentionalUserInput
- )
- else -> null
- }
- }
-
- private fun onIntentionalUserInput() {
- message.showDefaultMessage()
- bouncerInteractor.onIntentionalUserInput()
- }
-
- private fun createChildCoroutineScope(parentScope: CoroutineScope): CoroutineScope {
- return CoroutineScope(
- SupervisorJob(parent = parentScope.coroutineContext.job) + mainDispatcher
- )
- }
-
- /**
- * @return A message warning the user that the user/profile/device will be wiped upon a further
- * [AuthenticationWipeModel.remainingAttempts] unsuccessful authentication attempts.
- */
- private fun AuthenticationWipeModel.getAlmostAtWipeMessage(): String {
- val message =
- applicationContext.getString(
- wipeTarget.messageIdForAlmostWipe,
- failedAttempts,
- remainingAttempts,
- )
- return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
- devicePolicyManager.resources.getString(
- DevicePolicyResources.Strings.SystemUi
- .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ALMOST_ERASING_PROFILE,
- { message },
- failedAttempts,
- remainingAttempts,
- ) ?: message
- } else {
- message
- }
- }
-
- /**
- * @return A message informing the user that their user/profile/device will be wiped promptly.
- */
- private fun AuthenticationWipeModel.getWipeMessage(): String {
- val message = applicationContext.getString(wipeTarget.messageIdForWipe, failedAttempts)
- return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
- devicePolicyManager.resources.getString(
- DevicePolicyResources.Strings.SystemUi
- .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
- { message },
- failedAttempts,
- ) ?: message
- } else {
- message
- }
- }
-
- private val AuthenticationWipeModel.message: String
- get() = if (remainingAttempts > 0) getAlmostAtWipeMessage() else getWipeMessage()
-
- private fun createDialogViewModel(): DialogViewModel? {
- val wipeText = wipeDialogMessage.value
- val lockoutText = lockoutDialogMessage.value
- return when {
- // The wipe dialog takes priority over the lockout dialog.
- wipeText != null ->
- DialogViewModel(
- text = wipeText,
- onDismiss = { wipeDialogMessage.value = null },
- )
- lockoutText != null ->
- DialogViewModel(
- text = lockoutText,
- onDismiss = { lockoutDialogMessage.value = null },
- )
- else -> null // No dialog to show.
- }
- }
-
- /**
- * Notifies that a key event has occurred.
- *
- * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
- */
- fun onKeyEvent(keyEvent: KeyEvent): Boolean {
- return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent(
- keyEvent.type,
- keyEvent.nativeKeyEvent.keyCode
- ) ?: false
- }
-
- data class DialogViewModel(
- val text: String,
-
- /** Callback to run after the dialog has been dismissed by the user. */
- val onDismiss: () -> Unit,
- )
-
- data class UserSwitcherDropdownItemViewModel(
- val icon: Icon,
- val text: Text,
- val onClick: () -> Unit,
- )
-}
-
-@Module
-object BouncerViewModelModule {
-
- @Provides
- @SysUISingleton
- fun viewModel(
- @Application applicationContext: Context,
- @Application applicationScope: CoroutineScope,
- @Main mainDispatcher: CoroutineDispatcher,
- bouncerInteractor: BouncerInteractor,
- imeInteractor: InputMethodInteractor,
- simBouncerInteractor: SimBouncerInteractor,
- actionButtonInteractor: BouncerActionButtonInteractor,
- authenticationInteractor: AuthenticationInteractor,
- selectedUserInteractor: SelectedUserInteractor,
- flags: ComposeBouncerFlags,
- userSwitcherViewModel: UserSwitcherViewModel,
- devicePolicyManager: DevicePolicyManager,
- bouncerMessageViewModel: BouncerMessageViewModel,
- ): BouncerViewModel {
- return BouncerViewModel(
- applicationContext = applicationContext,
- applicationScope = applicationScope,
- mainDispatcher = mainDispatcher,
- bouncerInteractor = bouncerInteractor,
- inputMethodInteractor = imeInteractor,
- simBouncerInteractor = simBouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- selectedUserInteractor = selectedUserInteractor,
- devicePolicyManager = devicePolicyManager,
- bouncerMessageViewModel = bouncerMessageViewModel,
- flags = flags,
- selectedUser = userSwitcherViewModel.selectedUser,
- users = userSwitcherViewModel.users,
- userSwitcherMenu = userSwitcherViewModel.menu,
- actionButton = actionButtonInteractor.actionButton,
- )
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index 052fb6b..9ead7a0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -23,29 +23,33 @@
import com.android.systemui.res.R
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.onSubscriberAdded
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the password bouncer UI. */
-class PasswordBouncerViewModel(
- viewModelScope: CoroutineScope,
- isInputEnabled: StateFlow<Boolean>,
+class PasswordBouncerViewModel
+@AssistedInject
+constructor(
interactor: BouncerInteractor,
- private val onIntentionalUserInput: () -> Unit,
private val inputMethodInteractor: InputMethodInteractor,
private val selectedUserInteractor: SelectedUserInteractor,
+ @Assisted isInputEnabled: StateFlow<Boolean>,
+ @Assisted private val onIntentionalUserInput: () -> Unit,
) :
AuthMethodBouncerViewModel(
- viewModelScope = viewModelScope,
interactor = interactor,
isInputEnabled = isInputEnabled,
) {
@@ -59,28 +63,70 @@
override val lockoutMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message
+ private val _isImeSwitcherButtonVisible = MutableStateFlow(false)
/** Informs the UI whether the input method switcher button should be visible. */
- val isImeSwitcherButtonVisible: StateFlow<Boolean> = imeSwitcherRefreshingFlow()
+ val isImeSwitcherButtonVisible: StateFlow<Boolean> = _isImeSwitcherButtonVisible.asStateFlow()
/** Whether the text field element currently has focus. */
private val isTextFieldFocused = MutableStateFlow(false)
+ private val _isTextFieldFocusRequested =
+ MutableStateFlow(isInputEnabled.value && !isTextFieldFocused.value)
/** Whether the UI should request focus on the text field element. */
- val isTextFieldFocusRequested =
- combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus -> hasInput && !hasFocus }
- .stateIn(
- scope = viewModelScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = isInputEnabled.value && !isTextFieldFocused.value,
- )
+ val isTextFieldFocusRequested = _isTextFieldFocusRequested.asStateFlow()
+ private val _selectedUserId = MutableStateFlow(selectedUserInteractor.getSelectedUserId())
/** The ID of the currently-selected user. */
- val selectedUserId: StateFlow<Int> =
- selectedUserInteractor.selectedUser.stateIn(
- scope = viewModelScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = selectedUserInteractor.getSelectedUserId(),
- )
+ val selectedUserId: StateFlow<Int> = _selectedUserId.asStateFlow()
+
+ private val requests = Channel<Request>(Channel.BUFFERED)
+
+ override suspend fun onActivated() {
+ coroutineScope {
+ launch { super.onActivated() }
+ launch {
+ requests.receiveAsFlow().collect { request ->
+ when (request) {
+ is OnImeSwitcherButtonClicked -> {
+ inputMethodInteractor.showInputMethodPicker(
+ displayId = request.displayId,
+ showAuxiliarySubtypes = false,
+ )
+ }
+ is OnImeDismissed -> {
+ interactor.onImeHiddenByUser()
+ }
+ }
+ }
+ }
+ launch {
+ combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus ->
+ hasInput && !hasFocus
+ }
+ .collectLatest { _isTextFieldFocusRequested.value = it }
+ }
+ launch {
+ selectedUserInteractor.selectedUser.collectLatest { _selectedUserId.value = it }
+ }
+ launch {
+ // Re-fetch the currently-enabled IMEs whenever the selected user changes, and
+ // whenever
+ // the UI subscribes to the `isImeSwitcherButtonVisible` flow.
+ combine(
+ // InputMethodManagerService sometimes takes some time to update its
+ // internal
+ // state when the selected user changes. As a workaround, delay fetching the
+ // IME
+ // info.
+ selectedUserInteractor.selectedUser.onEach { delay(DELAY_TO_FETCH_IMES) },
+ _isImeSwitcherButtonVisible.onSubscriberAdded()
+ ) { selectedUserId, _ ->
+ inputMethodInteractor.hasMultipleEnabledImesOrSubtypes(selectedUserId)
+ }
+ .collectLatest { _isImeSwitcherButtonVisible.value = it }
+ }
+ }
+ }
override fun onHidden() {
super.onHidden()
@@ -106,9 +152,7 @@
/** Notifies that the user clicked the button to change the input method. */
fun onImeSwitcherButtonClicked(displayId: Int) {
- viewModelScope.launch {
- inputMethodInteractor.showInputMethodPicker(displayId, showAuxiliarySubtypes = false)
- }
+ requests.trySend(OnImeSwitcherButtonClicked(displayId))
}
/** Notifies that the user has pressed the key for attempting to authenticate the password. */
@@ -120,7 +164,7 @@
/** Notifies that the user has dismissed the software keyboard (IME). */
fun onImeDismissed() {
- viewModelScope.launch { interactor.onImeHiddenByUser() }
+ requests.trySend(OnImeDismissed)
}
/** Notifies that the password text field has gained or lost focus. */
@@ -128,34 +172,21 @@
isTextFieldFocused.value = isFocused
}
- /**
- * Whether the input method switcher button should be displayed in the password bouncer UI. The
- * value may be stale at the moment of subscription to this flow, but it is guaranteed to be
- * shortly updated with a fresh value.
- *
- * Note: Each added subscription triggers an IPC call in the background, so this should only be
- * subscribed to by the UI once in its lifecycle (i.e. when the bouncer is shown).
- */
- private fun imeSwitcherRefreshingFlow(): StateFlow<Boolean> {
- val isImeSwitcherButtonVisible = MutableStateFlow(value = false)
- viewModelScope.launch {
- // Re-fetch the currently-enabled IMEs whenever the selected user changes, and whenever
- // the UI subscribes to the `isImeSwitcherButtonVisible` flow.
- combine(
- // InputMethodManagerService sometimes takes some time to update its internal
- // state when the selected user changes. As a workaround, delay fetching the IME
- // info.
- selectedUserInteractor.selectedUser.onEach { delay(DELAY_TO_FETCH_IMES) },
- isImeSwitcherButtonVisible.onSubscriberAdded()
- ) { selectedUserId, _ ->
- inputMethodInteractor.hasMultipleEnabledImesOrSubtypes(selectedUserId)
- }
- .collect { isImeSwitcherButtonVisible.value = it }
- }
- return isImeSwitcherButtonVisible.asStateFlow()
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ isInputEnabled: StateFlow<Boolean>,
+ onIntentionalUserInput: () -> Unit,
+ ): PasswordBouncerViewModel
}
companion object {
@VisibleForTesting val DELAY_TO_FETCH_IMES = 300.milliseconds
}
+
+ private sealed interface Request
+
+ private data class OnImeSwitcherButtonClicked(val displayId: Int) : Request
+
+ private data object OnImeDismissed : Request
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 8b9c0a9a..b1df04b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -22,28 +22,32 @@
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.res.R
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sqrt
-import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the pattern bouncer UI. */
-class PatternBouncerViewModel(
+class PatternBouncerViewModel
+@AssistedInject
+constructor(
private val applicationContext: Context,
- viewModelScope: CoroutineScope,
interactor: BouncerInteractor,
- isInputEnabled: StateFlow<Boolean>,
- private val onIntentionalUserInput: () -> Unit,
+ @Assisted isInputEnabled: StateFlow<Boolean>,
+ @Assisted private val onIntentionalUserInput: () -> Unit,
) :
AuthMethodBouncerViewModel(
- viewModelScope = viewModelScope,
interactor = interactor,
isInputEnabled = isInputEnabled,
) {
@@ -54,17 +58,10 @@
/** The number of rows in the dot grid. */
val rowCount = 3
- private val _selectedDots = MutableStateFlow<LinkedHashSet<PatternDotViewModel>>(linkedSetOf())
-
+ private val selectedDotSet = MutableStateFlow<LinkedHashSet<PatternDotViewModel>>(linkedSetOf())
+ private val selectedDotList = MutableStateFlow(selectedDotSet.value.toList())
/** The dots that were selected by the user, in the order of selection. */
- val selectedDots: StateFlow<List<PatternDotViewModel>> =
- _selectedDots
- .map { it.toList() }
- .stateIn(
- scope = viewModelScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = emptyList(),
- )
+ val selectedDots: StateFlow<List<PatternDotViewModel>> = selectedDotList.asStateFlow()
private val _currentDot = MutableStateFlow<PatternDotViewModel?>(null)
@@ -83,6 +80,17 @@
override val lockoutMessageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message
+ override suspend fun onActivated() {
+ coroutineScope {
+ launch { super.onActivated() }
+ launch {
+ selectedDotSet
+ .map { it.toList() }
+ .collectLatest { selectedDotList.value = it.toList() }
+ }
+ }
+ }
+
/** Notifies that the user has started a drag gesture across the dot grid. */
fun onDragStart() {
onIntentionalUserInput()
@@ -120,7 +128,7 @@
}
val hitDot = dots.value.firstOrNull { dot -> dot.x == dotColumn && dot.y == dotRow }
- if (hitDot != null && !_selectedDots.value.contains(hitDot)) {
+ if (hitDot != null && !selectedDotSet.value.contains(hitDot)) {
val skippedOverDots =
currentDot.value?.let { previousDot ->
buildList {
@@ -147,9 +155,9 @@
}
} ?: emptyList()
- _selectedDots.value =
+ selectedDotSet.value =
linkedSetOf<PatternDotViewModel>().apply {
- addAll(_selectedDots.value)
+ addAll(selectedDotSet.value)
addAll(skippedOverDots)
add(hitDot)
}
@@ -172,11 +180,11 @@
override fun clearInput() {
_dots.value = defaultDots()
_currentDot.value = null
- _selectedDots.value = linkedSetOf()
+ selectedDotSet.value = linkedSetOf()
}
override fun getInput(): List<Any> {
- return _selectedDots.value.map(PatternDotViewModel::toCoordinate)
+ return selectedDotSet.value.map(PatternDotViewModel::toCoordinate)
}
private fun defaultDots(): List<PatternDotViewModel> {
@@ -204,6 +212,14 @@
max(min(outValue.float, 1f), MIN_DOT_HIT_FACTOR)
}
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ isInputEnabled: StateFlow<Boolean>,
+ onIntentionalUserInput: () -> Unit,
+ ): PatternBouncerViewModel
+ }
+
companion object {
private const val MIN_DOT_HIT_FACTOR = 0.2f
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index aa447ff..cb36560 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -32,29 +32,34 @@
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
import com.android.systemui.res.R
-import kotlinx.coroutines.CoroutineScope
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the PIN code bouncer UI. */
-class PinBouncerViewModel(
+class PinBouncerViewModel
+@AssistedInject
+constructor(
applicationContext: Context,
- viewModelScope: CoroutineScope,
interactor: BouncerInteractor,
- isInputEnabled: StateFlow<Boolean>,
- private val onIntentionalUserInput: () -> Unit,
private val simBouncerInteractor: SimBouncerInteractor,
- authenticationMethod: AuthenticationMethodModel,
+ @Assisted isInputEnabled: StateFlow<Boolean>,
+ @Assisted private val onIntentionalUserInput: () -> Unit,
+ @Assisted override val authenticationMethod: AuthenticationMethodModel,
) :
AuthMethodBouncerViewModel(
- viewModelScope = viewModelScope,
interactor = interactor,
isInputEnabled = isInputEnabled,
) {
@@ -73,69 +78,89 @@
/** Currently entered pin keys. */
val pinInput: StateFlow<PinInputViewModel> = mutablePinInput
+ private val _hintedPinLength = MutableStateFlow<Int?>(null)
/** The length of the PIN for which we should show a hint. */
- val hintedPinLength: StateFlow<Int?> =
- if (isSimAreaVisible) {
- flowOf(null)
- } else {
- interactor.hintedPinLength
- }
- .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
+ val hintedPinLength: StateFlow<Int?> = _hintedPinLength.asStateFlow()
+ private val _backspaceButtonAppearance = MutableStateFlow(ActionButtonAppearance.Hidden)
/** Appearance of the backspace button. */
val backspaceButtonAppearance: StateFlow<ActionButtonAppearance> =
- combine(
- mutablePinInput,
- interactor.isAutoConfirmEnabled,
- ) { mutablePinEntries, isAutoConfirmEnabled ->
- computeBackspaceButtonAppearance(
- pinInput = mutablePinEntries,
- isAutoConfirmEnabled = isAutoConfirmEnabled,
- )
- }
- .stateIn(
- scope = viewModelScope,
- // Make sure this is kept as WhileSubscribed or we can run into a bug where the
- // downstream continues to receive old/stale/cached values.
- started = SharingStarted.WhileSubscribed(),
- initialValue = ActionButtonAppearance.Hidden,
- )
+ _backspaceButtonAppearance.asStateFlow()
+ private val _confirmButtonAppearance = MutableStateFlow(ActionButtonAppearance.Hidden)
/** Appearance of the confirm button. */
val confirmButtonAppearance: StateFlow<ActionButtonAppearance> =
- interactor.isAutoConfirmEnabled
- .map { if (it) ActionButtonAppearance.Hidden else ActionButtonAppearance.Shown }
- .stateIn(
- scope = viewModelScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = ActionButtonAppearance.Hidden,
- )
-
- override val authenticationMethod: AuthenticationMethodModel = authenticationMethod
+ _confirmButtonAppearance.asStateFlow()
override val lockoutMessageId = R.string.kg_too_many_failed_pin_attempts_dialog_message
- init {
- viewModelScope.launch { simBouncerInteractor.subId.collect { onResetSimFlow() } }
+ private val requests = Channel<Request>(Channel.BUFFERED)
+
+ override suspend fun onActivated() {
+ coroutineScope {
+ launch { super.onActivated() }
+ launch {
+ requests.receiveAsFlow().collect { request ->
+ when (request) {
+ is OnErrorDialogDismissed -> {
+ simBouncerInteractor.onErrorDialogDismissed()
+ }
+ is OnAuthenticateButtonClickedForSim -> {
+ isSimUnlockingDialogVisible.value = true
+ simBouncerInteractor.verifySim(getInput())
+ isSimUnlockingDialogVisible.value = false
+ clearInput()
+ }
+ }
+ }
+ }
+ launch { simBouncerInteractor.subId.collect { onResetSimFlow() } }
+ launch {
+ if (isSimAreaVisible) {
+ flowOf(null)
+ } else {
+ interactor.hintedPinLength
+ }
+ .collectLatest { _hintedPinLength.value = it }
+ }
+ launch {
+ combine(
+ mutablePinInput,
+ interactor.isAutoConfirmEnabled,
+ ) { mutablePinEntries, isAutoConfirmEnabled ->
+ computeBackspaceButtonAppearance(
+ pinInput = mutablePinEntries,
+ isAutoConfirmEnabled = isAutoConfirmEnabled,
+ )
+ }
+ .collectLatest { _backspaceButtonAppearance.value = it }
+ }
+ launch {
+ interactor.isAutoConfirmEnabled
+ .map { if (it) ActionButtonAppearance.Hidden else ActionButtonAppearance.Shown }
+ .collectLatest { _confirmButtonAppearance.value = it }
+ }
+ launch {
+ interactor.isPinEnhancedPrivacyEnabled
+ .map { !it }
+ .collectLatest { _isDigitButtonAnimationEnabled.value = it }
+ }
+ }
}
/** Notifies that the user dismissed the sim pin error dialog. */
fun onErrorDialogDismissed() {
- viewModelScope.launch { simBouncerInteractor.onErrorDialogDismissed() }
+ requests.trySend(OnErrorDialogDismissed)
}
+ private val _isDigitButtonAnimationEnabled =
+ MutableStateFlow(!interactor.isPinEnhancedPrivacyEnabled.value)
/**
* Whether the digit buttons should be animated when touched. Note that this doesn't affect the
* delete or enter buttons; those should always animate.
*/
val isDigitButtonAnimationEnabled: StateFlow<Boolean> =
- interactor.isPinEnhancedPrivacyEnabled
- .map { !it }
- .stateIn(
- scope = viewModelScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = !interactor.isPinEnhancedPrivacyEnabled.value,
- )
+ _isDigitButtonAnimationEnabled.asStateFlow()
/** Notifies that the user clicked on a PIN button with the given digit value. */
fun onPinButtonClicked(input: Int) {
@@ -163,19 +188,14 @@
/** Notifies that the user clicked the "enter" button. */
fun onAuthenticateButtonClicked() {
if (authenticationMethod == AuthenticationMethodModel.Sim) {
- viewModelScope.launch {
- isSimUnlockingDialogVisible.value = true
- simBouncerInteractor.verifySim(getInput())
- isSimUnlockingDialogVisible.value = false
- clearInput()
- }
+ requests.trySend(OnAuthenticateButtonClickedForSim)
} else {
tryAuthenticate(useAutoConfirm = false)
}
}
fun onDisableEsimButtonClicked() {
- viewModelScope.launch { simBouncerInteractor.disableEsim() }
+ simBouncerInteractor.disableEsim()
}
/** Resets the sim screen and shows a default message. */
@@ -242,6 +262,21 @@
else -> false
}
}
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ isInputEnabled: StateFlow<Boolean>,
+ onIntentionalUserInput: () -> Unit,
+ authenticationMethod: AuthenticationMethodModel,
+ ): PinBouncerViewModel
+ }
+
+ private sealed interface Request
+
+ private data object OnErrorDialogDismissed : Request
+
+ private data object OnAuthenticateButtonClickedForSim : Request
}
/** Appearance of pin-pad action buttons. */
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt
new file mode 100644
index 0000000..7453368
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import android.app.ActivityManager
+import com.android.systemui.common.usagestats.domain.UsageStatsInteractor
+import com.android.systemui.common.usagestats.shared.model.ActivityEventModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shared.system.TaskStackChangeListener
+import com.android.systemui.shared.system.TaskStackChangeListeners
+import com.android.systemui.util.kotlin.race
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.takeWhile
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withTimeout
+
+/**
+ * Detects activity starts that occur while the communal hub is showing, within a short delay of a
+ * widget interaction occurring. Used for detecting non-activity trampolines which otherwise would
+ * not prompt the user for authentication.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class WidgetTrampolineInteractor
+@Inject
+constructor(
+ private val activityStarter: ActivityStarter,
+ private val systemClock: SystemClock,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val taskStackChangeListeners: TaskStackChangeListeners,
+ private val usageStatsInteractor: UsageStatsInteractor,
+ @CommunalLog logBuffer: LogBuffer,
+) {
+ private companion object {
+ const val TAG = "WidgetTrampolineInteractor"
+ }
+
+ private val logger = Logger(logBuffer, TAG)
+
+ /** Waits for a new task to be moved to the foreground. */
+ private suspend fun waitForNewForegroundTask() = suspendCancellableCoroutine { cont ->
+ val listener =
+ object : TaskStackChangeListener {
+ override fun onTaskMovedToFront(taskInfo: ActivityManager.RunningTaskInfo) {
+ if (!cont.isCompleted) {
+ cont.resume(Unit, null)
+ }
+ }
+ }
+ taskStackChangeListeners.registerTaskStackListener(listener)
+ cont.invokeOnCancellation { taskStackChangeListeners.unregisterTaskStackListener(listener) }
+ }
+
+ /**
+ * Waits for an activity to enter a [ActivityEventModel.Lifecycle.RESUMED] state by periodically
+ * polling the system to see if any activities have started.
+ */
+ private suspend fun waitForActivityStartByPolling(startTime: Long): Boolean {
+ while (true) {
+ val events = usageStatsInteractor.queryActivityEvents(startTime = startTime)
+ if (events.any { event -> event.lifecycle == ActivityEventModel.Lifecycle.RESUMED }) {
+ return true
+ } else {
+ // Poll again in the future to check if an activity started.
+ delay(200.milliseconds)
+ }
+ }
+ }
+
+ /** Waits for a transition away from the hub to occur. */
+ private suspend fun waitForTransitionAwayFromHub() {
+ keyguardTransitionInteractor
+ .isFinishedIn(Scenes.Communal, KeyguardState.GLANCEABLE_HUB)
+ .takeWhile { it }
+ .collect {}
+ }
+
+ private suspend fun waitForActivityStartWhileOnHub(): Boolean {
+ val startTime = systemClock.currentTimeMillis()
+ return try {
+ return withTimeout(1.seconds) {
+ race(
+ {
+ waitForNewForegroundTask()
+ true
+ },
+ { waitForActivityStartByPolling(startTime) },
+ {
+ waitForTransitionAwayFromHub()
+ false
+ },
+ )
+ }
+ } catch (e: TimeoutCancellationException) {
+ false
+ }
+ }
+
+ /**
+ * Checks if an activity starts while on the glanceable hub and dismisses the keyguard if it
+ * does. This can detect activities started due to broadcast trampolines from widgets.
+ */
+ suspend fun waitForActivityStartAndDismissKeyguard() {
+ if (waitForActivityStartWhileOnHub()) {
+ logger.d("Detected trampoline, requesting unlock")
+ activityStarter.dismissKeyguardThenExecute(
+ /* action= */ { false },
+ /* cancel= */ null,
+ /* afterKeyguardGone= */ false
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
index c4edcac..99e3232 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
@@ -48,7 +48,17 @@
InteractionHandlerDelegate(
communalSceneInteractor,
findViewToAnimate = { view -> view is SmartspaceAppWidgetHostView },
- intentStarter = this::startIntent,
+ intentStarter =
+ object : InteractionHandlerDelegate.IntentStarter {
+ override fun startActivity(
+ intent: PendingIntent,
+ fillInIntent: Intent,
+ activityOptions: ActivityOptions,
+ controller: ActivityTransitionAnimator.Controller?
+ ): Boolean {
+ return startIntent(intent, fillInIntent, activityOptions, controller)
+ }
+ },
logger = Logger(logBuffer, TAG),
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
index 7a05671..5f421fd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
@@ -21,10 +21,13 @@
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
+import androidx.core.view.doOnLayout
import com.android.app.tracing.coroutines.launch
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.util.WidgetViewFactory
import com.android.systemui.util.kotlin.DisposableHandles
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
@@ -44,13 +47,8 @@
val loadingJob =
applicationScope.launch("$TAG#createWidgetView") {
val widget = factory.createWidget(context, model, size)
- // TODO(b/358662507): Remove this workaround
- (container.parent as? ViewGroup)?.let { parent ->
- val index = parent.indexOfChild(container)
- parent.removeView(container)
- parent.addView(container, index)
- }
- container.setView(widget)
+ waitForLayout(container)
+ container.post { container.setView(widget) }
}
disposables += DisposableHandle { loadingJob.cancel() }
@@ -58,6 +56,10 @@
return disposables
}
+
+ private suspend fun waitForLayout(container: FrameLayout) = suspendCoroutine { cont ->
+ container.doOnLayout { cont.resume(Unit) }
+ }
}
private fun ViewGroup.setView(view: View) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
index d2029d5..5e21afa 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
@@ -19,6 +19,7 @@
import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Intent
+import android.util.Pair as UtilPair
import android.view.View
import android.widget.RemoteViews
import androidx.core.util.component1
@@ -36,14 +37,28 @@
private val logger: Logger,
) : RemoteViews.InteractionHandler {
- /** Responsible for starting the pending intent for launching activities. */
- fun interface IntentStarter {
- fun startPendingIntent(
+ interface IntentStarter {
+ /** Responsible for starting the pending intent for launching activities. */
+ fun startActivity(
intent: PendingIntent,
fillInIntent: Intent,
activityOptions: ActivityOptions,
controller: ActivityTransitionAnimator.Controller?,
): Boolean
+
+ /** Responsible for starting the pending intent for non-activity launches. */
+ fun startPendingIntent(
+ view: View,
+ pendingIntent: PendingIntent,
+ fillInIntent: Intent,
+ activityOptions: ActivityOptions,
+ ): Boolean {
+ return RemoteViews.startPendingIntent(
+ view,
+ pendingIntent,
+ UtilPair(fillInIntent, activityOptions),
+ )
+ }
}
override fun onInteraction(
@@ -55,7 +70,7 @@
str1 = pendingIntent.toLoggingString()
str2 = pendingIntent.creatorPackage
}
- val launchOptions = response.getLaunchOptions(view)
+ val (fillInIntent, activityOptions) = response.getLaunchOptions(view)
return when {
pendingIntent.isActivity -> {
// Forward the fill-in intent and activity options retrieved from the response
@@ -67,15 +82,15 @@
communalSceneInteractor.setIsLaunchingWidget(true)
CommunalTransitionAnimatorController(it, communalSceneInteractor)
}
- val (fillInIntent, activityOptions) = launchOptions
- intentStarter.startPendingIntent(
+ intentStarter.startActivity(
pendingIntent,
fillInIntent,
activityOptions,
animationController
)
}
- else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions)
+ else ->
+ intentStarter.startPendingIntent(view, pendingIntent, fillInIntent, activityOptions)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index 0eeb506..121b4a3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -21,22 +21,30 @@
import android.content.Intent
import android.view.View
import android.widget.RemoteViews
+import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalWidgetTrampolineFix
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.domain.interactor.WidgetTrampolineInteractor
import com.android.systemui.communal.util.InteractionHandlerDelegate
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
import com.android.systemui.plugins.ActivityStarter
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
@SysUISingleton
class WidgetInteractionHandler
@Inject
constructor(
+ @Application applicationScope: CoroutineScope,
private val activityStarter: ActivityStarter,
communalSceneInteractor: CommunalSceneInteractor,
+ private val widgetTrampolineInteractor: WidgetTrampolineInteractor,
@CommunalLog val logBuffer: LogBuffer,
) : RemoteViews.InteractionHandler {
@@ -48,7 +56,52 @@
InteractionHandlerDelegate(
communalSceneInteractor,
findViewToAnimate = { view -> view is CommunalAppWidgetHostView },
- intentStarter = this::startIntent,
+ intentStarter =
+ object : InteractionHandlerDelegate.IntentStarter {
+ private var job: Job? = null
+
+ override fun startActivity(
+ intent: PendingIntent,
+ fillInIntent: Intent,
+ activityOptions: ActivityOptions,
+ controller: ActivityTransitionAnimator.Controller?
+ ): Boolean {
+ cancelTrampolineMonitoring()
+ return startActivityIntent(
+ intent,
+ fillInIntent,
+ activityOptions,
+ controller
+ )
+ }
+
+ override fun startPendingIntent(
+ view: View,
+ pendingIntent: PendingIntent,
+ fillInIntent: Intent,
+ activityOptions: ActivityOptions
+ ): Boolean {
+ cancelTrampolineMonitoring()
+ if (communalWidgetTrampolineFix()) {
+ job =
+ applicationScope.launch("$TAG#monitorForActivityStart") {
+ widgetTrampolineInteractor
+ .waitForActivityStartAndDismissKeyguard()
+ }
+ }
+ return super.startPendingIntent(
+ view,
+ pendingIntent,
+ fillInIntent,
+ activityOptions
+ )
+ }
+
+ private fun cancelTrampolineMonitoring() {
+ job?.cancel()
+ job = null
+ }
+ },
logger = Logger(logBuffer, TAG),
)
@@ -58,7 +111,7 @@
response: RemoteViews.RemoteResponse
): Boolean = delegate.onInteraction(view, pendingIntent, response)
- private fun startIntent(
+ private fun startActivityIntent(
pendingIntent: PendingIntent,
fillInIntent: Intent,
extraOptions: ActivityOptions,
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
index b9b3895..36b9ac7 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.inputdevice.tutorial.data.repository
import android.content.Context
+import androidx.annotation.VisibleForTesting
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
@@ -33,15 +34,19 @@
import kotlinx.coroutines.flow.map
@SysUISingleton
-class TutorialSchedulerRepository
-@Inject
-constructor(
- @Application private val applicationContext: Context,
- @Background private val backgroundScope: CoroutineScope
+class TutorialSchedulerRepository(
+ private val applicationContext: Context,
+ backgroundScope: CoroutineScope,
+ dataStoreName: String
) {
+ @Inject
+ constructor(
+ @Application applicationContext: Context,
+ @Background backgroundScope: CoroutineScope
+ ) : this(applicationContext, backgroundScope, dataStoreName = "TutorialScheduler")
private val Context.dataStore: DataStore<Preferences> by
- preferencesDataStore(name = DATASTORE_NAME, scope = backgroundScope)
+ preferencesDataStore(name = dataStoreName, scope = backgroundScope)
suspend fun isLaunched(deviceType: DeviceType): Boolean = loadData()[deviceType]!!.isLaunched
@@ -81,8 +86,12 @@
private fun getConnectKey(device: DeviceType) =
longPreferencesKey(device.name + CONNECT_TIME_SUFFIX)
+ @VisibleForTesting
+ suspend fun clearDataStore() {
+ applicationContext.dataStore.edit { it.clear() }
+ }
+
companion object {
- const val DATASTORE_NAME = "TutorialScheduler"
const val IS_LAUNCHED_SUFFIX = "_IS_LAUNCHED"
const val CONNECT_TIME_SUFFIX = "_CONNECTED_TIME"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 81b0064..49303e0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -91,11 +91,11 @@
* the z-order (which is not really above the system UI window, but rather - the lock-screen
* becomes invisible to reveal the "occluding activity").
*/
- val isKeyguardShowing: Flow<Boolean>
+ val isKeyguardShowing: StateFlow<Boolean>
/** Is an activity showing over the keyguard? */
@Deprecated("Use KeyguardTransitionInteractor + KeyguardState.OCCLUDED")
- val isKeyguardOccluded: Flow<Boolean>
+ val isKeyguardOccluded: StateFlow<Boolean>
/**
* Whether the device is locked or unlocked right now. This is true when keyguard has been
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 2a8bb47..13d54ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -36,8 +36,6 @@
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
@SysUISingleton
@@ -73,15 +71,11 @@
listenForTransitionToCamera(scope, keyguardInteractor)
}
- private val canDismissLockscreen: Flow<Boolean> =
- combine(
- keyguardInteractor.isKeyguardShowing,
- keyguardInteractor.isKeyguardDismissible,
- keyguardInteractor.biometricUnlockState,
- ) { isKeyguardShowing, isKeyguardDismissible, biometricUnlockState ->
- (isWakeAndUnlock(biometricUnlockState.mode) ||
- (!isKeyguardShowing && isKeyguardDismissible))
- }
+ private fun canDismissLockscreen(): Boolean {
+ return isWakeAndUnlock(keyguardInteractor.biometricUnlockState.value.mode) ||
+ (!keyguardInteractor.isKeyguardShowing.value &&
+ keyguardInteractor.isKeyguardDismissible.value)
+ }
/**
* Listen for the signal that we're waking up and figure what state we need to transition to.
@@ -96,22 +90,18 @@
.debounce(50L)
.sample(
startedKeyguardTransitionStep,
- keyguardInteractor.biometricUnlockState,
- keyguardInteractor.primaryBouncerShowing,
- keyguardInteractor.isKeyguardOccluded,
- canDismissLockscreen,
wakeToGoneInteractor.canWakeDirectlyToGone,
)
.collect {
(
_,
startedStep,
- biometricUnlockState,
- primaryBouncerShowing,
- isKeyguardOccludedLegacy,
- canDismissLockscreen,
canWakeDirectlyToGone,
) ->
+ val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value
+ val biometricUnlockMode = keyguardInteractor.biometricUnlockState.value.mode
+ val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value
+
if (!maybeHandleInsecurePowerGesture()) {
val shouldTransitionToLockscreen =
if (KeyguardWmStateRefactor.isEnabled) {
@@ -121,12 +111,10 @@
// completes.
!maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
startTransitionTo(state, ownerReason = reason)
- } &&
- !isWakeAndUnlock(biometricUnlockState.mode) &&
- !primaryBouncerShowing
+ } && !isWakeAndUnlock(biometricUnlockMode) && !primaryBouncerShowing
} else {
!isKeyguardOccludedLegacy &&
- !isWakeAndUnlock(biometricUnlockState.mode) &&
+ !isWakeAndUnlock(biometricUnlockMode) &&
!primaryBouncerShowing
}
@@ -136,7 +124,7 @@
!KeyguardWmStateRefactor.isEnabled && isKeyguardOccludedLegacy
val shouldTransitionToGone =
- (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) ||
+ (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen()) ||
(KeyguardWmStateRefactor.isEnabled && canWakeDirectlyToGone)
if (shouldTransitionToGone) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 61446c1..0c12f8c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -42,8 +42,6 @@
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
@@ -83,13 +81,10 @@
listenForTransitionToCamera(scope, keyguardInteractor)
}
- private val canTransitionToGoneOnWake: Flow<Boolean> =
- combine(
- keyguardInteractor.isKeyguardShowing,
- keyguardInteractor.isKeyguardDismissible,
- ) { isKeyguardShowing, isKeyguardDismissible ->
- isKeyguardDismissible && !isKeyguardShowing
- }
+ private fun canDismissLockscreen(): Boolean {
+ return !keyguardInteractor.isKeyguardShowing.value &&
+ keyguardInteractor.isKeyguardDismissible.value
+ }
private fun listenForDozingToGoneViaBiometrics() {
if (KeyguardWmStateRefactor.isEnabled) {
@@ -135,27 +130,20 @@
.debounce(50L)
.filterRelevantKeyguardStateAnd { isAwake -> isAwake }
.sample(
- keyguardInteractor.isKeyguardOccluded,
communalInteractor.isCommunalAvailable,
communalSceneInteractor.isIdleOnCommunal,
- canTransitionToGoneOnWake,
- keyguardInteractor.primaryBouncerShowing,
)
- .collect {
- (
- _,
- occluded,
- isCommunalAvailable,
- isIdleOnCommunal,
- canTransitionToGoneOnWake,
- primaryBouncerShowing) ->
+ .collect { (_, isCommunalAvailable, isIdleOnCommunal) ->
+ val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value
+ val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value
+
if (!deviceEntryInteractor.isLockscreenEnabled()) {
if (SceneContainerFlag.isEnabled) {
// TODO(b/336576536): Check if adaptation for scene framework is needed
} else {
startTransitionTo(KeyguardState.GONE)
}
- } else if (canTransitionToGoneOnWake) {
+ } else if (canDismissLockscreen()) {
if (SceneContainerFlag.isEnabled) {
// TODO(b/336576536): Check if adaptation for scene framework is needed
} else {
@@ -167,7 +155,7 @@
} else {
startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
}
- } else if (occluded) {
+ } else if (isKeyguardOccludedLegacy) {
startTransitionTo(KeyguardState.OCCLUDED)
} else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
if (SceneContainerFlag.isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 17c1e82..7bf9c2f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -32,7 +32,6 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -208,15 +207,15 @@
scope.launch {
keyguardInteractor.isAbleToDream
- .sampleCombine(
- keyguardInteractor.isKeyguardShowing,
- keyguardInteractor.isKeyguardDismissible,
- )
- .filterRelevantKeyguardStateAnd {
- (isDreaming, isKeyguardShowing, isKeyguardDismissible) ->
- !isDreaming && isKeyguardDismissible && !isKeyguardShowing
+ .filterRelevantKeyguardStateAnd { isDreaming -> !isDreaming }
+ .collect {
+ if (
+ keyguardInteractor.isKeyguardDismissible.value &&
+ !keyguardInteractor.isKeyguardShowing.value
+ ) {
+ startTransitionTo(KeyguardState.GONE)
+ }
}
- .collect { startTransitionTo(KeyguardState.GONE) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 0df989e..4cab2bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -216,14 +216,14 @@
/** Whether the keyguard is showing or not. */
@Deprecated("Use KeyguardTransitionInteractor + KeyguardState")
- val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+ val isKeyguardShowing: StateFlow<Boolean> = repository.isKeyguardShowing
/** Whether the keyguard is dismissible or not. */
val isKeyguardDismissible: StateFlow<Boolean> = repository.isKeyguardDismissible
/** Whether the keyguard is occluded (covered by an activity). */
@Deprecated("Use KeyguardTransitionInteractor + KeyguardState.OCCLUDED")
- val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
+ val isKeyguardOccluded: StateFlow<Boolean> = repository.isKeyguardOccluded
/** Whether the keyguard is going away. */
@Deprecated("Use KeyguardTransitionInteractor + KeyguardState.GONE")
@@ -253,7 +253,7 @@
val ambientIndicationVisible: Flow<Boolean> = repository.ambientIndicationVisible.asStateFlow()
/** Whether the primary bouncer is showing or not. */
- @JvmField val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerShow
+ @JvmField val primaryBouncerShowing: StateFlow<Boolean> = bouncerRepository.primaryBouncerShow
/** Whether the alternate bouncer is showing or not. */
val alternateBouncerShowing: Flow<Boolean> =
@@ -274,7 +274,7 @@
val statusBarState: Flow<StatusBarState> = repository.statusBarState
/** Observable for [BiometricUnlockModel] when biometrics are used to unlock the device. */
- val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
+ val biometricUnlockState: StateFlow<BiometricUnlockModel> = repository.biometricUnlockState
/** Keyguard is present and is not occluded. */
val isKeyguardVisible: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index aab5b9b..89851db 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -152,6 +152,62 @@
}
}
}
+
+ if (
+ KeyguardBottomAreaRefactor.isEnabled || DeviceEntryUdfpsRefactor.isEnabled
+ ) {
+ launch("$TAG#alpha") {
+ viewModel.alpha(viewState).collect { alpha ->
+ view.alpha = alpha
+ if (KeyguardBottomAreaRefactor.isEnabled) {
+ childViews[statusViewId]?.alpha = alpha
+ childViews[burnInLayerId]?.alpha = alpha
+ }
+ }
+ }
+ }
+
+ if (MigrateClocksToBlueprint.isEnabled) {
+ launch("$TAG#translationY") {
+ // When translation happens in burnInLayer, it won't be weather clock
+ // large clock isn't added to burnInLayer due to its scale transition
+ // so we also need to add translation to it here
+ // same as translationX
+ viewModel.translationY.collect { y ->
+ childViews[burnInLayerId]?.translationY = y
+ childViews[largeClockId]?.translationY = y
+ childViews[aodNotificationIconContainerId]?.translationY = y
+ }
+ }
+
+ launch("$TAG#translationX") {
+ viewModel.translationX.collect { state ->
+ val px = state.value ?: return@collect
+ when {
+ state.isToOrFrom(KeyguardState.AOD) -> {
+ // Large Clock is not translated in the x direction
+ childViews[burnInLayerId]?.translationX = px
+ childViews[aodNotificationIconContainerId]?.translationX =
+ px
+ }
+ state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
+ for ((key, childView) in childViews.entries) {
+ when (key) {
+ indicationArea,
+ startButton,
+ endButton,
+ lockIcon,
+ deviceEntryIcon -> {
+ // Do not move these views
+ }
+ else -> childView.translationX = px
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
}
disposables +=
@@ -188,20 +244,6 @@
}
}
- if (
- KeyguardBottomAreaRefactor.isEnabled || DeviceEntryUdfpsRefactor.isEnabled
- ) {
- launch {
- viewModel.alpha(viewState).collect { alpha ->
- view.alpha = alpha
- if (KeyguardBottomAreaRefactor.isEnabled) {
- childViews[statusViewId]?.alpha = alpha
- childViews[burnInLayerId]?.alpha = alpha
- }
- }
- }
- }
-
if (MigrateClocksToBlueprint.isEnabled) {
launch {
viewModel.burnInLayerVisibility.collect { visibility ->
@@ -222,46 +264,6 @@
}
launch {
- // When translation happens in burnInLayer, it won't be weather clock
- // large clock isn't added to burnInLayer due to its scale transition
- // so we also need to add translation to it here
- // same as translationX
- viewModel.translationY.collect { y ->
- childViews[burnInLayerId]?.translationY = y
- childViews[largeClockId]?.translationY = y
- childViews[aodNotificationIconContainerId]?.translationY = y
- }
- }
-
- launch {
- viewModel.translationX.collect { state ->
- val px = state.value ?: return@collect
- when {
- state.isToOrFrom(KeyguardState.AOD) -> {
- // Large Clock is not translated in the x direction
- childViews[burnInLayerId]?.translationX = px
- childViews[aodNotificationIconContainerId]?.translationX =
- px
- }
- state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
- for ((key, childView) in childViews.entries) {
- when (key) {
- indicationArea,
- startButton,
- endButton,
- lockIcon,
- deviceEntryIcon -> {
- // Do not move these views
- }
- else -> childView.translationX = px
- }
- }
- }
- }
- }
- }
-
- launch {
viewModel.scale.collect { scaleViewModel ->
if (scaleViewModel.scaleClockOnly) {
// For clocks except weather clock, we have scale transition
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 050ef6f..06f77bf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -270,7 +270,7 @@
occludedToLockscreenTransitionViewModel.lockscreenAlpha,
primaryBouncerToAodTransitionViewModel.lockscreenAlpha,
primaryBouncerToGoneTransitionViewModel.lockscreenAlpha,
- primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
+ primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
)
.onStart { emit(1f) }
) { hideKeyguard, alpha ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index 7511101..d29f512 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
@@ -58,7 +59,14 @@
onStep = { it }
)
- val lockscreenAlpha: Flow<Float> = shortcutsAlpha
+ fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+ var currentAlpha = 0f
+ return transitionAnimation.sharedFlow(
+ duration = 250.milliseconds,
+ onStart = { currentAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(currentAlpha, 1f, it) },
+ )
+ }
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(1f)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
deleted file mode 100644
index e4bafcd..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ /dev/null
@@ -1,298 +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.qs
-
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.os.UserHandle
-import android.provider.Settings
-import android.text.TextUtils
-import android.util.ArraySet
-import android.util.Log
-import androidx.annotation.GuardedBy
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.Dumpable
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.util.UserAwareController
-import com.android.systemui.util.settings.SecureSettings
-import java.io.PrintWriter
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-private const val TAG = "AutoAddTracker"
-private const val DELIMITER = ","
-
-/**
- * Class to track tiles that have been auto-added
- *
- * The list is backed by [Settings.Secure.QS_AUTO_ADDED_TILES].
- *
- * It also handles restore gracefully.
- */
-class AutoAddTracker
-@VisibleForTesting
-constructor(
- private val secureSettings: SecureSettings,
- private val broadcastDispatcher: BroadcastDispatcher,
- private val qsHost: QSHost,
- private val dumpManager: DumpManager,
- private val mainHandler: Handler?,
- private val backgroundExecutor: Executor,
- private var userId: Int
-) : UserAwareController, Dumpable {
-
- companion object {
- private val FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
- }
-
- @GuardedBy("autoAdded") private val autoAdded = ArraySet<String>()
- private var restoredTiles: Map<String, AutoTile>? = null
-
- override val currentUserId: Int
- get() = userId
-
- private val contentObserver =
- object : ContentObserver(mainHandler) {
- override fun onChange(
- selfChange: Boolean,
- uris: Collection<Uri>,
- flags: Int,
- _userId: Int
- ) {
- if (_userId != userId) {
- // Ignore changes outside of our user. We'll load the correct value on user
- // change
- return
- }
- loadTiles()
- }
- }
-
- private val restoreReceiver =
- object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action != Intent.ACTION_SETTING_RESTORED) return
- processRestoreIntent(intent)
- }
- }
-
- private fun processRestoreIntent(intent: Intent) {
- when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
- Settings.Secure.QS_TILES -> {
- restoredTiles =
- intent
- .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
- ?.split(DELIMITER)
- ?.mapIndexed(::AutoTile)
- ?.associateBy(AutoTile::tileType)
- ?: run {
- Log.w(TAG, "Null restored tiles for user $userId")
- emptyMap()
- }
- }
- Settings.Secure.QS_AUTO_ADDED_TILES -> {
- restoredTiles?.let { restoredTiles ->
- val restoredAutoAdded =
- intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)?.split(DELIMITER)
- ?: emptyList()
- val autoAddedBeforeRestore =
- intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)?.split(DELIMITER)
- ?: emptyList()
-
- val tilesToRemove = restoredAutoAdded.filter { it !in restoredTiles }
- if (tilesToRemove.isNotEmpty()) {
- Log.d(TAG, "Removing tiles: $tilesToRemove")
- qsHost.removeTiles(tilesToRemove)
- }
- val tiles =
- synchronized(autoAdded) {
- autoAdded.clear()
- autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
- getTilesFromListLocked()
- }
- saveTiles(tiles)
- }
- ?: run {
- Log.w(
- TAG,
- "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
- "${Settings.Secure.QS_TILES} for user $userId"
- )
- }
- }
- else -> {} // Do nothing for other Settings
- }
- }
-
- /** Init method must be called after construction to start listening */
- fun initialize() {
- dumpManager.registerDumpable(TAG, this)
- loadTiles()
- secureSettings.registerContentObserverForUserSync(
- secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
- contentObserver,
- UserHandle.USER_ALL
- )
- registerBroadcastReceiver()
- }
-
- /** Unregister listeners, receivers and observers */
- fun destroy() {
- dumpManager.unregisterDumpable(TAG)
- secureSettings.unregisterContentObserverSync(contentObserver)
- unregisterBroadcastReceiver()
- }
-
- private fun registerBroadcastReceiver() {
- broadcastDispatcher.registerReceiver(
- restoreReceiver,
- FILTER,
- backgroundExecutor,
- UserHandle.of(userId)
- )
- }
-
- private fun unregisterBroadcastReceiver() {
- broadcastDispatcher.unregisterReceiver(restoreReceiver)
- }
-
- override fun changeUser(newUser: UserHandle) {
- if (newUser.identifier == userId) return
- unregisterBroadcastReceiver()
- userId = newUser.identifier
- restoredTiles = null
- loadTiles()
- registerBroadcastReceiver()
- }
-
- fun getRestoredTilePosition(tile: String): Int =
- restoredTiles?.get(tile)?.index ?: QSHost.POSITION_AT_END
-
- /** Returns `true` if the tile has been auto-added before */
- fun isAdded(tile: String): Boolean {
- return synchronized(autoAdded) { tile in autoAdded }
- }
-
- /**
- * Sets a tile as auto-added.
- *
- * From here on, [isAdded] will return true for that tile.
- */
- fun setTileAdded(tile: String) {
- val tiles =
- synchronized(autoAdded) {
- if (autoAdded.add(tile)) {
- getTilesFromListLocked()
- } else {
- null
- }
- }
- tiles?.let { saveTiles(it) }
- }
-
- /**
- * Removes a tile from the list of auto-added.
- *
- * This allows for this tile to be auto-added again in the future.
- */
- fun setTileRemoved(tile: String) {
- val tiles =
- synchronized(autoAdded) {
- if (autoAdded.remove(tile)) {
- getTilesFromListLocked()
- } else {
- null
- }
- }
- tiles?.let { saveTiles(it) }
- }
-
- private fun getTilesFromListLocked(): String {
- return TextUtils.join(DELIMITER, autoAdded)
- }
-
- private fun saveTiles(tiles: String) {
- secureSettings.putStringForUser(
- Settings.Secure.QS_AUTO_ADDED_TILES,
- tiles,
- /* tag */ null,
- /* makeDefault */ false,
- userId,
- /* overrideableByRestore */ true
- )
- }
-
- private fun loadTiles() {
- synchronized(autoAdded) {
- autoAdded.clear()
- autoAdded.addAll(getAdded())
- }
- }
-
- private fun getAdded(): Collection<String> {
- val current = secureSettings.getStringForUser(Settings.Secure.QS_AUTO_ADDED_TILES, userId)
- return current?.split(DELIMITER) ?: emptySet()
- }
-
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println("Current user: $userId")
- pw.println("Restored tiles: $restoredTiles")
- pw.println("Added tiles: $autoAdded")
- }
-
- @SysUISingleton
- class Builder
- @Inject
- constructor(
- private val secureSettings: SecureSettings,
- private val broadcastDispatcher: BroadcastDispatcher,
- private val qsHost: QSHost,
- private val dumpManager: DumpManager,
- @Main private val handler: Handler,
- @Background private val executor: Executor
- ) {
- private var userId: Int = 0
-
- fun setUserId(_userId: Int): Builder {
- userId = _userId
- return this
- }
-
- fun build(): AutoAddTracker {
- return AutoAddTracker(
- secureSettings,
- broadcastDispatcher,
- qsHost,
- dumpManager,
- handler,
- executor,
- userId
- )
- }
- }
-
- private data class AutoTile(val index: Int, val tileType: String)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index c77233e..4323b31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -47,12 +47,10 @@
class QSHostAdapter
@Inject
constructor(
- private val qsTileHost: QSTileHost,
private val interactor: CurrentTilesInteractor,
private val context: Context,
private val tileServiceRequestControllerBuilder: TileServiceRequestController.Builder,
@Application private val scope: CoroutineScope,
- flags: QSPipelineFlagsRepository,
dumpManager: DumpManager,
) : QSHost {
@@ -60,123 +58,69 @@
private const val TAG = "QSTileHost"
}
- private val useNewHost = flags.pipelineEnabled
-
@GuardedBy("callbacksMap") private val callbacksMap = mutableMapOf<QSHost.Callback, Job>()
init {
scope.launch { tileServiceRequestControllerBuilder.create(this@QSHostAdapter).init() }
// Redirect dump to the correct host (needed for CTS tests)
- dumpManager.registerCriticalDumpable(TAG, if (useNewHost) interactor else qsTileHost)
+ dumpManager.registerCriticalDumpable(TAG, interactor)
}
override fun getTiles(): Collection<QSTile> {
- return if (useNewHost) {
- interactor.currentQSTiles
- } else {
- qsTileHost.getTiles()
- }
+ return interactor.currentQSTiles
}
override fun getSpecs(): List<String> {
- return if (useNewHost) {
- interactor.currentTilesSpecs.map { it.spec }
- } else {
- qsTileHost.getSpecs()
- }
+ return interactor.currentTilesSpecs.map { it.spec }
}
override fun removeTile(spec: String) {
- if (useNewHost) {
- interactor.removeTiles(listOf(TileSpec.create(spec)))
- } else {
- qsTileHost.removeTile(spec)
- }
+ interactor.removeTiles(listOf(TileSpec.create(spec)))
}
override fun addCallback(callback: QSHost.Callback) {
- if (useNewHost) {
- val job = scope.launch { interactor.currentTiles.collect { callback.onTilesChanged() } }
- synchronized(callbacksMap) { callbacksMap.put(callback, job) }
- } else {
- qsTileHost.addCallback(callback)
- }
+ val job = scope.launch { interactor.currentTiles.collect { callback.onTilesChanged() } }
+ synchronized(callbacksMap) { callbacksMap.put(callback, job) }
}
override fun removeCallback(callback: QSHost.Callback) {
- if (useNewHost) {
- synchronized(callbacksMap) { callbacksMap.remove(callback)?.cancel() }
- } else {
- qsTileHost.removeCallback(callback)
- }
+ synchronized(callbacksMap) { callbacksMap.remove(callback)?.cancel() }
}
override fun removeTiles(specs: Collection<String>) {
- if (useNewHost) {
- interactor.removeTiles(specs.map(TileSpec::create))
- } else {
- qsTileHost.removeTiles(specs)
- }
+ interactor.removeTiles(specs.map(TileSpec::create))
}
override fun removeTileByUser(component: ComponentName) {
- if (useNewHost) {
- interactor.removeTiles(listOf(TileSpec.create(component)))
- } else {
- qsTileHost.removeTileByUser(component)
- }
+ interactor.removeTiles(listOf(TileSpec.create(component)))
}
override fun addTile(spec: String, position: Int) {
- if (useNewHost) {
- interactor.addTile(TileSpec.create(spec), position)
- } else {
- qsTileHost.addTile(spec, position)
- }
+ interactor.addTile(TileSpec.create(spec), position)
}
override fun addTile(component: ComponentName, end: Boolean) {
- if (useNewHost) {
- interactor.addTile(TileSpec.create(component), if (end) POSITION_AT_END else 0)
- } else {
- qsTileHost.addTile(component, end)
- }
+ interactor.addTile(TileSpec.create(component), if (end) POSITION_AT_END else 0)
}
override fun changeTilesByUser(previousTiles: List<String>, newTiles: List<String>) {
- if (useNewHost) {
- interactor.setTiles(newTiles.map(TileSpec::create))
- } else {
- qsTileHost.changeTilesByUser(previousTiles, newTiles)
- }
+ interactor.setTiles(newTiles.map(TileSpec::create))
}
override fun getContext(): Context {
- return if (useNewHost) {
- context
- } else {
- qsTileHost.context
- }
+ return context
}
override fun getUserContext(): Context {
- return if (useNewHost) {
- interactor.userContext.value
- } else {
- qsTileHost.userContext
- }
+ return interactor.userContext.value
}
override fun getUserId(): Int {
- return if (useNewHost) {
- interactor.userId.value
- } else {
- qsTileHost.userId
- }
+ return interactor.userId.value
}
override fun createTile(tileSpec: String): QSTile? {
- return qsTileHost.createTile(tileSpec)
+ return interactor.createTileSync(TileSpec.create(tileSpec))
}
override fun addTile(spec: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
deleted file mode 100644
index 03c2aa6..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ /dev/null
@@ -1,630 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.Log;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dumpable;
-import com.android.systemui.ProtoDumpable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.nano.SystemUIProtoDump;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.qs.QSFactory;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.external.CustomTileStatePersister;
-import com.android.systemui.qs.external.TileLifecycleManager;
-import com.android.systemui.qs.external.TileServiceKey;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.nano.QsTileState;
-import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
-import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
-import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
-import com.android.systemui.qs.tiles.di.NewQSTileFactory;
-import com.android.systemui.res.R;
-import com.android.systemui.settings.UserFileManager;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.settings.SecureSettings;
-
-import dagger.Lazy;
-
-import org.jetbrains.annotations.NotNull;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-import javax.inject.Inject;
-import javax.inject.Provider;
-
-/** Platform implementation of the quick settings tile host
- *
- * This class keeps track of the set of current tiles and is the in memory source of truth
- * (ground truth is kept in {@link Secure#QS_TILES}). When the ground truth changes,
- * {@link #onTuningChanged} will be called and the tiles will be re-created as needed.
- *
- * This class also provides the interface for adding/removing/changing tiles.
- */
-@SysUISingleton
-public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, ProtoDumpable,
- PanelInteractor, CustomTileAddedRepository {
- private static final String TAG = "QSTileHost";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- // Shared prefs that hold tile lifecycle info.
- @VisibleForTesting
- static final String TILES = "tiles_prefs";
-
- private final Context mContext;
- private final LinkedHashMap<String, QSTile> mTiles = new LinkedHashMap<>();
- private final ArrayList<String> mTileSpecs = new ArrayList<>();
- private final TunerService mTunerService;
- private final PluginManager mPluginManager;
- private final QSLogger mQSLogger;
- private final CustomTileStatePersister mCustomTileStatePersister;
- private final Executor mMainExecutor;
- private final UserFileManager mUserFileManager;
-
- private final List<Callback> mCallbacks = new ArrayList<>();
- @Nullable
- private AutoTileManager mAutoTiles;
- private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
- private int mCurrentUser;
- private final Lazy<ShadeController> mShadeControllerProvider;
- private Context mUserContext;
- private UserTracker mUserTracker;
- private SecureSettings mSecureSettings;
- // Keep track of whether mTilesList contains the same information as the Settings value.
- // This is a performance optimization to reduce the number of blocking calls to Settings from
- // main thread.
- // This is enforced by only cleaning the flag at the end of a successful run of #onTuningChanged
- private boolean mTilesListDirty = true;
-
- private TileLifecycleManager.Factory mTileLifeCycleManagerFactory;
-
- private final QSPipelineFlagsRepository mFeatureFlags;
-
- @Inject
- public QSTileHost(Context context,
- Lazy<NewQSTileFactory> newQsTileFactoryProvider,
- QSFactory defaultFactory,
- @Main Executor mainExecutor,
- PluginManager pluginManager,
- TunerService tunerService,
- Provider<AutoTileManager> autoTiles,
- Lazy<ShadeController> shadeControllerProvider,
- QSLogger qsLogger,
- UserTracker userTracker,
- SecureSettings secureSettings,
- CustomTileStatePersister customTileStatePersister,
- TileLifecycleManager.Factory tileLifecycleManagerFactory,
- UserFileManager userFileManager,
- QSPipelineFlagsRepository featureFlags
- ) {
- mContext = context;
- mUserContext = context;
- mTunerService = tunerService;
- mPluginManager = pluginManager;
- mQSLogger = qsLogger;
- mMainExecutor = mainExecutor;
- mTileLifeCycleManagerFactory = tileLifecycleManagerFactory;
- mUserFileManager = userFileManager;
- mFeatureFlags = featureFlags;
-
- mShadeControllerProvider = shadeControllerProvider;
-
- if (featureFlags.getTilesEnabled()) {
- mQsFactories.add(newQsTileFactoryProvider.get());
- }
- mQsFactories.add(defaultFactory);
- pluginManager.addPluginListener(this, QSFactory.class, true);
- mUserTracker = userTracker;
- mCurrentUser = userTracker.getUserId();
- mSecureSettings = secureSettings;
- mCustomTileStatePersister = customTileStatePersister;
-
- mainExecutor.execute(() -> {
- // This is technically a hack to avoid circular dependency of
- // QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
- // finishes before creating any tiles.
- tunerService.addTunable(this, TILES_SETTING);
- // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
- if (!mFeatureFlags.getPipelineEnabled()) {
- mAutoTiles = autoTiles.get();
- }
- });
- }
-
- public void destroy() {
- mTiles.values().forEach(tile -> tile.destroy());
- mAutoTiles.destroy();
- mTunerService.removeTunable(this);
- mPluginManager.removePluginListener(this);
- }
-
- @Override
- public void onPluginConnected(QSFactory plugin, Context pluginContext) {
- // Give plugins priority over creation so they can override if they wish.
- mQsFactories.add(0, plugin);
- String value = mTunerService.getValue(TILES_SETTING);
- // Force remove and recreate of all tiles.
- onTuningChanged(TILES_SETTING, "");
- onTuningChanged(TILES_SETTING, value);
- }
-
- @Override
- public void onPluginDisconnected(QSFactory plugin) {
- mQsFactories.remove(plugin);
- // Force remove and recreate of all tiles.
- String value = mTunerService.getValue(TILES_SETTING);
- onTuningChanged(TILES_SETTING, "");
- onTuningChanged(TILES_SETTING, value);
- }
-
- @Override
- public void addCallback(Callback callback) {
- mCallbacks.add(callback);
- }
-
- @Override
- public void removeCallback(Callback callback) {
- mCallbacks.remove(callback);
- }
-
- @Override
- public Collection<QSTile> getTiles() {
- return mTiles.values();
- }
-
- @Override
- public void collapsePanels() {
- mShadeControllerProvider.get().postAnimateCollapseShade();
- }
-
- @Override
- public void forceCollapsePanels() {
- mShadeControllerProvider.get().postAnimateForceCollapseShade();
- }
-
- @Override
- public void openPanels() {
- mShadeControllerProvider.get().postAnimateExpandQs();
- }
-
- @Override
- public Context getContext() {
- return mContext;
- }
-
- @Override
- public Context getUserContext() {
- return mUserContext;
- }
-
- @Override
- public int getUserId() {
- return mCurrentUser;
- }
-
- public int indexOf(String spec) {
- return mTileSpecs.indexOf(spec);
- }
-
- /**
- * Whenever the Secure Setting keeping track of the current tiles changes (or upon start) this
- * will be called with the new value of the setting.
- *
- * This method will do the following:
- * <ol>
- * <li>Destroy any existing tile that's not one of the current tiles (in the setting)</li>
- * <li>Create new tiles for those that don't already exist. If this tiles end up being
- * not available, they'll also be destroyed.</li>
- * <li>Save the resolved list of tiles (current tiles that are available) into the setting.
- * This means that after this call ends, the tiles in the Setting, {@link #mTileSpecs},
- * and visible tiles ({@link #mTiles}) must match.
- * </li>
- * </ol>
- *
- * Additionally, if the user has changed, it'll do the following:
- * <ul>
- * <li>Change the user for SystemUI tiles: {@link QSTile#userSwitch}.</li>
- * <li>Destroy any {@link CustomTile} and recreate it for the new user.</li>
- * </ul>
- *
- * This happens in main thread as {@link com.android.systemui.tuner.TunerServiceImpl} dispatches
- * in main thread.
- *
- * @see QSTile#isAvailable
- */
- @MainThread
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (!TILES_SETTING.equals(key)) {
- return;
- }
- int currentUser = mUserTracker.getUserId();
- if (currentUser != mCurrentUser) {
- mUserContext = mUserTracker.getUserContext();
- if (mAutoTiles != null) {
- mAutoTiles.changeUser(UserHandle.of(currentUser));
- }
- }
- // Do not process tiles if the flag is enabled.
- if (mFeatureFlags.getPipelineEnabled()) {
- return;
- }
- QSPipelineFlagsRepository.Utils.assertInLegacyMode();
- if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
- newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
- }
- final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
- if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
- Log.d(TAG, "Recreating tiles: " + tileSpecs);
- mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
- tile -> {
- Log.d(TAG, "Destroying tile: " + tile.getKey());
- mQSLogger.logTileDestroyed(tile.getKey(), "Tile removed");
- tile.getValue().destroy();
- });
- final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
- for (String tileSpec : tileSpecs) {
- QSTile tile = mTiles.get(tileSpec);
- if (tile != null && (!(tile instanceof CustomTile)
- || ((CustomTile) tile).getUser() == currentUser)) {
- if (tile.isAvailable()) {
- Log.d(TAG, "Adding " + tile);
- tile.removeCallbacks();
- if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
- tile.userSwitch(currentUser);
- }
- newTiles.put(tileSpec, tile);
- mQSLogger.logTileAdded(tileSpec);
- } else {
- tile.destroy();
- Log.d(TAG, "Destroying not available tile: " + tileSpec);
- mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
- }
- } else {
- // This means that the tile is a CustomTile AND the user is different, so let's
- // destroy it
- if (tile != null) {
- tile.destroy();
- Log.d(TAG, "Destroying tile for wrong user: " + tileSpec);
- mQSLogger.logTileDestroyed(tileSpec, "Tile for wrong user");
- }
- Log.d(TAG, "Creating tile: " + tileSpec);
- try {
- tile = createTile(tileSpec);
- if (tile != null) {
- if (tile.isAvailable()) {
- newTiles.put(tileSpec, tile);
- mQSLogger.logTileAdded(tileSpec);
- } else {
- tile.destroy();
- Log.d(TAG, "Destroying not available tile: " + tileSpec);
- mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
- }
- } else {
- Log.d(TAG, "No factory for a spec: " + tileSpec);
- }
- } catch (Throwable t) {
- Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
- }
- }
- }
- mCurrentUser = currentUser;
- List<String> currentSpecs = new ArrayList<>(mTileSpecs);
- mTileSpecs.clear();
- mTileSpecs.addAll(newTiles.keySet()); // Only add the valid (available) tiles.
- mTiles.clear();
- mTiles.putAll(newTiles);
- if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
- // If we didn't manage to create any tiles, set it to empty (default)
- Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
- changeTilesByUser(currentSpecs, loadTileSpecs(mContext, ""));
- } else {
- String resolvedTiles = TextUtils.join(",", mTileSpecs);
- if (!resolvedTiles.equals(newValue)) {
- // If the resolved tiles (those we actually ended up with) are different than
- // the ones that are in the setting, update the Setting.
- saveTilesToSettings(mTileSpecs);
- }
- mTilesListDirty = false;
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onTilesChanged();
- }
- }
- }
-
- /**
- * Only use with [CustomTile] if the tile doesn't exist anymore (and therefore doesn't need
- * its lifecycle terminated).
- */
- @Override
- public void removeTile(String spec) {
- if (spec.startsWith(CustomTile.PREFIX)) {
- // If the tile is removed (due to it not actually existing), mark it as removed. That
- // way it will be marked as newly added if it appears in the future.
- setTileAdded(CustomTile.getComponentFromSpec(spec), mCurrentUser, false);
- }
- mMainExecutor.execute(() -> changeTileSpecs(tileSpecs-> tileSpecs.remove(spec)));
- }
-
- /**
- * Remove many tiles at once.
- *
- * It will only save to settings once (as opposed to {@link QSTileHost#removeTileByUser} called
- * multiple times).
- */
- @Override
- public void removeTiles(Collection<String> specs) {
- mMainExecutor.execute(() -> changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs)));
- }
-
- /**
- * Add a tile to the end
- *
- * @param spec string matching a pre-defined tilespec
- */
- public void addTile(String spec) {
- addTile(spec, POSITION_AT_END);
- }
-
- @Override
- public void addTile(String spec, int requestPosition) {
- mMainExecutor.execute(() ->
- changeTileSpecs(tileSpecs -> {
- if (tileSpecs.contains(spec)) return false;
-
- int size = tileSpecs.size();
- if (requestPosition == POSITION_AT_END || requestPosition >= size) {
- tileSpecs.add(spec);
- } else {
- tileSpecs.add(requestPosition, spec);
- }
- return true;
- })
- );
- }
-
- // When calling this, you may want to modify mTilesListDirty accordingly.
- @MainThread
- private void saveTilesToSettings(List<String> tileSpecs) {
- Log.d(TAG, "Saving tiles: " + tileSpecs + " for user: " + mCurrentUser);
- mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
- null /* tag */, false /* default */, mCurrentUser,
- true /* overrideable by restore */);
- }
-
- @MainThread
- private void changeTileSpecs(Predicate<List<String>> changeFunction) {
- final List<String> tileSpecs;
- if (!mTilesListDirty) {
- tileSpecs = new ArrayList<>(mTileSpecs);
- } else {
- tileSpecs = loadTileSpecs(mContext,
- mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser));
- }
- if (changeFunction.test(tileSpecs)) {
- mTilesListDirty = true;
- saveTilesToSettings(tileSpecs);
- }
- }
-
- @Override
- public void addTile(ComponentName tile) {
- addTile(tile, /* end */ false);
- }
-
- @Override
- public void addTile(ComponentName tile, boolean end) {
- String spec = CustomTile.toSpec(tile);
- addTile(spec, end ? POSITION_AT_END : 0);
- }
-
- /**
- * This will call through {@link #changeTilesByUser}. It should only be used when a tile is
- * removed by a <b>user action</b> like {@code adb}.
- */
- @Override
- public void removeTileByUser(ComponentName tile) {
- mMainExecutor.execute(() -> {
- List<String> newSpecs = new ArrayList<>(mTileSpecs);
- if (newSpecs.remove(CustomTile.toSpec(tile))) {
- changeTilesByUser(mTileSpecs, newSpecs);
- }
- });
- }
-
- /**
- * Change the tiles triggered by the user editing.
- * <p>
- * This is not called on device start, or on user change.
- *
- * {@link android.service.quicksettings.TileService#onTileRemoved} will be called for tiles
- * that are removed.
- */
- @MainThread
- @Override
- public void changeTilesByUser(List<String> previousTiles, List<String> newTiles) {
- final List<String> copy = new ArrayList<>(previousTiles);
- final int NP = copy.size();
- for (int i = 0; i < NP; i++) {
- String tileSpec = copy.get(i);
- if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
- if (!newTiles.contains(tileSpec)) {
- ComponentName component = CustomTile.getComponentFromSpec(tileSpec);
- Intent intent = new Intent().setComponent(component);
- TileLifecycleManager lifecycleManager = mTileLifeCycleManagerFactory.create(
- intent, new UserHandle(mCurrentUser));
- lifecycleManager.onStopListening();
- lifecycleManager.onTileRemoved();
- mCustomTileStatePersister.removeState(new TileServiceKey(component, mCurrentUser));
- setTileAdded(component, mCurrentUser, false);
- lifecycleManager.flushMessagesAndUnbind();
- }
- }
- Log.d(TAG, "saveCurrentTiles " + newTiles);
- mTilesListDirty = true;
- saveTilesToSettings(newTiles);
- }
-
- @Nullable
- @Override
- public QSTile createTile(String tileSpec) {
- for (int i = 0; i < mQsFactories.size(); i++) {
- QSTile t = mQsFactories.get(i).createTile(tileSpec);
- if (t != null) {
- return t;
- }
- }
- return null;
- }
-
- /**
- * Check if a particular {@link CustomTile} has been added for a user and has not been removed
- * since.
- * @param componentName the {@link ComponentName} of the
- * {@link android.service.quicksettings.TileService} associated with the
- * tile.
- * @param userId the user to check
- */
- @Override
- public boolean isTileAdded(ComponentName componentName, int userId) {
- return mUserFileManager
- .getSharedPreferences(TILES, 0, userId)
- .getBoolean(componentName.flattenToString(), false);
- }
-
- /**
- * Persists whether a particular {@link CustomTile} has been added and it's currently in the
- * set of selected tiles ({@link #mTiles}.
- * @param componentName the {@link ComponentName} of the
- * {@link android.service.quicksettings.TileService} associated
- * with the tile.
- * @param userId the user for this tile
- * @param added {@code true} if the tile is being added, {@code false} otherwise
- */
- @Override
- public void setTileAdded(ComponentName componentName, int userId, boolean added) {
- mUserFileManager.getSharedPreferences(TILES, 0, userId)
- .edit()
- .putBoolean(componentName.flattenToString(), added)
- .apply();
- }
-
- @Override
- public List<String> getSpecs() {
- return mTileSpecs;
- }
-
- protected static List<String> loadTileSpecs(Context context, String tileList) {
- final Resources res = context.getResources();
-
- if (TextUtils.isEmpty(tileList)) {
- tileList = res.getString(R.string.quick_settings_tiles);
- Log.d(TAG, "Loaded tile specs from default config: " + tileList);
- } else {
- Log.d(TAG, "Loaded tile specs from setting: " + tileList);
- }
- final ArrayList<String> tiles = new ArrayList<String>();
- boolean addedDefault = false;
- Set<String> addedSpecs = new ArraySet<>();
- for (String tile : tileList.split(",")) {
- tile = tile.trim();
- if (tile.isEmpty()) continue;
- if (tile.equals("default")) {
- if (!addedDefault) {
- List<String> defaultSpecs = QSHost.getDefaultSpecs(context.getResources());
- for (String spec : defaultSpecs) {
- if (!addedSpecs.contains(spec)) {
- tiles.add(spec);
- addedSpecs.add(spec);
- }
- }
- addedDefault = true;
- }
- } else {
- if (!addedSpecs.contains(tile)) {
- tiles.add(tile);
- addedSpecs.add(tile);
- }
- }
- }
-
- if (!tiles.contains("internet")) {
- if (tiles.contains("wifi")) {
- // Replace the WiFi with Internet, and remove the Cell
- tiles.set(tiles.indexOf("wifi"), "internet");
- tiles.remove("cell");
- } else if (tiles.contains("cell")) {
- // Replace the Cell with Internet
- tiles.set(tiles.indexOf("cell"), "internet");
- }
- } else {
- tiles.remove("wifi");
- tiles.remove("cell");
- }
- return tiles;
- }
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- pw.println("QSTileHost:");
- pw.println("tile specs: " + mTileSpecs);
- pw.println("current user: " + mCurrentUser);
- pw.println("is dirty: " + mTilesListDirty);
- pw.println("tiles:");
- mTiles.values().stream().filter(obj -> obj instanceof Dumpable)
- .forEach(o -> ((Dumpable) o).dump(pw, args));
- }
-
- @Override
- public void dumpProto(@NotNull SystemUIProtoDump systemUIProtoDump, @NotNull String[] args) {
- List<QsTileState> data = mTiles.values().stream()
- .map(QSTile::getState)
- .map(TileStateToProtoKt::toProto)
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
-
- systemUIProtoDump.tiles = data.toArray(new QsTileState[0]);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
index 496a6f8..a947d36 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
@@ -18,17 +18,14 @@
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QSHostAdapter
-import com.android.systemui.qs.QSTileHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.QsEventLoggerImpl
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedSharedPrefsRepository
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl
-import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import dagger.Binds
import dagger.Module
-import dagger.Provides
@Module
interface QSHostModule {
@@ -37,36 +34,10 @@
@Binds fun provideEventLogger(impl: QsEventLoggerImpl): QsEventLogger
- @Module
- companion object {
- private const val MAX_QS_INSTANCE_ID = 1 shl 20
+ @Binds fun providePanelInteractor(impl: PanelInteractorImpl): PanelInteractor
- @Provides
- @JvmStatic
- fun providePanelInteractor(
- featureFlags: QSPipelineFlagsRepository,
- qsHost: QSTileHost,
- panelInteractorImpl: PanelInteractorImpl
- ): PanelInteractor {
- return if (featureFlags.pipelineEnabled) {
- panelInteractorImpl
- } else {
- qsHost
- }
- }
-
- @Provides
- @JvmStatic
- fun provideCustomTileAddedRepository(
- featureFlags: QSPipelineFlagsRepository,
- qsHost: QSTileHost,
- customTileAddedRepository: CustomTileAddedSharedPrefsRepository
- ): CustomTileAddedRepository {
- return if (featureFlags.pipelineEnabled) {
- customTileAddedRepository
- } else {
- qsHost
- }
- }
- }
+ @Binds
+ fun provideCustomTileAddedRepository(
+ impl: CustomTileAddedSharedPrefsRepository
+ ): CustomTileAddedRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index b705a03..29bcad4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -16,17 +16,7 @@
package com.android.systemui.qs.dagger;
-import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
-
-import android.content.Context;
-import android.os.Handler;
-
-import com.android.systemui.dagger.NightDisplayListenerModule;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.media.dagger.MediaModule;
-import com.android.systemui.qs.AutoAddTracker;
-import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.ReduceBrightColorsControllerImpl;
import com.android.systemui.qs.external.QSExternalModule;
@@ -36,24 +26,12 @@
import com.android.systemui.qs.tiles.di.QSTilesModule;
import com.android.systemui.qs.ui.adapter.QSSceneAdapter;
import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl;
-import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.DeviceControlsController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.SafetyController;
-import com.android.systemui.statusbar.policy.WalletController;
-import com.android.systemui.util.settings.SecureSettings;
-
-import dagger.Binds;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.Multibinds;
import java.util.Map;
-import javax.inject.Named;
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.Multibinds;
/**
* Module for QS dependencies
@@ -78,45 +56,6 @@
@Multibinds
Map<String, QSTileImpl<?>> tileMap();
- @Provides
- @SysUISingleton
- static AutoTileManager provideAutoTileManager(
- Context context,
- AutoAddTracker.Builder autoAddTrackerBuilder,
- QSHost host,
- @Background Handler handler,
- SecureSettings secureSettings,
- HotspotController hotspotController,
- DataSaverController dataSaverController,
- ManagedProfileController managedProfileController,
- NightDisplayListenerModule.Builder nightDisplayListenerBuilder,
- CastController castController,
- ReduceBrightColorsController reduceBrightColorsController,
- DeviceControlsController deviceControlsController,
- WalletController walletController,
- SafetyController safetyController,
- @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
- AutoTileManager manager = new AutoTileManager(
- context,
- autoAddTrackerBuilder,
- host,
- handler,
- secureSettings,
- hotspotController,
- dataSaverController,
- managedProfileController,
- nightDisplayListenerBuilder,
- castController,
- reduceBrightColorsController,
- deviceControlsController,
- walletController,
- safetyController,
- isReduceBrightColorsAvailable
- );
- manager.init();
- return manager;
- }
-
@Binds
QSSceneAdapter bindsQsSceneInteractor(QSSceneAdapterImpl impl);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 02379e6..4a96710 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -116,6 +116,8 @@
*/
fun setTiles(specs: List<TileSpec>)
+ fun createTileSync(spec: TileSpec): QSTile?
+
companion object {
val POSITION_AT_END: Int = TileSpecRepository.POSITION_AT_END
}
@@ -190,9 +192,7 @@
}
init {
- if (featureFlags.pipelineEnabled) {
- startTileCollection()
- }
+ startTileCollection()
}
private fun startTileCollection() {
@@ -342,15 +342,16 @@
lifecycleManager.flushMessagesAndUnbind()
}
+ override fun createTileSync(spec: TileSpec): QSTile? {
+ return if (featureFlags.tilesEnabled) {
+ newQSTileFactory.get().createTile(spec.spec)
+ } else {
+ null
+ } ?: tileFactory.createTile(spec.spec)
+ }
+
private suspend fun createTile(spec: TileSpec): QSTile? {
- val tile =
- withContext(mainDispatcher) {
- if (featureFlags.tilesEnabled) {
- newQSTileFactory.get().createTile(spec.spec)
- } else {
- null
- } ?: tileFactory.createTile(spec.spec)
- }
+ val tile = withContext(mainDispatcher) { createTileSync(spec) }
if (tile == null) {
logger.logTileNotFoundInFactory(spec)
return null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
index c8fbeb5..0bcb6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
@@ -40,14 +40,12 @@
) : CoreStartable {
override fun start() {
- if (featureFlags.pipelineEnabled) {
- accessibilityTilesInteractor.init(currentTilesInteractor)
- autoAddInteractor.init(currentTilesInteractor)
- restoreReconciliationInteractor.start()
+ accessibilityTilesInteractor.init(currentTilesInteractor)
+ autoAddInteractor.init(currentTilesInteractor)
+ restoreReconciliationInteractor.start()
- if (NewQsUI.isEnabled) {
- gridConsistencyInteractor.start()
- }
+ if (NewQsUI.isEnabled) {
+ gridConsistencyInteractor.start()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
index 42bee3c..5dc8d1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
@@ -9,19 +9,10 @@
@SysUISingleton
class QSPipelineFlagsRepository @Inject constructor() {
- val pipelineEnabled: Boolean
- get() = AconfigFlags.qsNewPipeline()
-
val tilesEnabled: Boolean
get() = AconfigFlags.qsNewTiles()
companion object Utils {
- fun assertInLegacyMode() =
- RefactorFlagUtils.assertInLegacyMode(
- AconfigFlags.qsNewPipeline(),
- AconfigFlags.FLAG_QS_NEW_PIPELINE
- )
-
fun assertNewTiles() =
RefactorFlagUtils.assertInNewMode(
AconfigFlags.qsNewTiles(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt
new file mode 100644
index 0000000..af55f5a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.ui.viewmodel
+
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.scene.domain.interactor.SceneBackInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+/**
+ * Models the UI state needed to figure out which user actions can trigger navigation from the quick
+ * settings scene to other scenes.
+ *
+ * Different from [QuickSettingsSceneContentViewModel] that models UI state needed for rendering the
+ * content of the quick settings scene.
+ */
+class QuickSettingsSceneActionsViewModel
+@AssistedInject
+constructor(
+ private val qsSceneAdapter: QSSceneAdapter,
+ sceneBackInteractor: SceneBackInteractor,
+) : SceneActionsViewModel() {
+
+ private val backScene: Flow<SceneKey> =
+ sceneBackInteractor.backScene
+ .filter { it != Scenes.QuickSettings }
+ .map { it ?: Scenes.Shade }
+
+ override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+ combine(
+ qsSceneAdapter.isCustomizerShowing,
+ backScene,
+ ) { isCustomizing, backScene ->
+ buildMap<UserAction, UserActionResult> {
+ if (isCustomizing) {
+ // TODO(b/332749288) Empty map so there are no back handlers and back can
+ // close
+ // customizer
+
+ // TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
+ // while customizing
+ } else {
+ put(Back, UserActionResult(backScene))
+ put(Swipe(SwipeDirection.Up), UserActionResult(backScene))
+ put(
+ Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up),
+ UserActionResult(SceneFamilies.Home),
+ )
+ }
+ }
+ }
+ .collectLatest { actions -> setActions(actions) }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): QuickSettingsSceneActionsViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt
new file mode 100644
index 0000000..55b8f5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.ui.viewmodel
+
+import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.lifecycle.SysUiViewModel
+import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
+import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Models UI state needed for rendering the content of the quick settings scene.
+ *
+ * Different from [QuickSettingsSceneActionsViewModel] that models the UI state needed to figure out
+ * which user actions can trigger navigation to other scenes.
+ */
+class QuickSettingsSceneContentViewModel
+@AssistedInject
+constructor(
+ val brightnessMirrorViewModelFactory: BrightnessMirrorViewModel.Factory,
+ val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
+ val qsSceneAdapter: QSSceneAdapter,
+ private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
+ private val footerActionsController: FooterActionsController,
+ val mediaCarouselInteractor: MediaCarouselInteractor,
+) : SysUiViewModel() {
+
+ val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation
+
+ private val footerActionsControllerInitialized = AtomicBoolean(false)
+
+ fun getFooterActionsViewModel(lifecycleOwner: LifecycleOwner): FooterActionsViewModel {
+ if (footerActionsControllerInitialized.compareAndSet(false, true)) {
+ footerActionsController.init()
+ }
+ return footerActionsViewModelFactory.create(lifecycleOwner)
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): QuickSettingsSceneContentViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
deleted file mode 100644
index 7258882..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.ui.viewmodel
-
-import androidx.lifecycle.LifecycleOwner
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Edge
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
-import com.android.systemui.qs.FooterActionsController
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.qs.ui.adapter.QSSceneAdapter
-import com.android.systemui.scene.domain.interactor.SceneBackInteractor
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
-import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
-import java.util.concurrent.atomic.AtomicBoolean
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
-
-/** Models UI state and handles user input for the quick settings scene. */
-@SysUISingleton
-class QuickSettingsSceneViewModel
-@Inject
-constructor(
- val brightnessMirrorViewModelFactory: BrightnessMirrorViewModel.Factory,
- val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
- val qsSceneAdapter: QSSceneAdapter,
- private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
- private val footerActionsController: FooterActionsController,
- sceneBackInteractor: SceneBackInteractor,
- val mediaCarouselInteractor: MediaCarouselInteractor,
-) {
- private val backScene: Flow<SceneKey> =
- sceneBackInteractor.backScene
- .filter { it != Scenes.QuickSettings }
- .map { it ?: Scenes.Shade }
-
- val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
- combine(
- qsSceneAdapter.isCustomizerShowing,
- backScene,
- ) { isCustomizing, backScene ->
- buildMap<UserAction, UserActionResult> {
- if (isCustomizing) {
- // TODO(b/332749288) Empty map so there are no back handlers and back can close
- // customizer
-
- // TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
- // while customizing
- } else {
- put(Back, UserActionResult(backScene))
- put(Swipe(SwipeDirection.Up), UserActionResult(backScene))
- put(
- Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up),
- UserActionResult(SceneFamilies.Home),
- )
- }
- }
- }
-
- val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation
-
- private val footerActionsControllerInitialized = AtomicBoolean(false)
-
- fun getFooterActionsViewModel(lifecycleOwner: LifecycleOwner): FooterActionsViewModel {
- if (footerActionsControllerInitialized.compareAndSet(false, true)) {
- footerActionsController.init()
- }
- return footerActionsViewModelFactory.create(lifecycleOwner)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index f6924f2..8aa601f 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -26,13 +26,12 @@
attrs,
) {
- private lateinit var viewModel: SceneContainerViewModel
-
+ private var motionEventHandler: SceneContainerViewModel.MotionEventHandler? = null
// TODO(b/298525212): remove once Compose exposes window inset bounds.
private val windowInsets: MutableStateFlow<WindowInsets?> = MutableStateFlow(null)
fun init(
- viewModel: SceneContainerViewModel,
+ viewModelFactory: SceneContainerViewModel.Factory,
containerConfig: SceneContainerConfig,
sharedNotificationContainer: SharedNotificationContainer,
scenes: Set<Scene>,
@@ -40,11 +39,13 @@
sceneDataSourceDelegator: SceneDataSourceDelegator,
alternateBouncerDependencies: AlternateBouncerDependencies,
) {
- this.viewModel = viewModel
setLayoutInsetsController(layoutInsetController)
SceneWindowRootViewBinder.bind(
view = this@SceneWindowRootView,
- viewModel = viewModel,
+ viewModelFactory = viewModelFactory,
+ motionEventHandlerReceiver = { motionEventHandler ->
+ this.motionEventHandler = motionEventHandler
+ },
windowInsets = windowInsets,
containerConfig = containerConfig,
sharedNotificationContainer = sharedNotificationContainer,
@@ -69,10 +70,10 @@
}
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
- viewModel.onMotionEvent(ev)
+ motionEventHandler?.onMotionEvent(ev)
return super.dispatchTouchEvent(ev).also {
TouchLogger.logDispatchTouch(TAG, ev, it)
- viewModel.onMotionEventComplete()
+ motionEventHandler?.onMotionEventComplete()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 73a8e4c..ad68f17 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -29,8 +29,6 @@
import androidx.compose.ui.unit.dp
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.animation.scene.SceneKey
import com.android.compose.theme.PlatformTheme
import com.android.internal.policy.ScreenDecorationsUtils
@@ -39,7 +37,9 @@
import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
import com.android.systemui.keyguard.ui.composable.AlternateBouncer
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
+import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scene
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@@ -63,7 +64,8 @@
/** Binds between the view and view-model pertaining to a specific scene container. */
fun bind(
view: ViewGroup,
- viewModel: SceneContainerViewModel,
+ viewModelFactory: SceneContainerViewModel.Factory,
+ motionEventHandlerReceiver: (SceneContainerViewModel.MotionEventHandler?) -> Unit,
windowInsets: StateFlow<WindowInsets?>,
containerConfig: SceneContainerConfig,
sharedNotificationContainer: SharedNotificationContainer,
@@ -85,8 +87,11 @@
}
view.repeatWhenAttached {
- lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
+ view.viewModel(
+ minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+ factory = { viewModelFactory.create(motionEventHandlerReceiver) },
+ ) { viewModel ->
+ try {
view.setViewTreeOnBackPressedDispatcherOwner(
object : OnBackPressedDispatcherOwner {
override val onBackPressedDispatcher =
@@ -140,10 +145,11 @@
onVisibilityChangedInternal(isVisible)
}
}
+ awaitCancellation()
+ } finally {
+ // Here when destroyed.
+ view.removeAllViews()
}
-
- // Here when destroyed.
- view.removeAllViews()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index a28222e..2d02f5a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -23,25 +23,26 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
-import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.SysUiViewModel
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
/** Models UI state for the scene container. */
-@SysUISingleton
class SceneContainerViewModel
-@Inject
+@AssistedInject
constructor(
private val sceneInteractor: SceneInteractor,
private val falsingInteractor: FalsingInteractor,
private val powerInteractor: PowerInteractor,
-) {
+ @Assisted private val motionEventHandlerReceiver: (MotionEventHandler?) -> Unit,
+) : SysUiViewModel() {
/**
* Keys of all scenes in the container.
*
@@ -56,6 +57,29 @@
/** Whether the container is visible. */
val isVisible: StateFlow<Boolean> = sceneInteractor.isVisible
+ override suspend fun onActivated() {
+ try {
+ // Sends a MotionEventHandler to the owner of the view-model so they can report
+ // MotionEvents into the view-model.
+ motionEventHandlerReceiver(
+ object : MotionEventHandler {
+ override fun onMotionEvent(motionEvent: MotionEvent) {
+ this@SceneContainerViewModel.onMotionEvent(motionEvent)
+ }
+
+ override fun onMotionEventComplete() {
+ this@SceneContainerViewModel.onMotionEventComplete()
+ }
+ }
+ )
+ awaitCancellation()
+ } finally {
+ // Clears the previously-sent MotionEventHandler so the owner of the view-model releases
+ // their reference to it.
+ motionEventHandlerReceiver(null)
+ }
+ }
+
/**
* Binds the given flow so the system remembers it.
*
@@ -136,21 +160,22 @@
}
}
- private fun replaceSceneFamilies(
- destinationScenes: Map<UserAction, UserActionResult>,
- ): Flow<Map<UserAction, UserActionResult>> {
- return destinationScenes
- .mapValues { (_, actionResult) ->
- sceneInteractor.resolveSceneFamily(actionResult.toScene).map { scene ->
- actionResult.copy(toScene = scene)
- }
- }
- .combineValueFlows()
+ /** Defines interface for classes that can handle externally-reported [MotionEvent]s. */
+ interface MotionEventHandler {
+ /** Notifies that a [MotionEvent] has occurred. */
+ fun onMotionEvent(motionEvent: MotionEvent)
+
+ /**
+ * Notifies that the previous [MotionEvent] reported by [onMotionEvent] has finished
+ * processing.
+ */
+ fun onMotionEventComplete()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ motionEventHandlerReceiver: (MotionEventHandler?) -> Unit,
+ ): SceneContainerViewModel
}
}
-
-private fun <K, V> Map<K, Flow<V>>.combineValueFlows(): Flow<Map<K, V>> =
- combine(
- asIterable().map { (k, fv) -> fv.map { k to it } },
- transform = Array<Pair<K, V>>::toMap,
- )
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 21bbaa5..606fef0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -79,7 +79,7 @@
@SysUISingleton
fun providesWindowRootView(
layoutInflater: LayoutInflater,
- viewModelProvider: Provider<SceneContainerViewModel>,
+ viewModelFactory: SceneContainerViewModel.Factory,
containerConfigProvider: Provider<SceneContainerConfig>,
scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
layoutInsetController: NotificationInsetsController,
@@ -91,7 +91,7 @@
val sceneWindowRootView =
layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView
sceneWindowRootView.init(
- viewModel = viewModelProvider.get(),
+ viewModelFactory = viewModelFactory,
containerConfig = containerConfigProvider.get(),
sharedNotificationContainer =
sceneWindowRootView.requireViewById(R.id.shared_notification_container),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 99f7a75..f63ee7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -511,7 +511,7 @@
occludedToAodTransitionViewModel.lockscreenAlpha,
occludedToGoneTransitionViewModel.notificationAlpha(viewState),
occludedToLockscreenTransitionViewModel.lockscreenAlpha,
- primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
+ primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
deleted file mode 100644
index a538856..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * Copyright (C) 2016 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.phone;
-
-import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.hardware.display.ColorDisplayManager;
-import android.hardware.display.NightDisplayListener;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.NightDisplayListenerModule;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.AutoAddTracker;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.UserSettingObserver;
-import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
-import com.android.systemui.res.R;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastDevice;
-import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.DataSaverController.Listener;
-import com.android.systemui.statusbar.policy.DeviceControlsController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.HotspotController.Callback;
-import com.android.systemui.statusbar.policy.SafetyController;
-import com.android.systemui.statusbar.policy.WalletController;
-import com.android.systemui.util.UserAwareController;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Objects;
-
-import javax.inject.Named;
-
-/**
- * Manages which tiles should be automatically added to QS.
- */
-public class AutoTileManager implements UserAwareController {
- private static final String TAG = "AutoTileManager";
-
- public static final String HOTSPOT = "hotspot";
- public static final String SAVER = "saver";
- public static final String INVERSION = "inversion";
- public static final String WORK = "work";
- public static final String NIGHT = "night";
- public static final String CAST = "cast";
- public static final String DEVICE_CONTROLS = "controls";
- public static final String WALLET = "wallet";
- public static final String BRIGHTNESS = "reduce_brightness";
- static final String SETTING_SEPARATOR = ":";
-
- private UserHandle mCurrentUser;
- private boolean mInitialized;
- private final String mSafetySpec;
-
- protected final Context mContext;
- protected final QSHost mHost;
- protected final Handler mHandler;
- protected final SecureSettings mSecureSettings;
- protected final AutoAddTracker mAutoTracker;
- private final HotspotController mHotspotController;
- private final DataSaverController mDataSaverController;
- private final ManagedProfileController mManagedProfileController;
- private final NightDisplayListenerModule.Builder mNightDisplayListenerBuilder;
- private NightDisplayListener mNightDisplayListener;
- private final CastController mCastController;
- private final DeviceControlsController mDeviceControlsController;
- private final WalletController mWalletController;
- private final ReduceBrightColorsController mReduceBrightColorsController;
- private final SafetyController mSafetyController;
- private final boolean mIsReduceBrightColorsAvailable;
- private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
-
- public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder,
- QSHost host,
- @Background Handler handler,
- SecureSettings secureSettings,
- HotspotController hotspotController,
- DataSaverController dataSaverController,
- ManagedProfileController managedProfileController,
- NightDisplayListenerModule.Builder nightDisplayListenerBuilder,
- CastController castController,
- ReduceBrightColorsController reduceBrightColorsController,
- DeviceControlsController deviceControlsController,
- WalletController walletController,
- SafetyController safetyController,
- @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
- mContext = context;
- mHost = host;
- mSecureSettings = secureSettings;
- mCurrentUser = mHost.getUserContext().getUser();
- mAutoTracker = autoAddTrackerBuilder.setUserId(mCurrentUser.getIdentifier()).build();
- mHandler = handler;
- mHotspotController = hotspotController;
- mDataSaverController = dataSaverController;
- mManagedProfileController = managedProfileController;
- mNightDisplayListenerBuilder = nightDisplayListenerBuilder;
- mCastController = castController;
- mReduceBrightColorsController = reduceBrightColorsController;
- mIsReduceBrightColorsAvailable = isReduceBrightColorsAvailable;
- mDeviceControlsController = deviceControlsController;
- mWalletController = walletController;
- mSafetyController = safetyController;
- String safetySpecClass;
- try {
- safetySpecClass =
- context.getResources().getString(R.string.safety_quick_settings_tile_class);
- if (safetySpecClass.length() == 0) {
- safetySpecClass = null;
- }
- } catch (Resources.NotFoundException | NullPointerException e) {
- safetySpecClass = null;
- }
- mSafetySpec = safetySpecClass != null ? CustomTile.toSpec(new ComponentName(mContext
- .getPackageManager().getPermissionControllerPackageName(), safetySpecClass)) : null;
- }
-
- /**
- * Init method must be called after construction to start listening
- */
- public void init() {
- QSPipelineFlagsRepository.Utils.assertInLegacyMode();
- if (mInitialized) {
- Log.w(TAG, "Trying to re-initialize");
- return;
- }
- mAutoTracker.initialize();
- populateSettingsList();
- startControllersAndSettingsListeners();
- mInitialized = true;
- }
-
- protected void startControllersAndSettingsListeners() {
- if (!mAutoTracker.isAdded(HOTSPOT)) {
- mHotspotController.addCallback(mHotspotCallback);
- }
- if (!mAutoTracker.isAdded(SAVER)) {
- mDataSaverController.addCallback(mDataSaverListener);
- }
- mManagedProfileController.addCallback(mProfileCallback);
-
- mNightDisplayListener = mNightDisplayListenerBuilder
- .setUser(mCurrentUser.getIdentifier())
- .build();
- if (!mAutoTracker.isAdded(NIGHT)
- && ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- mNightDisplayListener.setCallback(mNightDisplayCallback);
- }
- if (!mAutoTracker.isAdded(CAST)) {
- mCastController.addCallback(mCastCallback);
- }
- if (!mAutoTracker.isAdded(BRIGHTNESS) && mIsReduceBrightColorsAvailable) {
- mReduceBrightColorsController.addCallback(mReduceBrightColorsCallback);
- }
- // We always want this callback, because if the feature stops being supported,
- // we want to remove the tile from AutoAddTracker. That way it will be re-added when the
- // feature is reenabled (similar to work tile).
- mDeviceControlsController.setCallback(mDeviceControlsCallback);
- if (!mAutoTracker.isAdded(WALLET)) {
- initWalletController();
- }
- if (mSafetySpec != null) {
- if (!mAutoTracker.isAdded(mSafetySpec)) {
- initSafetyTile();
- }
- mSafetyController.addCallback(mSafetyCallback);
- }
-
- int settingsN = mAutoAddSettingList.size();
- for (int i = 0; i < settingsN; i++) {
- if (!mAutoTracker.isAdded(mAutoAddSettingList.get(i).mSpec)) {
- mAutoAddSettingList.get(i).setListening(true);
- }
- }
- }
-
- protected void stopListening() {
- mHotspotController.removeCallback(mHotspotCallback);
- mDataSaverController.removeCallback(mDataSaverListener);
- mManagedProfileController.removeCallback(mProfileCallback);
- if (ColorDisplayManager.isNightDisplayAvailable(mContext)
- && mNightDisplayListener != null) {
- mNightDisplayListener.setCallback(null);
- }
- if (mIsReduceBrightColorsAvailable) {
- mReduceBrightColorsController.removeCallback(mReduceBrightColorsCallback);
- }
- mCastController.removeCallback(mCastCallback);
- mDeviceControlsController.removeCallback();
- if (mSafetySpec != null) {
- mSafetyController.removeCallback(mSafetyCallback);
- }
- int settingsN = mAutoAddSettingList.size();
- for (int i = 0; i < settingsN; i++) {
- mAutoAddSettingList.get(i).setListening(false);
- }
- }
-
- public void destroy() {
- stopListening();
- mAutoTracker.destroy();
- }
-
- /**
- * Populates a list with the pairs setting:spec in the config resource.
- * <p>
- * This will only create {@link AutoAddSetting} objects for those tiles that have not been
- * auto-added before, and set the corresponding {@link ContentObserver} to listening.
- */
- private void populateSettingsList() {
- String [] autoAddList;
- try {
- autoAddList = mContext.getResources().getStringArray(
- R.array.config_quickSettingsAutoAdd);
- } catch (Resources.NotFoundException e) {
- Log.w(TAG, "Missing config resource");
- return;
- }
- // getStringArray returns @NotNull, so if we got here, autoAddList is not null
- for (String tile : autoAddList) {
- String[] split = tile.split(SETTING_SEPARATOR);
- if (split.length == 2) {
- String setting = split[0];
- String spec = split[1];
- // Populate all the settings. As they may not have been added in other users
- AutoAddSetting s = new AutoAddSetting(
- mSecureSettings, mHandler, setting, mCurrentUser.getIdentifier(), spec);
- mAutoAddSettingList.add(s);
- } else {
- Log.w(TAG, "Malformed item in array: " + tile);
- }
- }
- }
-
- /*
- * This will be sent off the main thread if needed
- */
- @Override
- public void changeUser(UserHandle newUser) {
- if (!mInitialized) {
- throw new IllegalStateException("AutoTileManager not initialized");
- }
- if (!Thread.currentThread().equals(mHandler.getLooper().getThread())) {
- mHandler.post(() -> changeUser(newUser));
- return;
- }
- if (newUser.getIdentifier() == mCurrentUser.getIdentifier()) {
- return;
- }
- stopListening();
- mCurrentUser = newUser;
- int settingsN = mAutoAddSettingList.size();
- for (int i = 0; i < settingsN; i++) {
- mAutoAddSettingList.get(i).setUserId(newUser.getIdentifier());
- }
- mAutoTracker.changeUser(newUser);
- startControllersAndSettingsListeners();
- }
-
- @Override
- public int getCurrentUserId() {
- return mCurrentUser.getIdentifier();
- }
-
- private final ManagedProfileController.Callback mProfileCallback =
- new ManagedProfileController.Callback() {
- @Override
- public void onManagedProfileChanged() {
- if (mManagedProfileController.hasActiveProfile()) {
- if (mAutoTracker.isAdded(WORK)) return;
- final int position = mAutoTracker.getRestoredTilePosition(WORK);
- mHost.addTile(WORK, position);
- mAutoTracker.setTileAdded(WORK);
- } else {
- if (!mAutoTracker.isAdded(WORK)) return;
- mHost.removeTile(WORK);
- mAutoTracker.setTileRemoved(WORK);
- }
- }
-
- @Override
- public void onManagedProfileRemoved() {
- }
- };
-
- private final DataSaverController.Listener mDataSaverListener = new Listener() {
- @Override
- public void onDataSaverChanged(boolean isDataSaving) {
- if (mAutoTracker.isAdded(SAVER)) return;
- if (isDataSaving) {
- mHost.addTile(SAVER);
- mAutoTracker.setTileAdded(SAVER);
- mHandler.post(() -> mDataSaverController.removeCallback(mDataSaverListener));
- }
- }
- };
-
- private final HotspotController.Callback mHotspotCallback = new Callback() {
- @Override
- public void onHotspotChanged(boolean enabled, int numDevices) {
- if (mAutoTracker.isAdded(HOTSPOT)) return;
- if (enabled) {
- mHost.addTile(HOTSPOT);
- mAutoTracker.setTileAdded(HOTSPOT);
- mHandler.post(() -> mHotspotController.removeCallback(mHotspotCallback));
- }
- }
- };
-
- private final DeviceControlsController.Callback mDeviceControlsCallback =
- new DeviceControlsController.Callback() {
- @Override
- public void onControlsUpdate(@Nullable Integer position) {
- if (mAutoTracker.isAdded(DEVICE_CONTROLS)) return;
- if (position != null && !hasTile(DEVICE_CONTROLS)) {
- mHost.addTile(DEVICE_CONTROLS, position);
- mAutoTracker.setTileAdded(DEVICE_CONTROLS);
- }
- mHandler.post(() -> mDeviceControlsController.removeCallback());
- }
-
- @Override
- public void removeControlsAutoTracker() {
- mAutoTracker.setTileRemoved(DEVICE_CONTROLS);
- }
- };
-
- private boolean hasTile(String tileSpec) {
- if (tileSpec == null) return false;
- Collection<QSTile> tiles = mHost.getTiles();
- for (QSTile tile : tiles) {
- if (tileSpec.equals(tile.getTileSpec())) {
- return true;
- }
- }
- return false;
- }
-
- private void initWalletController() {
- if (mAutoTracker.isAdded(WALLET)) return;
- Integer position = mWalletController.getWalletPosition();
-
- if (position != null) {
- mHost.addTile(WALLET, position);
- mAutoTracker.setTileAdded(WALLET);
- }
- }
-
- private void initSafetyTile() {
- if (mSafetySpec == null || mAutoTracker.isAdded(mSafetySpec)) {
- return;
- }
- mHost.addTile(CustomTile.getComponentFromSpec(mSafetySpec), true);
- mAutoTracker.setTileAdded(mSafetySpec);
- }
-
- @VisibleForTesting
- final NightDisplayListener.Callback mNightDisplayCallback =
- new NightDisplayListener.Callback() {
- @Override
- public void onActivated(boolean activated) {
- if (activated) {
- addNightTile();
- }
- }
-
- @Override
- public void onAutoModeChanged(int autoMode) {
- if (autoMode == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME
- || autoMode == ColorDisplayManager.AUTO_MODE_TWILIGHT) {
- addNightTile();
- }
- }
-
- private void addNightTile() {
- if (mAutoTracker.isAdded(NIGHT)) return;
- mHost.addTile(NIGHT);
- mAutoTracker.setTileAdded(NIGHT);
- mHandler.post(() -> mNightDisplayListener.setCallback(null));
- }
- };
-
- @VisibleForTesting
- final ReduceBrightColorsController.Listener mReduceBrightColorsCallback =
- new ReduceBrightColorsController.Listener() {
- @Override
- public void onActivated(boolean activated) {
- if (activated) {
- addReduceBrightColorsTile();
- }
- }
-
- @Override
- public void onFeatureEnabledChanged(boolean enabled) {
- if (!enabled) {
- mHost.removeTile(BRIGHTNESS);
- mHandler.post(() -> mReduceBrightColorsController.removeCallback(this));
- }
- }
-
- private void addReduceBrightColorsTile() {
- if (mAutoTracker.isAdded(BRIGHTNESS)) return;
- mHost.addTile(BRIGHTNESS);
- mAutoTracker.setTileAdded(BRIGHTNESS);
- mHandler.post(() -> mReduceBrightColorsController.removeCallback(this));
- }
- };
-
- @VisibleForTesting
- final CastController.Callback mCastCallback = new CastController.Callback() {
- @Override
- public void onCastDevicesChanged() {
- if (mAutoTracker.isAdded(CAST)) return;
-
- boolean isCasting = false;
- for (CastDevice device : mCastController.getCastDevices()) {
- if (device.isCasting()) {
- isCasting = true;
- break;
- }
- }
-
- if (isCasting) {
- mHost.addTile(CAST);
- mAutoTracker.setTileAdded(CAST);
- mHandler.post(() -> mCastController.removeCallback(mCastCallback));
- }
- }
- };
-
- @VisibleForTesting
- final SafetyController.Listener mSafetyCallback = new SafetyController.Listener() {
- @Override
- public void onSafetyCenterEnableChanged(boolean isSafetyCenterEnabled) {
- if (mSafetySpec == null) {
- return;
- }
-
- if (isSafetyCenterEnabled && !mAutoTracker.isAdded(mSafetySpec)) {
- initSafetyTile();
- } else if (!isSafetyCenterEnabled && mAutoTracker.isAdded(mSafetySpec)) {
- mHost.removeTile(mSafetySpec);
- mAutoTracker.setTileRemoved(mSafetySpec);
- }
- }
- };
-
- @VisibleForTesting
- protected UserSettingObserver getSecureSettingForKey(String key) {
- for (UserSettingObserver s : mAutoAddSettingList) {
- if (Objects.equals(key, s.getKey())) {
- return s;
- }
- }
- return null;
- }
-
- /**
- * Tracks tiles that should be auto added when a setting changes.
- * <p>
- * When the setting changes to a value different from 0, if the tile has not been auto added
- * before, it will be added and the listener will be stopped.
- */
- private class AutoAddSetting extends UserSettingObserver {
- private final String mSpec;
-
- AutoAddSetting(
- SecureSettings secureSettings,
- Handler handler,
- String setting,
- int userId,
- String tileSpec
- ) {
- super(secureSettings, handler, setting, userId);
- mSpec = tileSpec;
- }
-
- @Override
- protected void handleValueChanged(int value, boolean observedChange) {
- if (mAutoTracker.isAdded(mSpec)) {
- // This should not be listening anymore
- mHandler.post(() -> setListening(false));
- return;
- }
- if (value != 0) {
- if (mSpec.startsWith(CustomTile.PREFIX)) {
- mHost.addTile(CustomTile.getComponentFromSpec(mSpec), /* end */ true);
- } else {
- mHost.addTile(mSpec);
- }
- mAutoTracker.setTileAdded(mSpec);
- mHandler.post(() -> setListening(false));
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index f178708..04604e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-
-
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
@@ -38,11 +36,8 @@
import com.android.systemui.Dependency;
import com.android.systemui.Flags;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
-import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
@@ -55,8 +50,6 @@
private final StatusBarContentInsetsProvider mContentInsetsProvider;
private final StatusBarWindowController mStatusBarWindowController;
- private DarkReceiver mBattery;
- private Clock mClock;
private int mRotationOrientation = -1;
@Nullable
private View mCutoutSpace;
@@ -93,8 +86,6 @@
@Override
public void onFinishInflate() {
super.onFinishInflate();
- mBattery = findViewById(R.id.battery);
- mClock = findViewById(R.id.clock);
mCutoutSpace = findViewById(R.id.cutout_space_view);
updateResources();
@@ -103,9 +94,6 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- // Always have Battery meters in the status bar observe the dark/light modes.
- Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
- Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mClock);
if (updateDisplayParameters()) {
updateLayoutForCutout();
updateWindowHeight();
@@ -115,8 +103,6 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
- Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mClock);
mDisplayCutout = null;
}
@@ -136,10 +122,6 @@
updateWindowHeight();
}
- void onDensityOrFontScaleChanged() {
- mClock.onDensityOrFontScaleChanged();
- }
-
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (updateDisplayParameters()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index a818c05..468a3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -23,10 +23,13 @@
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
+import androidx.annotation.VisibleForTesting
import com.android.systemui.Flags
import com.android.systemui.Gefingerpoken
+import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS
+import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.ui.view.WindowRootView
@@ -35,6 +38,7 @@
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.SysUIUnfoldComponent
@@ -68,19 +72,27 @@
private val viewUtil: ViewUtil,
private val configurationController: ConfigurationController,
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
+ private val darkIconDispatcher: DarkIconDispatcher,
) : ViewController<PhoneStatusBarView>(view) {
+ private lateinit var battery: BatteryMeterView
+ private lateinit var clock: Clock
private lateinit var statusContainer: View
private val configurationListener =
object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
- mView.onDensityOrFontScaleChanged()
+ clock.onDensityOrFontScaleChanged()
}
}
override fun onViewAttached() {
statusContainer = mView.requireViewById(R.id.system_icons)
+ clock = mView.requireViewById(R.id.clock)
+ battery = mView.requireViewById(R.id.battery)
+
+ addDarkReceivers()
+
statusContainer.setOnHoverListener(
statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer)
)
@@ -133,7 +145,9 @@
}
}
- override fun onViewDetached() {
+ @VisibleForTesting
+ public override fun onViewDetached() {
+ removeDarkReceivers()
statusContainer.setOnHoverListener(null)
progressProvider?.setReadyToHandleTransition(false)
moveFromCenterAnimationController?.onViewDetached()
@@ -182,6 +196,16 @@
}
}
+ private fun addDarkReceivers() {
+ darkIconDispatcher.addDarkReceiver(battery)
+ darkIconDispatcher.addDarkReceiver(clock)
+ }
+
+ private fun removeDarkReceivers() {
+ darkIconDispatcher.removeDarkReceiver(battery)
+ darkIconDispatcher.removeDarkReceiver(clock)
+ }
+
inner class PhoneStatusBarViewTouchHandler : Gefingerpoken {
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
return if (Flags.statusBarSwipeOverChip()) {
@@ -285,6 +309,7 @@
private val viewUtil: ViewUtil,
private val configurationController: ConfigurationController,
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
+ private val darkIconDispatcher: DarkIconDispatcher,
) {
fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
val statusBarMoveFromCenterAnimationController =
@@ -309,6 +334,7 @@
viewUtil,
configurationController,
statusOverlayHoverListenerFactory,
+ darkIconDispatcher,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 775f34d..d281920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -174,15 +174,24 @@
IndentingPrintWriter ipw = asIndenting(pw);
ipw.println("BatteryController state:");
ipw.increaseIndent();
- ipw.print("mHasReceivedBattery="); ipw.println(mHasReceivedBattery);
- ipw.print("mLevel="); ipw.println(mLevel);
- ipw.print("mPluggedIn="); ipw.println(mPluggedIn);
- ipw.print("mCharging="); ipw.println(mCharging);
- ipw.print("mCharged="); ipw.println(mCharged);
- ipw.print("mIsBatteryDefender="); ipw.println(mIsBatteryDefender);
- ipw.print("mIsIncompatibleCharging="); ipw.println(mIsIncompatibleCharging);
- ipw.print("mPowerSave="); ipw.println(mPowerSave);
- ipw.print("mStateUnknown="); ipw.println(mStateUnknown);
+ ipw.print("mHasReceivedBattery=");
+ ipw.println(mHasReceivedBattery);
+ ipw.print("mLevel=");
+ ipw.println(mLevel);
+ ipw.print("mPluggedIn=");
+ ipw.println(mPluggedIn);
+ ipw.print("mCharging=");
+ ipw.println(mCharging);
+ ipw.print("mCharged=");
+ ipw.println(mCharged);
+ ipw.print("mIsBatteryDefender=");
+ ipw.println(mIsBatteryDefender);
+ ipw.print("mIsIncompatibleCharging=");
+ ipw.println(mIsIncompatibleCharging);
+ ipw.print("mPowerSave=");
+ ipw.println(mPowerSave);
+ ipw.print("mStateUnknown=");
+ ipw.println(mStateUnknown);
ipw.println("Callbacks:------------------");
// Since the above lines are already indented, we need to indent twice for the callbacks.
ipw.increaseIndent();
@@ -272,7 +281,7 @@
}
int chargingStatus = intent.getIntExtra(EXTRA_CHARGING_STATUS, CHARGING_POLICY_DEFAULT);
- boolean isBatteryDefender = chargingStatus == CHARGING_POLICY_ADAPTIVE_LONGLIFE;
+ boolean isBatteryDefender = isBatteryDefenderMode(chargingStatus);
if (isBatteryDefender != mIsBatteryDefender) {
mIsBatteryDefender = isBatteryDefender;
fireIsBatteryDefenderChanged();
@@ -359,11 +368,24 @@
return mPluggedChargingSource == BatteryManager.BATTERY_PLUGGED_WIRELESS;
}
- public boolean isBatteryDefender() {
+ /**
+ * This method is used for tests only. Returns whether the device is in battery defender
+ * mode.
+ */
+ @VisibleForTesting
+ protected boolean isBatteryDefender() {
return mIsBatteryDefender;
}
/**
+ * Checks whether the device is in battery defender mode based on the current charging
+ * status. This method can be overridden to have a different definition for its subclasses.
+ */
+ protected boolean isBatteryDefenderMode(int chargingStatus) {
+ return chargingStatus == CHARGING_POLICY_ADAPTIVE_LONGLIFE;
+ }
+
+ /**
* Returns whether the charging adapter is incompatible.
*/
public boolean isIncompatibleCharging() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
index 1224275..e29e069 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -21,13 +21,12 @@
import android.content.SharedPreferences
import android.provider.Settings
import android.util.Log
-import com.android.systemui.res.R
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
-import com.android.systemui.statusbar.phone.AutoTileManager
import com.android.systemui.statusbar.policy.DeviceControlsController.Callback
import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
@@ -35,14 +34,16 @@
/**
* Watches for Device Controls QS Tile activation, which can happen in two ways:
* <ol>
- * <li>Migration from Power Menu - For existing Android 11 users, create a tile in a high
- * priority position.
- * <li>Device controls service becomes available - For non-migrated users, create a tile and
- * place at the end of active tiles, and initiate seeding where possible.
+ * <li>Migration from Power Menu - For existing Android 11 users, create a tile in a high priority
+ * position.
+ * <li>Device controls service becomes available - For non-migrated users, create a tile and place
+ * at the end of active tiles, and initiate seeding where possible.
* </ol>
*/
@SysUISingleton
-public class DeviceControlsControllerImpl @Inject constructor(
+public class DeviceControlsControllerImpl
+@Inject
+constructor(
private val context: Context,
private val controlsComponent: ControlsComponent,
private val userContextProvider: UserContextProvider,
@@ -52,13 +53,14 @@
private var callback: Callback? = null
internal var position: Int? = null
- private val listingCallback = object : ControlsListingController.ControlsListingCallback {
- override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
- if (!serviceInfos.isEmpty()) {
- seedFavorites(serviceInfos)
+ private val listingCallback =
+ object : ControlsListingController.ControlsListingCallback {
+ override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
+ if (!serviceInfos.isEmpty()) {
+ seedFavorites(serviceInfos)
+ }
}
}
- }
companion object {
private const val TAG = "DeviceControlsControllerImpl"
@@ -80,7 +82,7 @@
}
/**
- * This migration logic assumes that something like [AutoTileManager] is tracking state
+ * This migration logic assumes that something like [AutoAddTracker] is tracking state
* externally, and won't call this method after receiving a response via
* [Callback#onControlsUpdate], once per user. Otherwise the calculated position may be
* incorrect.
@@ -118,16 +120,19 @@
}
/**
- * See if any available control service providers match one of the preferred components. If
- * they do, and there are no current favorites for that component, query the preferred
- * component for a limited number of suggested controls.
+ * See if any available control service providers match one of the preferred components. If they
+ * do, and there are no current favorites for that component, query the preferred component for
+ * a limited number of suggested controls.
*/
private fun seedFavorites(serviceInfos: List<ControlsServiceInfo>) {
- val preferredControlsPackages = context.getResources().getStringArray(
- R.array.config_controlsPreferredPackages)
+ val preferredControlsPackages =
+ context.getResources().getStringArray(R.array.config_controlsPreferredPackages)
- val prefs = userContextProvider.userContext.getSharedPreferences(
- PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
+ val prefs =
+ userContextProvider.userContext.getSharedPreferences(
+ PREFS_CONTROLS_FILE,
+ Context.MODE_PRIVATE
+ )
val seededPackages =
prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, emptySet()) ?: emptySet()
@@ -157,21 +162,22 @@
if (componentsToSeed.isEmpty()) return
controlsController.seedFavoritesForComponents(
- componentsToSeed,
- { response ->
- Log.d(TAG, "Controls seeded: $response")
- if (response.accepted) {
- addPackageToSeededSet(prefs, response.packageName)
- if (position == null) {
- position = QS_DEFAULT_POSITION
- }
- fireControlsUpdate()
-
- controlsComponent.getControlsListingController().ifPresent {
- it.removeCallback(listingCallback)
- }
+ componentsToSeed,
+ { response ->
+ Log.d(TAG, "Controls seeded: $response")
+ if (response.accepted) {
+ addPackageToSeededSet(prefs, response.packageName)
+ if (position == null) {
+ position = QS_DEFAULT_POSITION
}
- })
+ fireControlsUpdate()
+
+ controlsComponent.getControlsListingController().ifPresent {
+ it.removeCallback(listingCallback)
+ }
+ }
+ }
+ )
}
private fun addPackageToSeededSet(prefs: SharedPreferences, pkg: String) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
index a4936e6..8e215f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
@@ -33,9 +33,10 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
-import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModelFactory
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.motion.createSysUiComposeMotionTestRule
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.testKosmos
@@ -81,7 +82,8 @@
private fun BouncerContentUnderTest() {
PlatformTheme {
BouncerContent(
- viewModel = kosmos.bouncerViewModel,
+ viewModel =
+ rememberViewModel { kosmos.bouncerSceneContentViewModelFactory.create() },
layout = BouncerSceneLayout.BESIDE_USER_SWITCHER,
modifier = Modifier.fillMaxSize().testTag("BouncerContent"),
dialogFactory = bouncerDialogFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
index 2948c02..4b61a0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
@@ -24,14 +24,14 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.bouncer.ui.viewmodel.patternBouncerViewModelFactory
+import com.android.systemui.lifecycle.activateIn
import com.android.systemui.motion.createSysUiComposeMotionTestRule
import com.android.systemui.testKosmos
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.takeWhile
+import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,15 +51,15 @@
@get:Rule val motionTestRule = createSysUiComposeMotionTestRule(kosmos)
- private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
- private val viewModel by lazy {
- PatternBouncerViewModel(
- applicationContext = context,
- viewModelScope = kosmos.testScope.backgroundScope,
- interactor = bouncerInteractor,
+ private val viewModel =
+ kosmos.patternBouncerViewModelFactory.create(
isInputEnabled = MutableStateFlow(true).asStateFlow(),
onIntentionalUserInput = {},
)
+
+ @Before
+ fun setUp() {
+ viewModel.activateIn(motionTestRule.toolkit.testScope)
}
@Composable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
new file mode 100644
index 0000000..7583399
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
+import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TutorialSchedulerRepositoryTest : SysuiTestCase() {
+
+ private lateinit var underTest: TutorialSchedulerRepository
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+
+ @Before
+ fun setup() {
+ underTest =
+ TutorialSchedulerRepository(
+ context,
+ testScope.backgroundScope,
+ "TutorialSchedulerRepositoryTest"
+ )
+ }
+
+ @After
+ fun clear() {
+ testScope.launch { underTest.clearDataStore() }
+ }
+
+ @Test
+ fun initialState() =
+ testScope.runTest {
+ assertThat(underTest.wasEverConnected(KEYBOARD)).isFalse()
+ assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse()
+ assertThat(underTest.isLaunched(KEYBOARD)).isFalse()
+ assertThat(underTest.isLaunched(TOUCHPAD)).isFalse()
+ }
+
+ @Test
+ fun connectKeyboard() =
+ testScope.runTest {
+ val now = Instant.now().toEpochMilli()
+ underTest.updateConnectTime(KEYBOARD, now)
+
+ assertThat(underTest.wasEverConnected(KEYBOARD)).isTrue()
+ assertThat(underTest.connectTime(KEYBOARD)).isEqualTo(now)
+ assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse()
+ }
+
+ @Test
+ fun launchKeyboard() =
+ testScope.runTest {
+ underTest.updateLaunch(KEYBOARD)
+
+ assertThat(underTest.isLaunched(KEYBOARD)).isTrue()
+ assertThat(underTest.isLaunched(TOUCHPAD)).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
deleted file mode 100644
index 1eeaef7..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.BroadcastReceiver;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.UserHandle;
-import android.provider.Settings.Secure;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.util.settings.FakeSettings;
-import com.android.systemui.util.settings.SecureSettings;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-@RunWith(AndroidJUnit4.class)
-@RunWithLooper
-@SmallTest
-public class AutoAddTrackerTest extends SysuiTestCase {
-
- private static final int END_POSITION = -1;
- private static final int USER = 0;
-
- @Mock
- private BroadcastDispatcher mBroadcastDispatcher;
- @Mock
- private QSHost mQSHost;
- @Mock
- private DumpManager mDumpManager;
- @Captor
- private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
- @Captor
- private ArgumentCaptor<IntentFilter> mIntentFilterArgumentCaptor;
-
- private Executor mBackgroundExecutor = Runnable::run; // Direct executor
- private AutoAddTracker mAutoTracker;
- private SecureSettings mSecureSettings;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mSecureSettings = new FakeSettings();
-
- mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, null, USER);
-
- mAutoTracker = createAutoAddTracker(USER);
- mAutoTracker.initialize();
- }
-
- @Test
- public void testChangeFromBackup() {
- assertFalse(mAutoTracker.isAdded(SAVER));
-
- mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, SAVER, USER);
-
- assertTrue(mAutoTracker.isAdded(SAVER));
-
- mAutoTracker.destroy();
- }
-
- @Test
- public void testSetAdded() {
- assertFalse(mAutoTracker.isAdded(SAVER));
- mAutoTracker.setTileAdded(SAVER);
-
- assertTrue(mAutoTracker.isAdded(SAVER));
-
- mAutoTracker.destroy();
- }
-
- @Test
- public void testPersist() {
- assertFalse(mAutoTracker.isAdded(SAVER));
- mAutoTracker.setTileAdded(SAVER);
-
- mAutoTracker.destroy();
- mAutoTracker = createAutoAddTracker(USER);
- mAutoTracker.initialize();
-
- assertTrue(mAutoTracker.isAdded(SAVER));
-
- mAutoTracker.destroy();
- }
-
- @Test
- public void testIndependentUsers() {
- mAutoTracker.setTileAdded(SAVER);
-
- mAutoTracker = createAutoAddTracker(USER + 1);
- mAutoTracker.initialize();
- assertFalse(mAutoTracker.isAdded(SAVER));
- }
-
- @Test
- public void testChangeUser() {
- mAutoTracker.setTileAdded(SAVER);
-
- mAutoTracker = createAutoAddTracker(USER + 1);
- mAutoTracker.changeUser(UserHandle.of(USER));
- assertTrue(mAutoTracker.isAdded(SAVER));
- }
-
- @Test
- public void testRestoredTilePositionPreserved() {
- verify(mBroadcastDispatcher).registerReceiver(
- mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
- String restoredTiles = "saver,internet,work,cast";
- Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
-
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
- assertEquals(2, mAutoTracker.getRestoredTilePosition("work"));
- }
-
- @Test
- public void testNoRestoredTileReturnsEndPosition() {
- verify(mBroadcastDispatcher).registerReceiver(
- mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
- Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, null);
-
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
- assertEquals(END_POSITION, mAutoTracker.getRestoredTilePosition("work"));
- }
-
- @Test
- public void testBroadcastReceiverRegistered() {
- verify(mBroadcastDispatcher).registerReceiver(
- any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER)),
- anyInt(), any());
-
- assertTrue(
- mIntentFilterArgumentCaptor.getValue().hasAction(Intent.ACTION_SETTING_RESTORED));
- }
-
- @Test
- public void testBroadcastReceiverChangesWithUser() {
- mAutoTracker.changeUser(UserHandle.of(USER + 1));
-
- InOrder inOrder = Mockito.inOrder(mBroadcastDispatcher);
- inOrder.verify(mBroadcastDispatcher).unregisterReceiver(any());
- inOrder.verify(mBroadcastDispatcher)
- .registerReceiver(any(), any(), any(), eq(UserHandle.of(USER + 1)), anyInt(),
- any());
- }
-
- @Test
- public void testSettingRestoredWithTilesNotRemovedInSource_noAutoAddedInTarget() {
- verify(mBroadcastDispatcher).registerReceiver(
- mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
-
- // These tiles were present in the original device
- String restoredTiles = "saver,work,internet,cast";
- Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
- // And these tiles have been auto-added in the original device
- // (no auto-added before restore)
- String restoredAutoAddTiles = "work";
- Intent restoreAutoAddTilesIntent =
- makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
-
- // Then, don't remove any current tiles
- verify(mQSHost, never()).removeTiles(any());
- assertEquals(restoredAutoAddTiles,
- mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
- }
-
- @Test
- public void testSettingRestoredWithTilesRemovedInSource_noAutoAddedInTarget() {
- verify(mBroadcastDispatcher)
- .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(),
- anyInt(), any());
-
- // These tiles were present in the original device
- String restoredTiles = "saver,internet,cast";
- Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
- // And these tiles have been auto-added in the original device
- // (no auto-added before restore)
- String restoredAutoAddTiles = "work";
- Intent restoreAutoAddTilesIntent =
- makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
-
- // Then, remove work tile
- verify(mQSHost).removeTiles(List.of("work"));
- assertEquals(restoredAutoAddTiles,
- mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
- }
-
- @Test
- public void testSettingRestoredWithTilesRemovedInSource_sameAutoAddedinTarget() {
- verify(mBroadcastDispatcher)
- .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(),
- anyInt(), any());
-
- // These tiles were present in the original device
- String restoredTiles = "saver,internet,cast";
- Intent restoreTilesIntent =
- makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
- // And these tiles have been auto-added in the original device
- // (no auto-added before restore)
- String restoredAutoAddTiles = "work";
- Intent restoreAutoAddTilesIntent =
- makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "work", restoredAutoAddTiles);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
-
- // Then, remove work tile
- verify(mQSHost).removeTiles(List.of("work"));
- assertEquals(restoredAutoAddTiles,
- mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
- }
-
- @Test
- public void testSettingRestoredWithTilesRemovedInSource_othersAutoAddedinTarget() {
- verify(mBroadcastDispatcher)
- .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(),
- anyInt(), any());
-
- // These tiles were present in the original device
- String restoredTiles = "saver,internet,cast";
- Intent restoreTilesIntent =
- makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
- // And these tiles have been auto-added in the original device
- // (no auto-added before restore)
- String restoredAutoAddTiles = "work";
- Intent restoreAutoAddTilesIntent =
- makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "inversion", restoredAutoAddTiles);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
-
- // Then, remove work tile
- verify(mQSHost).removeTiles(List.of("work"));
-
- String setting = mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER);
- assertEquals(2, setting.split(",").length);
- assertTrue(setting.contains("work"));
- assertTrue(setting.contains("inversion"));
- }
-
-
- private Intent makeRestoreIntent(
- String settingName, String previousValue, String restoredValue) {
- Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED);
- intent.putExtra(Intent.EXTRA_SETTING_NAME, settingName);
- intent.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue);
- intent.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, restoredValue);
- return intent;
- }
-
- private AutoAddTracker createAutoAddTracker(int user) {
- // Null handler wil dispatch sync.
- return new AutoAddTracker(
- mSecureSettings,
- mBroadcastDispatcher,
- mQSHost,
- mDumpManager,
- null,
- mBackgroundExecutor,
- user
- );
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
deleted file mode 100644
index 6d1bc82..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ /dev/null
@@ -1,786 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-
-import static com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE;
-import static com.android.systemui.Flags.FLAG_QS_NEW_TILES;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.util.SparseArray;
-
-import androidx.annotation.Nullable;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.util.CollectionUtils;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.Expandable;
-import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.dump.nano.SystemUIProtoDump;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.qs.QSFactory;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.external.CustomTileStatePersister;
-import com.android.systemui.qs.external.TileLifecycleManager;
-import com.android.systemui.qs.external.TileServiceKey;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.qs.tiles.di.NewQSTileFactory;
-import com.android.systemui.res.R;
-import com.android.systemui.settings.UserFileManager;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.FakeSharedPreferences;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.settings.FakeSettings;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import dagger.Lazy;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-import javax.inject.Provider;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class QSTileHostTest extends SysuiTestCase {
-
- private static String MOCK_STATE_STRING = "MockState";
- private static ComponentName CUSTOM_TILE =
- ComponentName.unflattenFromString("TEST_PKG/.TEST_CLS");
- private static final String CUSTOM_TILE_SPEC = CustomTile.toSpec(CUSTOM_TILE);
- private static final String SETTING = QSHost.TILES_SETTING;
- @Mock
- private PluginManager mPluginManager;
- @Mock
- private TunerService mTunerService;
- @Mock
- private AutoTileManager mAutoTiles;
- @Mock
- private ShadeController mShadeController;
- @Mock
- private QSLogger mQSLogger;
- @Mock
- private CustomTile mCustomTile;
- @Mock
- private UserTracker mUserTracker;
- @Mock
- private CustomTileStatePersister mCustomTileStatePersister;
- @Mock
- private TileLifecycleManager.Factory mTileLifecycleManagerFactory;
- @Mock
- private TileLifecycleManager mTileLifecycleManager;
- @Mock
- private UserFileManager mUserFileManager;
-
- private SecureSettings mSecureSettings;
-
- private QSFactory mDefaultFactory;
-
- private SparseArray<SharedPreferences> mSharedPreferencesByUser;
-
- private QSPipelineFlagsRepository mQSPipelineFlagsRepository;
-
- private FakeExecutor mMainExecutor;
-
- private QSTileHost mQSTileHost;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mSetFlagsRule.disableFlags(FLAG_QS_NEW_PIPELINE);
- mSetFlagsRule.disableFlags(FLAG_QS_NEW_TILES);
- mQSPipelineFlagsRepository = new QSPipelineFlagsRepository();
-
- mMainExecutor = new FakeExecutor(new FakeSystemClock());
-
- mSharedPreferencesByUser = new SparseArray<>();
- when(mTileLifecycleManagerFactory
- .create(any(Intent.class), any(UserHandle.class)))
- .thenReturn(mTileLifecycleManager);
- when(mUserFileManager.getSharedPreferences(anyString(), anyInt(), anyInt()))
- .thenAnswer((Answer<SharedPreferences>) invocation -> {
- assertEquals(QSTileHost.TILES, invocation.getArgument(0));
- int userId = invocation.getArgument(2);
- if (!mSharedPreferencesByUser.contains(userId)) {
- mSharedPreferencesByUser.put(userId, new FakeSharedPreferences());
- }
- return mSharedPreferencesByUser.get(userId);
- });
-
- mSecureSettings = new FakeSettings();
- saveSetting("");
- setUpTileFactory();
- mQSTileHost = new TestQSTileHost(mContext, () -> null, mDefaultFactory, mMainExecutor,
- mPluginManager, mTunerService, () -> mAutoTiles, () -> mShadeController,
- mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
- mTileLifecycleManagerFactory, mUserFileManager, mQSPipelineFlagsRepository);
- mMainExecutor.runAllReady();
-
- mSecureSettings.registerContentObserverForUserSync(SETTING, new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- mMainExecutor.execute(() -> mQSTileHost.onTuningChanged(SETTING, getSetting()));
- mMainExecutor.runAllReady();
- }
- }, mUserTracker.getUserId());
- }
-
- private void saveSetting(String value) {
- mSecureSettings.putStringForUser(
- SETTING, value, "", false, mUserTracker.getUserId(), false);
- }
-
- private String getSetting() {
- return mSecureSettings.getStringForUser(SETTING, mUserTracker.getUserId());
- }
-
- private void setUpTileFactory() {
- mDefaultFactory = new FakeQSFactory(spec -> {
- if ("spec1".equals(spec)) {
- return new TestTile1(mQSTileHost);
- } else if ("spec2".equals(spec)) {
- return new TestTile2(mQSTileHost);
- } else if ("spec3".equals(spec)) {
- return new TestTile3(mQSTileHost);
- } else if ("na".equals(spec)) {
- return new NotAvailableTile(mQSTileHost);
- } else if (CUSTOM_TILE_SPEC.equals(spec)) {
- QSTile tile = mCustomTile;
- QSTile.State s = mock(QSTile.State.class);
- s.spec = spec;
- when(mCustomTile.getState()).thenReturn(s);
- return tile;
- } else if ("internet".equals(spec)
- || "wifi".equals(spec)
- || "cell".equals(spec)) {
- return new TestTile1(mQSTileHost);
- } else {
- return null;
- }
- });
- when(mCustomTile.isAvailable()).thenReturn(true);
- }
-
- @Test
- public void testLoadTileSpecs_emptySetting() {
- List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
- assertFalse(tiles.isEmpty());
- }
-
- @Test
- public void testLoadTileSpecs_nullSetting() {
- List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
- assertFalse(tiles.isEmpty());
- }
-
- @Test
- public void testInvalidSpecUsesDefault() {
- mContext.getOrCreateTestableResources()
- .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
- saveSetting("not-valid");
-
- assertEquals(2, mQSTileHost.getTiles().size());
- }
-
- @Test
- public void testRemoveWifiAndCellularWithoutInternet() {
- saveSetting("wifi, spec1, cell, spec2");
-
- assertEquals("internet", mQSTileHost.getSpecs().get(0));
- assertEquals("spec1", mQSTileHost.getSpecs().get(1));
- assertEquals("spec2", mQSTileHost.getSpecs().get(2));
- }
-
- @Test
- public void testRemoveWifiAndCellularWithInternet() {
- saveSetting("wifi, spec1, cell, spec2, internet");
-
- assertEquals("spec1", mQSTileHost.getSpecs().get(0));
- assertEquals("spec2", mQSTileHost.getSpecs().get(1));
- assertEquals("internet", mQSTileHost.getSpecs().get(2));
- }
-
- @Test
- public void testRemoveWifiWithoutInternet() {
- saveSetting("spec1, wifi, spec2");
-
- assertEquals("spec1", mQSTileHost.getSpecs().get(0));
- assertEquals("internet", mQSTileHost.getSpecs().get(1));
- assertEquals("spec2", mQSTileHost.getSpecs().get(2));
- }
-
- @Test
- public void testRemoveCellWithInternet() {
- saveSetting("spec1, spec2, cell, internet");
-
- assertEquals("spec1", mQSTileHost.getSpecs().get(0));
- assertEquals("spec2", mQSTileHost.getSpecs().get(1));
- assertEquals("internet", mQSTileHost.getSpecs().get(2));
- }
-
- @Test
- public void testNoWifiNoCellularNoInternet() {
- saveSetting("spec1,spec2");
-
- assertEquals("spec1", mQSTileHost.getSpecs().get(0));
- assertEquals("spec2", mQSTileHost.getSpecs().get(1));
- }
-
- @Test
- public void testSpecWithInvalidDoesNotUseDefault() {
- mContext.getOrCreateTestableResources()
- .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
- saveSetting("spec2,not-valid");
-
- assertEquals(1, mQSTileHost.getTiles().size());
- QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
- assertTrue(element instanceof TestTile2);
- }
-
- @Test
- public void testDump() {
- saveSetting("spec1,spec2");
- StringWriter w = new StringWriter();
- PrintWriter pw = new PrintWriter(w);
- mQSTileHost.dump(pw, new String[]{});
-
- String output = "QSTileHost:" + "\n"
- + "tile specs: [spec1, spec2]" + "\n"
- + "current user: 0" + "\n"
- + "is dirty: false" + "\n"
- + "tiles:" + "\n"
- + "TestTile1:" + "\n"
- + " MockState" + "\n"
- + "TestTile2:" + "\n"
- + " MockState" + "\n";
-
- System.out.println(output);
- System.out.println(w.getBuffer().toString());
-
- assertEquals(output, w.getBuffer().toString());
- }
-
- @Test
- public void testDefault() {
- mContext.getOrCreateTestableResources()
- .addOverride(R.string.quick_settings_tiles_default, "spec1");
- saveSetting("default");
- assertEquals(1, mQSTileHost.getTiles().size());
- QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
- assertTrue(element instanceof TestTile1);
- verify(mQSLogger).logTileAdded("spec1");
- }
-
- @Test
- public void testNoRepeatedSpecs_addTile() {
- mContext.getOrCreateTestableResources()
- .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
- saveSetting("spec1,spec2");
-
- mQSTileHost.addTile("spec1");
-
- assertEquals(2, mQSTileHost.getSpecs().size());
- assertEquals("spec1", mQSTileHost.getSpecs().get(0));
- assertEquals("spec2", mQSTileHost.getSpecs().get(1));
- }
-
- @Test
- public void testAddTileAtValidPosition() {
- mContext.getOrCreateTestableResources()
- .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
- saveSetting("spec1,spec3");
-
- mQSTileHost.addTile("spec2", 1);
- mMainExecutor.runAllReady();
-
- assertEquals(3, mQSTileHost.getSpecs().size());
- assertEquals("spec1", mQSTileHost.getSpecs().get(0));
- assertEquals("spec2", mQSTileHost.getSpecs().get(1));
- assertEquals("spec3", mQSTileHost.getSpecs().get(2));
- }
-
- @Test
- public void testAddTileAtInvalidPositionAddsToEnd() {
- mContext.getOrCreateTestableResources()
- .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
- saveSetting("spec1,spec3");
-
- mQSTileHost.addTile("spec2", 100);
- mMainExecutor.runAllReady();
-
- assertEquals(3, mQSTileHost.getSpecs().size());
- assertEquals("spec1", mQSTileHost.getSpecs().get(0));
- assertEquals("spec3", mQSTileHost.getSpecs().get(1));
- assertEquals("spec2", mQSTileHost.getSpecs().get(2));
- }
-
- @Test
- public void testAddTileAtEnd() {
- mContext.getOrCreateTestableResources()
- .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
- saveSetting("spec1,spec3");
-
- mQSTileHost.addTile("spec2", QSTileHost.POSITION_AT_END);
- mMainExecutor.runAllReady();
-
- assertEquals(3, mQSTileHost.getSpecs().size());
- assertEquals("spec1", mQSTileHost.getSpecs().get(0));
- assertEquals("spec3", mQSTileHost.getSpecs().get(1));
- assertEquals("spec2", mQSTileHost.getSpecs().get(2));
- }
-
- @Test
- public void testNoRepeatedSpecs_customTile() {
- saveSetting(CUSTOM_TILE_SPEC);
-
- mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
- mMainExecutor.runAllReady();
-
- assertEquals(1, mQSTileHost.getSpecs().size());
- assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
- }
-
- @Test
- public void testAddedAtBeginningOnDefault_customTile() {
- saveSetting("spec1"); // seed
-
- mQSTileHost.addTile(CUSTOM_TILE);
- mMainExecutor.runAllReady();
-
- assertEquals(2, mQSTileHost.getSpecs().size());
- assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
- }
-
- @Test
- public void testAddedAtBeginning_customTile() {
- saveSetting("spec1"); // seed
-
- mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
- mMainExecutor.runAllReady();
-
- assertEquals(2, mQSTileHost.getSpecs().size());
- assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
- }
-
- @Test
- public void testAddedAtEnd_customTile() {
- saveSetting("spec1"); // seed
-
- mQSTileHost.addTile(CUSTOM_TILE, /* end */ true);
- mMainExecutor.runAllReady();
-
- assertEquals(2, mQSTileHost.getSpecs().size());
- assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(1));
- }
-
- @Test
- public void testLoadTileSpec_repeated() {
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");
-
- assertEquals(2, specs.size());
- assertEquals("spec1", specs.get(0));
- assertEquals("spec2", specs.get(1));
- }
-
- @Test
- public void testLoadTileSpec_repeatedInDefault() {
- mContext.getOrCreateTestableResources()
- .addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");
- }
-
- @Test
- public void testLoadTileSpec_repeatedDefaultAndSetting() {
- mContext.getOrCreateTestableResources()
- .addOverride(R.string.quick_settings_tiles_default, "spec1");
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");
- }
-
- @Test
- public void testNotAvailableTile_specNotNull() {
- saveSetting("na");
- verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
- }
-
- @Test
- public void testCustomTileRemoved_stateDeleted() {
- mQSTileHost.changeTilesByUser(List.of(CUSTOM_TILE_SPEC), List.of());
-
- verify(mCustomTileStatePersister)
- .removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
- }
-
- @Test
- public void testRemoveTiles() {
- saveSetting("spec1,spec2,spec3");
-
- mQSTileHost.removeTiles(List.of("spec1", "spec2"));
-
- mMainExecutor.runAllReady();
- assertEquals(List.of("spec3"), mQSTileHost.getSpecs());
- }
-
- @Test
- public void testTilesRemovedInQuickSuccession() {
- saveSetting("spec1,spec2,spec3");
- mQSTileHost.removeTile("spec1");
- mQSTileHost.removeTile("spec3");
-
- mMainExecutor.runAllReady();
- assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
- assertEquals("spec2", getSetting());
- }
-
- @Test
- public void testAddTileInMainThread() {
- saveSetting("spec1,spec2");
-
- mQSTileHost.addTile("spec3");
- assertEquals(List.of("spec1", "spec2"), mQSTileHost.getSpecs());
-
- mMainExecutor.runAllReady();
- assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.getSpecs());
- }
-
- @Test
- public void testRemoveTileInMainThread() {
- saveSetting("spec1,spec2");
-
- mQSTileHost.removeTile("spec1");
- assertEquals(List.of("spec1", "spec2"), mQSTileHost.getSpecs());
-
- mMainExecutor.runAllReady();
- assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
- }
-
- @Test
- public void testRemoveTilesInMainThread() {
- saveSetting("spec1,spec2,spec3");
-
- mQSTileHost.removeTiles(List.of("spec3", "spec1"));
- assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.getSpecs());
-
- mMainExecutor.runAllReady();
- assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
- }
-
- @Test
- public void testRemoveTileByUserInMainThread() {
- saveSetting("spec1," + CUSTOM_TILE_SPEC);
-
- mQSTileHost.removeTileByUser(CUSTOM_TILE);
- assertEquals(List.of("spec1", CUSTOM_TILE_SPEC), mQSTileHost.getSpecs());
-
- mMainExecutor.runAllReady();
- assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
- }
-
- @Test
- public void testNonValidTileNotStoredInSettings() {
- saveSetting("spec1,not-valid");
-
- assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
- assertEquals("spec1", getSetting());
- }
-
- @Test
- public void testNotAvailableTileNotStoredInSettings() {
- saveSetting("spec1,na");
-
- assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
- assertEquals("spec1", getSetting());
- }
-
- @Test
- public void testIsTileAdded_true() {
- int user = mUserTracker.getUserId();
- getSharedPreferencesForUser(user)
- .edit()
- .putBoolean(CUSTOM_TILE.flattenToString(), true)
- .apply();
-
- assertTrue(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
- }
-
- @Test
- public void testIsTileAdded_false() {
- int user = mUserTracker.getUserId();
- getSharedPreferencesForUser(user)
- .edit()
- .putBoolean(CUSTOM_TILE.flattenToString(), false)
- .apply();
-
- assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
- }
-
- @Test
- public void testIsTileAdded_notSet() {
- int user = mUserTracker.getUserId();
-
- assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
- }
-
- @Test
- public void testIsTileAdded_differentUser() {
- int user = mUserTracker.getUserId();
- mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user)
- .edit()
- .putBoolean(CUSTOM_TILE.flattenToString(), true)
- .apply();
-
- assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user + 1));
- }
-
- @Test
- public void testSetTileAdded_true() {
- int user = mUserTracker.getUserId();
- mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
-
- assertTrue(getSharedPreferencesForUser(user)
- .getBoolean(CUSTOM_TILE.flattenToString(), false));
- }
-
- @Test
- public void testSetTileAdded_false() {
- int user = mUserTracker.getUserId();
- mQSTileHost.setTileAdded(CUSTOM_TILE, user, false);
-
- assertFalse(getSharedPreferencesForUser(user)
- .getBoolean(CUSTOM_TILE.flattenToString(), false));
- }
-
- @Test
- public void testSetTileAdded_differentUser() {
- int user = mUserTracker.getUserId();
- mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
-
- assertFalse(getSharedPreferencesForUser(user + 1)
- .getBoolean(CUSTOM_TILE.flattenToString(), false));
- }
-
- @Test
- public void testSetTileRemoved_afterCustomTileChangedByUser() {
- int user = mUserTracker.getUserId();
- saveSetting(CUSTOM_TILE_SPEC);
-
- // This will be done by TileServiceManager
- mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
-
- mQSTileHost.changeTilesByUser(mQSTileHost.getSpecs(), List.of("spec1"));
- assertFalse(getSharedPreferencesForUser(user)
- .getBoolean(CUSTOM_TILE.flattenToString(), false));
- }
-
- @Test
- public void testSetTileRemoved_removedByUser() {
- int user = mUserTracker.getUserId();
- saveSetting(CUSTOM_TILE_SPEC);
-
- // This will be done by TileServiceManager
- mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
-
- mQSTileHost.removeTileByUser(CUSTOM_TILE);
- mMainExecutor.runAllReady();
- assertFalse(getSharedPreferencesForUser(user)
- .getBoolean(CUSTOM_TILE.flattenToString(), false));
- }
-
- @Test
- public void testSetTileRemoved_removedBySystem() {
- int user = mUserTracker.getUserId();
- saveSetting("spec1," + CUSTOM_TILE_SPEC);
-
- // This will be done by TileServiceManager
- mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
-
- mQSTileHost.removeTile(CUSTOM_TILE_SPEC);
- mMainExecutor.runAllReady();
- assertFalse(getSharedPreferencesForUser(user)
- .getBoolean(CUSTOM_TILE.flattenToString(), false));
- }
-
- @Test
- public void testProtoDump_noTiles() {
- SystemUIProtoDump proto = new SystemUIProtoDump();
- mQSTileHost.dumpProto(proto, new String[0]);
-
- assertEquals(0, proto.tiles.length);
- }
-
- @Test
- public void testTilesInOrder() {
- saveSetting("spec1," + CUSTOM_TILE_SPEC);
-
- SystemUIProtoDump proto = new SystemUIProtoDump();
- mQSTileHost.dumpProto(proto, new String[0]);
-
- assertEquals(2, proto.tiles.length);
- assertEquals("spec1", proto.tiles[0].getSpec());
- assertEquals(CUSTOM_TILE.getPackageName(), proto.tiles[1].getComponentName().packageName);
- assertEquals(CUSTOM_TILE.getClassName(), proto.tiles[1].getComponentName().className);
- }
-
- private SharedPreferences getSharedPreferencesForUser(int user) {
- return mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user);
- }
-
- private class TestQSTileHost extends QSTileHost {
- TestQSTileHost(Context context, Lazy<NewQSTileFactory> newQSTileFactoryProvider,
- QSFactory defaultFactory, Executor mainExecutor,
- PluginManager pluginManager, TunerService tunerService,
- Provider<AutoTileManager> autoTiles,
- Lazy<ShadeController> shadeController, QSLogger qsLogger,
- UserTracker userTracker, SecureSettings secureSettings,
- CustomTileStatePersister customTileStatePersister,
- TileLifecycleManager.Factory tileLifecycleManagerFactory,
- UserFileManager userFileManager, QSPipelineFlagsRepository featureFlags) {
- super(context, newQSTileFactoryProvider, defaultFactory, mainExecutor, pluginManager,
- tunerService, autoTiles, shadeController, qsLogger,
- userTracker, secureSettings, customTileStatePersister,
- tileLifecycleManagerFactory, userFileManager, featureFlags);
- }
-
- @Override
- public void onPluginConnected(QSFactory plugin, Context pluginContext) {
- }
-
- @Override
- public void onPluginDisconnected(QSFactory plugin) {
- }
- }
-
-
- private class TestTile extends QSTileImpl<QSTile.State> {
-
- protected TestTile(QSHost host) {
- super(
- host,
- mock(QsEventLogger.class),
- mock(Looper.class),
- mock(Handler.class),
- new FalsingManagerFake(),
- mock(MetricsLogger.class),
- mock(StatusBarStateController.class),
- mock(ActivityStarter.class),
- QSTileHostTest.this.mQSLogger
- );
- }
-
- @Override
- public State newTileState() {
- State s = mock(QSTile.State.class);
- when(s.toString()).thenReturn(MOCK_STATE_STRING);
- return s;
- }
-
- @Override
- protected void handleClick(@Nullable Expandable expandable) {}
-
- @Override
- protected void handleUpdateState(State state, Object arg) {}
-
- @Override
- public int getMetricsCategory() {
- return 0;
- }
-
- @Override
- public Intent getLongClickIntent() {
- return null;
- }
-
- @Override
- public CharSequence getTileLabel() {
- return null;
- }
- }
-
- private class TestTile1 extends TestTile {
-
- protected TestTile1(QSHost host) {
- super(host);
- }
- }
-
- private class TestTile2 extends TestTile {
-
- protected TestTile2(QSHost host) {
- super(host);
- }
- }
-
- private class TestTile3 extends TestTile {
-
- protected TestTile3(QSHost host) {
- super(host);
- }
- }
-
- private class NotAvailableTile extends TestTile {
-
- protected NotAvailableTile(QSHost host) {
- super(host);
- }
-
- @Override
- public boolean isAvailable() {
- return false;
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
index 970cd17..090a85b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
@@ -15,20 +15,6 @@
private val underTest = QSPipelineFlagsRepository()
@Test
- fun pipelineFlagDisabled() {
- mSetFlagsRule.disableFlags(Flags.FLAG_QS_NEW_PIPELINE)
-
- assertThat(underTest.pipelineEnabled).isFalse()
- }
-
- @Test
- fun pipelineFlagEnabled() {
- mSetFlagsRule.enableFlags(Flags.FLAG_QS_NEW_PIPELINE)
-
- assertThat(underTest.pipelineEnabled).isTrue()
- }
-
- @Test
fun tilesFlagDisabled() {
mSetFlagsRule.disableFlags(Flags.FLAG_QS_NEW_TILES)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 2d11917..63192f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -36,6 +36,7 @@
import android.animation.Animator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.os.Handler;
+import android.platform.test.annotations.EnableFlags;
import android.service.notification.StatusBarNotification;
import android.testing.TestableLooper;
import android.view.MotionEvent;
@@ -52,6 +53,7 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import org.junit.Before;
import org.junit.Rule;
@@ -672,17 +674,31 @@
}
@Test
- public void testForceResetSwipeStateDoesNothingIfTranslationIsZero() {
+ public void testForceResetSwipeStateDoesNothingIfTranslationIsZeroAndAlphaIsOne() {
doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
doReturn(0f).when(mNotificationRow).getTranslationX();
+ doReturn(1f).when(mNotificationRow).getAlpha();
mSwipeHelper.forceResetSwipeState(mNotificationRow);
verify(mNotificationRow).getTranslationX();
+ verify(mNotificationRow).getAlpha();
verifyNoMoreInteractions(mNotificationRow);
}
@Test
+ @EnableFlags(NotificationContentAlphaOptimization.FLAG_NAME)
+ public void testForceResetSwipeStateResetsAlphaIfTranslationIsZeroAndAlphaNotOne() {
+ doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
+ doReturn(0f).when(mNotificationRow).getTranslationX();
+ doReturn(0.5f).when(mNotificationRow).getAlpha();
+
+ mSwipeHelper.forceResetSwipeState(mNotificationRow);
+
+ verify(mNotificationRow).setContentAlpha(eq(1f));
+ }
+
+ @Test
public void testForceResetSwipeStateResetsTranslationAndAlpha() {
doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
doReturn(10f).when(mNotificationRow).getTranslationX();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
deleted file mode 100644
index 665544d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ /dev/null
@@ -1,648 +0,0 @@
-/*
- * Copyright (C) 2017 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.phone;
-
-import static com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE;
-import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
-import static com.android.systemui.statusbar.phone.AutoTileManager.DEVICE_CONTROLS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNotNull;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.display.ColorDisplayManager;
-import android.hardware.display.NightDisplayListener;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dagger.NightDisplayListenerModule;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.AutoAddTracker;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.UserSettingObserver;
-import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.res.R;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastDevice;
-import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.DeviceControlsController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.SafetyController;
-import com.android.systemui.statusbar.policy.WalletController;
-import com.android.systemui.util.settings.FakeSettings;
-import com.android.systemui.util.settings.SecureSettings;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
-import org.mockito.stubbing.Answer;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.inject.Named;
-
-@RunWith(AndroidJUnit4.class)
-@RunWithLooper
-@SmallTest
-public class AutoTileManagerTest extends SysuiTestCase {
-
- private static final String TEST_SETTING = "setting";
- private static final String TEST_SPEC = "spec";
- private static final String TEST_SETTING_COMPONENT = "setting_component";
- private static final String TEST_COMPONENT = "test_pkg/test_cls";
- private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")";
- private static final String TEST_CUSTOM_SAFETY_CLASS = "safety_cls";
- private static final String TEST_CUSTOM_SAFETY_PKG = "safety_pkg";
- private static final String TEST_CUSTOM_SAFETY_SPEC = CustomTile.toSpec(new ComponentName(
- TEST_CUSTOM_SAFETY_PKG, TEST_CUSTOM_SAFETY_CLASS));
- private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR;
-
- private static final int USER = 0;
-
- @Mock private QSHost mQsHost;
- @Mock private AutoAddTracker mAutoAddTracker;
- @Mock private CastController mCastController;
- @Mock private HotspotController mHotspotController;
- @Mock private DataSaverController mDataSaverController;
- @Mock private ManagedProfileController mManagedProfileController;
- @Mock private NightDisplayListener mNightDisplayListener;
- @Mock(answer = Answers.RETURNS_SELF)
- private NightDisplayListenerModule.Builder mNightDisplayListenerBuilder;
- @Mock private ReduceBrightColorsController mReduceBrightColorsController;
- @Mock private DeviceControlsController mDeviceControlsController;
- @Mock private WalletController mWalletController;
- @Mock private SafetyController mSafetyController;
- @Mock(answer = Answers.RETURNS_SELF)
- private AutoAddTracker.Builder mAutoAddTrackerBuilder;
- @Mock private Context mUserContext;
- @Spy private PackageManager mPackageManager;
- private final boolean mIsReduceBrightColorsAvailable = true;
-
- private AutoTileManager mAutoTileManager; // under test
-
- private SecureSettings mSecureSettings;
- private ManagedProfileController.Callback mManagedProfileCallback;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mSecureSettings = new FakeSettings();
-
- mSetFlagsRule.disableFlags(FLAG_QS_NEW_PIPELINE);
-
- mContext.getOrCreateTestableResources().addOverride(
- R.array.config_quickSettingsAutoAdd,
- new String[] {
- TEST_SETTING + SEPARATOR + TEST_SPEC,
- TEST_SETTING_COMPONENT + SEPARATOR + TEST_CUSTOM_SPEC
- }
- );
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_nightDisplayAvailable, true);
- mContext.getOrCreateTestableResources().addOverride(
- R.string.safety_quick_settings_tile_class, TEST_CUSTOM_SAFETY_CLASS);
-
- when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker);
- when(mQsHost.getUserContext()).thenReturn(mUserContext);
- when(mUserContext.getUser()).thenReturn(UserHandle.of(USER));
- mPackageManager = Mockito.spy(mContext.getPackageManager());
- when(mPackageManager.getPermissionControllerPackageName())
- .thenReturn(TEST_CUSTOM_SAFETY_PKG);
- Context context = Mockito.spy(mContext);
- when(context.getPackageManager()).thenReturn(mPackageManager);
- when(mNightDisplayListenerBuilder.build()).thenReturn(mNightDisplayListener);
-
- mAutoTileManager = createAutoTileManager(context);
- mAutoTileManager.init();
- }
-
- @After
- public void tearDown() {
- mAutoTileManager.destroy();
- }
-
- private AutoTileManager createAutoTileManager(
- Context context,
- AutoAddTracker.Builder autoAddTrackerBuilder,
- HotspotController hotspotController,
- DataSaverController dataSaverController,
- ManagedProfileController managedProfileController,
- NightDisplayListenerModule.Builder nightDisplayListenerBuilder,
- CastController castController,
- ReduceBrightColorsController reduceBrightColorsController,
- DeviceControlsController deviceControlsController,
- WalletController walletController,
- SafetyController safetyController,
- @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
- return new AutoTileManager(context, autoAddTrackerBuilder, mQsHost,
- Handler.createAsync(TestableLooper.get(this).getLooper()),
- mSecureSettings,
- hotspotController,
- dataSaverController,
- managedProfileController,
- mNightDisplayListenerBuilder,
- castController,
- reduceBrightColorsController,
- deviceControlsController,
- walletController,
- safetyController,
- isReduceBrightColorsAvailable);
- }
-
- private AutoTileManager createAutoTileManager(Context context) {
- return createAutoTileManager(context, mAutoAddTrackerBuilder, mHotspotController,
- mDataSaverController, mManagedProfileController, mNightDisplayListenerBuilder,
- mCastController, mReduceBrightColorsController, mDeviceControlsController,
- mWalletController, mSafetyController, mIsReduceBrightColorsAvailable);
- }
-
- @Test
- public void testCreatedAutoTileManagerIsNotInitialized() {
- AutoAddTracker.Builder builder = mock(AutoAddTracker.Builder.class, Answers.RETURNS_SELF);
- AutoAddTracker tracker = mock(AutoAddTracker.class);
- when(builder.build()).thenReturn(tracker);
- HotspotController hC = mock(HotspotController.class);
- DataSaverController dSC = mock(DataSaverController.class);
- ManagedProfileController mPC = mock(ManagedProfileController.class);
- NightDisplayListenerModule.Builder nDSB = mock(NightDisplayListenerModule.Builder.class);
- CastController cC = mock(CastController.class);
- ReduceBrightColorsController rBC = mock(ReduceBrightColorsController.class);
- DeviceControlsController dCC = mock(DeviceControlsController.class);
- WalletController wC = mock(WalletController.class);
- SafetyController sC = mock(SafetyController.class);
-
- AutoTileManager manager =
- createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDSB, cC, rBC,
- dCC, wC, sC, true);
-
- verify(tracker, never()).initialize();
- verify(hC, never()).addCallback(any());
- verify(dSC, never()).addCallback(any());
- verify(mPC, never()).addCallback(any());
- verifyNoMoreInteractions(nDSB);
- verify(cC, never()).addCallback(any());
- verify(rBC, never()).addCallback(any());
- verify(dCC, never()).setCallback(any());
- verify(wC, never()).getWalletPosition();
- verify(sC, never()).addCallback(any());
- assertNull(manager.getSecureSettingForKey(TEST_SETTING));
- assertNull(manager.getSecureSettingForKey(TEST_SETTING_COMPONENT));
- }
-
- @Test
- public void testChangeUserWhenNotInitializedThrows() {
- AutoTileManager manager = createAutoTileManager(mock(Context.class));
-
- try {
- manager.changeUser(UserHandle.of(USER + 1));
- fail();
- } catch (Exception e) {
- // This should throw and take this path
- }
- }
-
- @Test
- public void testChangeUserCallbacksStoppedAndStarted() throws Exception {
- TestableLooper.get(this).runWithLooper(() ->
- mAutoTileManager.changeUser(UserHandle.of(USER + 1))
- );
-
- InOrder inOrderHotspot = inOrder(mHotspotController);
- inOrderHotspot.verify(mHotspotController).removeCallback(any());
- inOrderHotspot.verify(mHotspotController).addCallback(any());
-
- InOrder inOrderDataSaver = inOrder(mDataSaverController);
- inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
- inOrderDataSaver.verify(mDataSaverController).addCallback(any());
-
- InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
- inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
- inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
-
- if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
- inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
- inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
- }
-
- InOrder inOrderReduceBrightColors = inOrder(mReduceBrightColorsController);
- inOrderReduceBrightColors.verify(mReduceBrightColorsController).removeCallback(any());
- inOrderReduceBrightColors.verify(mReduceBrightColorsController).addCallback(any());
-
- InOrder inOrderCast = inOrder(mCastController);
- inOrderCast.verify(mCastController).removeCallback(any());
- inOrderCast.verify(mCastController).addCallback(any());
-
- InOrder inOrderDevices = inOrder(mDeviceControlsController);
- inOrderDevices.verify(mDeviceControlsController).removeCallback();
- inOrderDevices.verify(mDeviceControlsController).setCallback(any());
-
- verify(mWalletController, times(2)).getWalletPosition();
-
- InOrder inOrderSafety = inOrder(mSafetyController);
- inOrderSafety.verify(mSafetyController).removeCallback(any());
- inOrderSafety.verify(mSafetyController).addCallback(any());
-
- UserSettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
- assertEquals(USER + 1, setting.getCurrentUser());
- assertTrue(setting.isListening());
- }
-
- @Test
- public void testChangeUserSomeCallbacksNotAdded() throws Exception {
- when(mAutoAddTracker.isAdded("hotspot")).thenReturn(true);
- when(mAutoAddTracker.isAdded("work")).thenReturn(true);
- when(mAutoAddTracker.isAdded("cast")).thenReturn(true);
- when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
-
- TestableLooper.get(this).runWithLooper(() ->
- mAutoTileManager.changeUser(UserHandle.of(USER + 1))
- );
-
- verify(mAutoAddTracker).changeUser(UserHandle.of(USER + 1));
-
- InOrder inOrderHotspot = inOrder(mHotspotController);
- inOrderHotspot.verify(mHotspotController).removeCallback(any());
- inOrderHotspot.verify(mHotspotController, never()).addCallback(any());
-
- InOrder inOrderDataSaver = inOrder(mDataSaverController);
- inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
- inOrderDataSaver.verify(mDataSaverController).addCallback(any());
-
- InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
- inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
- inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
-
- if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
- inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
- inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
- }
-
- InOrder inOrderReduceBrightColors = inOrder(mReduceBrightColorsController);
- inOrderReduceBrightColors.verify(mReduceBrightColorsController).removeCallback(any());
- inOrderReduceBrightColors.verify(mReduceBrightColorsController).addCallback(any());
-
- InOrder inOrderCast = inOrder(mCastController);
- inOrderCast.verify(mCastController).removeCallback(any());
- inOrderCast.verify(mCastController, never()).addCallback(any());
-
- InOrder inOrderDevices = inOrder(mDeviceControlsController);
- inOrderDevices.verify(mDeviceControlsController).removeCallback();
- inOrderDevices.verify(mDeviceControlsController).setCallback(any());
-
- verify(mWalletController, times(2)).getWalletPosition();
-
- InOrder inOrderSafety = inOrder(mSafetyController);
- inOrderSafety.verify(mSafetyController).removeCallback(any());
- inOrderSafety.verify(mSafetyController).addCallback(any());
-
- UserSettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
- assertEquals(USER + 1, setting.getCurrentUser());
- assertFalse(setting.isListening());
- }
-
- @Test
- public void testGetCurrentUserId() throws Exception {
- assertEquals(USER, mAutoTileManager.getCurrentUserId());
-
- TestableLooper.get(this).runWithLooper(() ->
- mAutoTileManager.changeUser(UserHandle.of(USER + 100))
- );
-
- assertEquals(USER + 100, mAutoTileManager.getCurrentUserId());
- }
-
- @Test
- public void nightTileAdded_whenActivated() {
- if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- return;
- }
- mAutoTileManager.mNightDisplayCallback.onActivated(true);
- verify(mQsHost).addTile("night");
- }
-
- @Test
- public void nightTileNotAdded_whenDeactivated() {
- if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- return;
- }
- mAutoTileManager.mNightDisplayCallback.onActivated(false);
- verify(mQsHost, never()).addTile("night");
- }
-
- @Test
- public void nightTileAdded_whenNightModeTwilight() {
- if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- return;
- }
- mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
- ColorDisplayManager.AUTO_MODE_TWILIGHT);
- verify(mQsHost).addTile("night");
- }
-
- @Test
- public void nightTileAdded_whenNightModeCustom() {
- if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- return;
- }
- mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
- ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
- verify(mQsHost).addTile("night");
- }
-
- @Test
- public void nightTileNotAdded_whenNightModeDisabled() {
- if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- return;
- }
- mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
- ColorDisplayManager.AUTO_MODE_DISABLED);
- verify(mQsHost, never()).addTile("night");
- }
-
- @Test
- public void reduceBrightColorsTileAdded_whenActivated() {
- mAutoTileManager.mReduceBrightColorsCallback.onActivated(true);
- verify(mQsHost).addTile("reduce_brightness");
- }
-
- @Test
- public void reduceBrightColorsTileNotAdded_whenDeactivated() {
- mAutoTileManager.mReduceBrightColorsCallback.onActivated(false);
- verify(mQsHost, never()).addTile("reduce_brightness");
- }
-
- private static List<CastDevice> buildFakeCastDevice(boolean isCasting) {
- CastDevice.CastState state = isCasting
- ? CastDevice.CastState.Connected
- : CastDevice.CastState.Disconnected;
- return Collections.singletonList(
- new CastDevice(
- "id",
- /* name= */ null,
- /* description= */ null,
- /* state= */ state,
- /* origin= */ CastDevice.CastOrigin.MediaProjection,
- /* tag= */ null));
- }
-
- @Test
- public void castTileAdded_whenDeviceIsCasting() {
- doReturn(buildFakeCastDevice(true)).when(mCastController).getCastDevices();
- mAutoTileManager.mCastCallback.onCastDevicesChanged();
- verify(mQsHost).addTile("cast");
- }
-
- @Test
- public void castTileNotAdded_whenDeviceIsNotCasting() {
- doReturn(buildFakeCastDevice(false)).when(mCastController).getCastDevices();
- mAutoTileManager.mCastCallback.onCastDevicesChanged();
- verify(mQsHost, never()).addTile("cast");
- }
-
- @Test
- public void testSettingTileAdded_onChanged() {
- changeValue(TEST_SETTING, 1);
- verify(mAutoAddTracker).setTileAdded(TEST_SPEC);
- verify(mQsHost).addTile(TEST_SPEC);
- }
-
- @Test
- public void testSettingTileAddedComponentAtEnd_onChanged() {
- changeValue(TEST_SETTING_COMPONENT, 1);
- verify(mAutoAddTracker).setTileAdded(TEST_CUSTOM_SPEC);
- verify(mQsHost).addTile(ComponentName.unflattenFromString(TEST_COMPONENT)
- , /* end */ true);
- }
-
- @Test
- public void testSettingTileAdded_onlyOnce() {
- changeValue(TEST_SETTING, 1);
- changeValue(TEST_SETTING, 2);
- verify(mAutoAddTracker).setTileAdded(TEST_SPEC);
- verify(mQsHost).addTile(TEST_SPEC);
- }
-
- @Test
- public void testSettingTileNotAdded_onChangedTo0() {
- changeValue(TEST_SETTING, 0);
- verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC);
- verify(mQsHost, never()).addTile(TEST_SPEC);
- }
-
- @Test
- public void testSettingTileNotAdded_ifPreviouslyAdded() {
- when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
-
- changeValue(TEST_SETTING, 1);
- verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC);
- verify(mQsHost, never()).addTile(TEST_SPEC);
- }
-
- @Test
- public void testSafetyTileNotAdded_ifPreviouslyAdded() {
- ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
- mAutoTileManager.init();
- verify(mQsHost, times(1)).addTile(safetyComponent, true);
- when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
- mAutoTileManager.init();
- verify(mQsHost, times(1)).addTile(safetyComponent, true);
- }
-
- @Test
- public void testSafetyTileAdded_onUserChange() {
- ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
- mAutoTileManager.init();
- verify(mQsHost, times(1)).addTile(safetyComponent, true);
- when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(false);
- mAutoTileManager.changeUser(UserHandle.of(USER + 1));
- verify(mQsHost, times(2)).addTile(safetyComponent, true);
- }
-
- @Test
- public void testSafetyTileRemoved_onSafetyCenterDisable() {
- ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
- mAutoTileManager.init();
- when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
- mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
- verify(mQsHost, times(1)).removeTile(TEST_CUSTOM_SAFETY_SPEC);
- }
-
- @Test
- public void testSafetyTileAdded_onSafetyCenterEnable() {
- ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
- mAutoTileManager.init();
- verify(mQsHost, times(1)).addTile(safetyComponent, true);
- mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
- mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(true);
- verify(mQsHost, times(2)).addTile(safetyComponent, true);
- }
-
- @Test
- public void managedProfileAdded_tileAdded() {
- when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(false);
- when(mAutoAddTracker.getRestoredTilePosition(eq("work"))).thenReturn(2);
- mAutoTileManager = createAutoTileManager(mContext);
- Mockito.doAnswer((Answer<Object>) invocation -> {
- mManagedProfileCallback = invocation.getArgument(0);
- return null;
- }).when(mManagedProfileController).addCallback(any());
- mAutoTileManager.init();
- when(mManagedProfileController.hasActiveProfile()).thenReturn(true);
-
- mManagedProfileCallback.onManagedProfileChanged();
-
- verify(mQsHost, times(1)).addTile(eq("work"), eq(2));
- verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
- }
-
- @Test
- public void managedProfileRemoved_tileRemoved() {
- when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(true);
- mAutoTileManager = createAutoTileManager(mContext);
- Mockito.doAnswer((Answer<Object>) invocation -> {
- mManagedProfileCallback = invocation.getArgument(0);
- return null;
- }).when(mManagedProfileController).addCallback(any());
- mAutoTileManager.init();
- when(mManagedProfileController.hasActiveProfile()).thenReturn(false);
-
- mManagedProfileCallback.onManagedProfileChanged();
-
- verify(mQsHost, times(1)).removeTile(eq("work"));
- verify(mAutoAddTracker, times(1)).setTileRemoved(eq("work"));
- }
-
- @Test
- public void testAddControlsTileIfNotPresent() {
- String spec = DEVICE_CONTROLS;
- when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(false);
- when(mQsHost.getTiles()).thenReturn(new ArrayList<>());
-
- mAutoTileManager.init();
- ArgumentCaptor<DeviceControlsController.Callback> captor =
- ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
-
- verify(mDeviceControlsController).setCallback(captor.capture());
-
- captor.getValue().onControlsUpdate(3);
- verify(mQsHost).addTile(spec, 3);
- verify(mAutoAddTracker).setTileAdded(spec);
- }
-
- @Test
- public void testDontAddControlsTileIfPresent() {
- String spec = DEVICE_CONTROLS;
- when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(false);
- when(mQsHost.getTiles()).thenReturn(new ArrayList<>());
-
- mAutoTileManager.init();
- ArgumentCaptor<DeviceControlsController.Callback> captor =
- ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
-
- verify(mDeviceControlsController).setCallback(captor.capture());
-
- captor.getValue().removeControlsAutoTracker();
- verify(mQsHost, never()).addTile(spec, 3);
- verify(mAutoAddTracker, never()).setTileAdded(spec);
- verify(mAutoAddTracker).setTileRemoved(spec);
- }
-
- @Test
- public void testRemoveControlsTileFromTrackerWhenRequested() {
- String spec = "controls";
- when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(true);
- QSTile mockTile = mock(QSTile.class);
- when(mockTile.getTileSpec()).thenReturn(spec);
- when(mQsHost.getTiles()).thenReturn(List.of(mockTile));
-
- mAutoTileManager.init();
- ArgumentCaptor<DeviceControlsController.Callback> captor =
- ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
-
- verify(mDeviceControlsController).setCallback(captor.capture());
-
- captor.getValue().onControlsUpdate(3);
- verify(mQsHost, never()).addTile(spec, 3);
- verify(mAutoAddTracker, never()).setTileAdded(spec);
- }
-
-
- @Test
- public void testEmptyArray_doesNotCrash() {
- mContext.getOrCreateTestableResources().addOverride(
- R.array.config_quickSettingsAutoAdd, new String[0]);
- createAutoTileManager(mContext).destroy();
- }
-
- @Test
- public void testMissingConfig_doesNotCrash() {
- mContext.getOrCreateTestableResources().addOverride(
- R.array.config_quickSettingsAutoAdd, null);
- createAutoTileManager(mContext).destroy();
- }
-
- @Test
- public void testUserChange_newNightDisplayListenerCreated() {
- UserHandle newUser = UserHandle.of(1000);
- mAutoTileManager.changeUser(newUser);
- InOrder inOrder = inOrder(mNightDisplayListenerBuilder);
- inOrder.verify(mNightDisplayListenerBuilder).setUser(newUser.getIdentifier());
- inOrder.verify(mNightDisplayListenerBuilder).build();
- }
-
- // Will only notify if it's listening
- private void changeValue(String key, int value) {
- mSecureSettings.putIntForUser(key, value, USER);
- TestableLooper.get(this).processAllMessages();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 5b45781..30e7247 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -33,8 +33,11 @@
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.SysuiTestCase
+import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.fakeDarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeControllerImpl
@@ -42,6 +45,7 @@
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.SysUIUnfoldComponent
@@ -70,7 +74,9 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class PhoneStatusBarViewControllerTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val fakeDarkIconDispatcher = kosmos.fakeDarkIconDispatcher
@Mock private lateinit var shadeViewController: ShadeViewController
@Mock private lateinit var panelExpansionInteractor: PanelExpansionInteractor
@Mock private lateinit var featureFlags: FeatureFlags
@@ -91,6 +97,12 @@
private lateinit var view: PhoneStatusBarView
private lateinit var controller: PhoneStatusBarViewController
+ private val clockView: Clock
+ get() = view.requireViewById(R.id.clock)
+
+ private val batteryView: BatteryMeterView
+ get() = view.requireViewById(R.id.battery)
+
private val unfoldConfig = UnfoldConfig()
@Before
@@ -114,16 +126,25 @@
@Test
fun onViewAttachedAndDrawn_startListeningConfigurationControllerCallback() {
val view = createViewMock()
- val argumentCaptor =
- ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java)
+
InstrumentationRegistry.getInstrumentation().runOnMainSync {
controller = createAndInitController(view)
}
- verify(configurationController).addCallback(argumentCaptor.capture())
- argumentCaptor.value.onDensityOrFontScaleChanged()
+ verify(configurationController).addCallback(any())
+ }
- verify(view).onDensityOrFontScaleChanged()
+ @Test
+ fun onViewAttachedAndDrawn_darkReceiversRegistered() {
+ val view = createViewMock()
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+
+ assertThat(fakeDarkIconDispatcher.receivers.size).isEqualTo(2)
+ assertThat(fakeDarkIconDispatcher.receivers).contains(clockView)
+ assertThat(fakeDarkIconDispatcher.receivers).contains(batteryView)
}
@Test
@@ -158,6 +179,21 @@
}
@Test
+ fun onViewDetached_darkReceiversUnregistered() {
+ val view = createViewMock()
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+
+ assertThat(fakeDarkIconDispatcher.receivers).isNotEmpty()
+
+ controller.onViewDetached()
+
+ assertThat(fakeDarkIconDispatcher.receivers).isEmpty()
+ }
+
+ @Test
fun handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() {
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(false)
val returnVal =
@@ -353,7 +389,8 @@
shadeLogger,
viewUtil,
configurationController,
- mStatusOverlayHoverListenerFactory
+ mStatusOverlayHoverListenerFactory,
+ fakeDarkIconDispatcher,
)
.create(view)
.also { it.init() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index abc50bc..ed5ec7b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -35,7 +35,6 @@
import com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP
import com.android.systemui.Gefingerpoken
import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.mockito.mock
@@ -64,7 +63,6 @@
StatusBarContentInsetsProvider::class.java,
contentInsetsProvider
)
- mDependency.injectTestDependency(DarkIconDispatcher::class.java, mock<DarkIconDispatcher>())
mDependency.injectTestDependency(StatusBarWindowController::class.java, windowController)
context.ensureTestableResources()
view = spy(createStatusBarView())
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
index e70631e..e8612d08 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.bouncer.ui.viewmodel
import android.content.applicationContext
@@ -26,26 +28,31 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.user.ui.viewmodel.userSwitcherViewModel
import com.android.systemui.util.time.systemClock
import kotlinx.coroutines.ExperimentalCoroutinesApi
-@ExperimentalCoroutinesApi
-val Kosmos.bouncerMessageViewModel by
- Kosmos.Fixture {
- BouncerMessageViewModel(
- applicationContext = applicationContext,
- applicationScope = testScope.backgroundScope,
- bouncerInteractor = bouncerInteractor,
- simBouncerInteractor = simBouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- selectedUser = userSwitcherViewModel.selectedUser,
- clock = systemClock,
- biometricMessageInteractor = biometricMessageInteractor,
- faceAuthInteractor = deviceEntryFaceAuthInteractor,
- deviceUnlockedInteractor = deviceUnlockedInteractor,
- deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
- flags = composeBouncerFlags,
- )
+val Kosmos.bouncerMessageViewModel by Fixture {
+ BouncerMessageViewModel(
+ applicationContext = applicationContext,
+ bouncerInteractor = bouncerInteractor,
+ simBouncerInteractor = simBouncerInteractor,
+ authenticationInteractor = authenticationInteractor,
+ userSwitcherViewModel = userSwitcherViewModel,
+ clock = systemClock,
+ biometricMessageInteractor = biometricMessageInteractor,
+ faceAuthInteractor = deviceEntryFaceAuthInteractor,
+ deviceUnlockedInteractor = deviceUnlockedInteractor,
+ deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
+ flags = composeBouncerFlags,
+ )
+}
+
+val Kosmos.bouncerMessageViewModelFactory by Fixture {
+ object : BouncerMessageViewModel.Factory {
+ override fun create(): BouncerMessageViewModel {
+ return bouncerMessageViewModel
+ }
}
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
index c3dad74..e405d17 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -21,6 +21,7 @@
import android.app.admin.devicePolicyManager
import android.content.applicationContext
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
@@ -28,28 +29,97 @@
import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.user.ui.viewmodel.userSwitcherViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.StateFlow
-val Kosmos.bouncerViewModel by Fixture {
- BouncerViewModel(
- applicationContext = applicationContext,
- applicationScope = testScope.backgroundScope,
- mainDispatcher = testDispatcher,
+val Kosmos.bouncerSceneActionsViewModel by Fixture {
+ BouncerSceneActionsViewModel(
bouncerInteractor = bouncerInteractor,
- inputMethodInteractor = inputMethodInteractor,
- simBouncerInteractor = simBouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- selectedUserInteractor = selectedUserInteractor,
- devicePolicyManager = devicePolicyManager,
- bouncerMessageViewModel = bouncerMessageViewModel,
- flags = composeBouncerFlags,
- selectedUser = userSwitcherViewModel.selectedUser,
- users = userSwitcherViewModel.users,
- userSwitcherMenu = userSwitcherViewModel.menu,
- actionButton = bouncerActionButtonInteractor.actionButton,
)
}
+
+val Kosmos.bouncerSceneActionsViewModelFactory by Fixture {
+ object : BouncerSceneActionsViewModel.Factory {
+ override fun create(): BouncerSceneActionsViewModel {
+ return bouncerSceneActionsViewModel
+ }
+ }
+}
+
+val Kosmos.bouncerSceneContentViewModel by Fixture {
+ BouncerSceneContentViewModel(
+ applicationContext = applicationContext,
+ bouncerInteractor = bouncerInteractor,
+ authenticationInteractor = authenticationInteractor,
+ devicePolicyManager = devicePolicyManager,
+ bouncerMessageViewModelFactory = bouncerMessageViewModelFactory,
+ flags = composeBouncerFlags,
+ userSwitcher = userSwitcherViewModel,
+ actionButtonInteractor = bouncerActionButtonInteractor,
+ pinViewModelFactory = pinBouncerViewModelFactory,
+ patternViewModelFactory = patternBouncerViewModelFactory,
+ passwordViewModelFactory = passwordBouncerViewModelFactory,
+ )
+}
+
+val Kosmos.bouncerSceneContentViewModelFactory by Fixture {
+ object : BouncerSceneContentViewModel.Factory {
+ override fun create(): BouncerSceneContentViewModel {
+ return bouncerSceneContentViewModel
+ }
+ }
+}
+
+val Kosmos.pinBouncerViewModelFactory by Fixture {
+ object : PinBouncerViewModel.Factory {
+ override fun create(
+ isInputEnabled: StateFlow<Boolean>,
+ onIntentionalUserInput: () -> Unit,
+ authenticationMethod: AuthenticationMethodModel,
+ ): PinBouncerViewModel {
+ return PinBouncerViewModel(
+ applicationContext = applicationContext,
+ interactor = bouncerInteractor,
+ simBouncerInteractor = simBouncerInteractor,
+ isInputEnabled = isInputEnabled,
+ onIntentionalUserInput = onIntentionalUserInput,
+ authenticationMethod = authenticationMethod,
+ )
+ }
+ }
+}
+
+val Kosmos.patternBouncerViewModelFactory by Fixture {
+ object : PatternBouncerViewModel.Factory {
+ override fun create(
+ isInputEnabled: StateFlow<Boolean>,
+ onIntentionalUserInput: () -> Unit,
+ ): PatternBouncerViewModel {
+ return PatternBouncerViewModel(
+ applicationContext = applicationContext,
+ interactor = bouncerInteractor,
+ isInputEnabled = isInputEnabled,
+ onIntentionalUserInput = onIntentionalUserInput,
+ )
+ }
+ }
+}
+
+val Kosmos.passwordBouncerViewModelFactory by Fixture {
+ object : PasswordBouncerViewModel.Factory {
+ override fun create(
+ isInputEnabled: StateFlow<Boolean>,
+ onIntentionalUserInput: () -> Unit,
+ ): PasswordBouncerViewModel {
+ return PasswordBouncerViewModel(
+ interactor = bouncerInteractor,
+ inputMethodInteractor = inputMethodInteractor,
+ selectedUserInteractor = selectedUserInteractor,
+ isInputEnabled = isInputEnabled,
+ onIntentionalUserInput = onIntentionalUserInput,
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorKosmos.kt
new file mode 100644
index 0000000..8124224
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorKosmos.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.common.usagestats.domain.interactor.usageStatsInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.shared.system.taskStackChangeListeners
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.widgetTrampolineInteractor: WidgetTrampolineInteractor by
+ Kosmos.Fixture {
+ WidgetTrampolineInteractor(
+ activityStarter = activityStarter,
+ systemClock = fakeSystemClock,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ taskStackChangeListeners = taskStackChangeListeners,
+ usageStatsInteractor = usageStatsInteractor,
+ logBuffer = logcatLogBuffer("WidgetTrampolineInteractor"),
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 4571c19..54a6c0c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -60,13 +60,13 @@
override val bottomAreaAlpha: StateFlow<Float> = _bottomAreaAlpha
private val _isKeyguardShowing = MutableStateFlow(false)
- override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
+ override val isKeyguardShowing: StateFlow<Boolean> = _isKeyguardShowing
private val _isKeyguardUnlocked = MutableStateFlow(false)
override val isKeyguardDismissible: StateFlow<Boolean> = _isKeyguardUnlocked.asStateFlow()
private val _isKeyguardOccluded = MutableStateFlow(false)
- override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded
+ override val isKeyguardOccluded: StateFlow<Boolean> = _isKeyguardOccluded
private val _isDozing = MutableStateFlow(false)
override val isDozing: StateFlow<Boolean> = _isDozing
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/DarkIconDispatcherKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/DarkIconDispatcherKosmos.kt
new file mode 100644
index 0000000..3d125e9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/DarkIconDispatcherKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.fakeDarkIconDispatcher: FakeDarkIconDispatcher by
+ Kosmos.Fixture { FakeDarkIconDispatcher() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeDarkIconDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeDarkIconDispatcher.kt
new file mode 100644
index 0000000..102a853
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeDarkIconDispatcher.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins
+
+import android.graphics.Rect
+import java.util.ArrayList
+
+class FakeDarkIconDispatcher : DarkIconDispatcher {
+ val receivers = mutableListOf<DarkIconDispatcher.DarkReceiver>()
+
+ override fun setIconsDarkArea(r: ArrayList<Rect>) {}
+
+ override fun addDarkReceiver(receiver: DarkIconDispatcher.DarkReceiver) {
+ receivers.add(receiver)
+ }
+
+ override fun removeDarkReceiver(receiver: DarkIconDispatcher.DarkReceiver) {
+ receivers.remove(receiver)
+ }
+
+ override fun applyDark(`object`: DarkIconDispatcher.DarkReceiver) {}
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/TaskStackChangeListenersKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/TaskStackChangeListenersKosmos.kt
new file mode 100644
index 0000000..67f611a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/TaskStackChangeListenersKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.taskStackChangeListeners: TaskStackChangeListeners by
+ Kosmos.Fixture { TaskStackChangeListeners.getTestInstance() }
diff --git a/services/contentcapture/java/com/android/server/contentprotection/OWNERS b/services/contentcapture/java/com/android/server/contentprotection/OWNERS
new file mode 100644
index 0000000..3d09da3
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentprotection/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 1040349
+
+include /core/java/android/view/contentprotection/OWNERS
+
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index f61bd60..154b52b 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1413,6 +1413,22 @@
}
public void uidRemoved(int uid) {
+ if (Flags.dontRemoveExistingUidStates()) {
+ // b/358365471 If apps sharing UID are installed on multiple users and only one of
+ // them is installed for a single user while keeping the others we observe this
+ // subroutine get invoked incorrectly since the UID still exists.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ String uidName = getPackageManagerInternal().getNameForUid(uid);
+ if (uidName != null) {
+ Slog.e(TAG, "Tried to remove existing UID. uid: " + uid + " name: " + uidName);
+ return;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
synchronized (this) {
if (mUidStates.indexOfKey(uid) >= 0) {
mUidStates.get(uid).clear();
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 226bdf5..240e91b 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+
import static com.android.server.display.BrightnessMappingStrategy.INVALID_LUX;
import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString;
@@ -241,6 +243,9 @@
private int mDisplayState = Display.STATE_UNKNOWN;
+ // True if the normal brightness should be forced while device is dozing.
+ private boolean mUseNormalBrightnessForDoze;
+
// True if we are collecting a brightness adjustment sample, along with some data
// for the initial state of the sample.
private boolean mBrightnessAdjustmentSamplePending;
@@ -442,11 +447,12 @@
public void configure(int state, @Nullable BrightnessConfiguration configuration,
float brightness, boolean userChangedBrightness, float adjustment,
boolean userChangedAutoBrightnessAdjustment, int displayPolicy, int displayState,
- boolean shouldResetShortTermModel) {
+ boolean useNormalBrightnessForDoze, boolean shouldResetShortTermModel) {
mState = state;
boolean changed = setBrightnessConfiguration(configuration, shouldResetShortTermModel);
changed |= setDisplayPolicy(displayPolicy);
mDisplayState = displayState;
+ mUseNormalBrightnessForDoze = useNormalBrightnessForDoze;
if (userChangedAutoBrightnessAdjustment) {
changed |= setAutoBrightnessAdjustment(adjustment);
}
@@ -1264,11 +1270,10 @@
}
private boolean shouldApplyDozeScaleFactor() {
- // Apply the doze scale factor if the display is in doze. We shouldn't rely on the display
- // policy here - the screen might turn on while the policy is POLICY_DOZE and in this
- // situation, we shouldn't apply the doze scale factor. We also don't apply the doze scale
- // factor if we have a designated brightness curve for doze.
- return Display.isDozeState(mDisplayState) && getMode() != AUTO_BRIGHTNESS_MODE_DOZE;
+ // We don't apply the doze scale factor if we have a designated brightness curve for doze.
+ return (mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()
+ ? !mUseNormalBrightnessForDoze && mDisplayPolicy == POLICY_DOZE
+ : Display.isDozeState(mDisplayState)) && getMode() != AUTO_BRIGHTNESS_MODE_DOZE;
}
private class ShortTermModel {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 8bb33dd..bd27f47 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1419,7 +1419,7 @@
mAutomaticBrightnessStrategy.setAutoBrightnessState(state,
allowAutoBrightnessWhileDozing, mBrightnessReasonTemp.getReason(),
- mPowerRequest.policy,
+ mPowerRequest.policy, mPowerRequest.useNormalBrightnessForDoze,
mDisplayBrightnessController.getLastUserSetScreenBrightness(),
userSetBrightnessChanged);
@@ -1485,7 +1485,9 @@
brightnessState = clampScreenBrightness(brightnessState);
}
- if (Display.isDozeState(state)) {
+ if (mFlags.isNormalBrightnessForDozeParameterEnabled()
+ ? !mPowerRequest.useNormalBrightnessForDoze && mPowerRequest.policy == POLICY_DOZE
+ : Display.isDozeState(state)) {
// TODO(b/329676661): Introduce a config property to choose between this brightness
// strategy and DOZE_DEFAULT
// On some devices, when auto-brightness is disabled and the device is dozing, we use
@@ -1506,7 +1508,7 @@
}
// Use default brightness when dozing unless overridden.
- if (Float.isNaN(brightnessState) && Display.isDozeState(state)
+ if (Float.isNaN(brightnessState)
&& !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig()) {
rawBrightnessState = mScreenBrightnessDozeConfig;
brightnessState = clampScreenBrightness(rawBrightnessState);
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 7835220..1b49bbc 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.view.Display;
@@ -167,7 +168,7 @@
StrategySelectionRequest strategySelectionRequest) {
DisplayBrightnessStrategy displayBrightnessStrategy = mInvalidBrightnessStrategy;
int targetDisplayState = strategySelectionRequest.getTargetDisplayState();
- DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = strategySelectionRequest
+ DisplayPowerRequest displayPowerRequest = strategySelectionRequest
.getDisplayPowerRequest();
setAllowAutoBrightnessWhileDozing(strategySelectionRequest.getDisplayOffloadSession());
if (targetDisplayState == Display.STATE_OFF) {
@@ -301,6 +302,7 @@
mAllowAutoBrightnessWhileDozing,
BrightnessReason.REASON_UNKNOWN,
strategySelectionRequest.getDisplayPowerRequest().policy,
+ strategySelectionRequest.getDisplayPowerRequest().useNormalBrightnessForDoze,
strategySelectionRequest.getLastUserSetScreenBrightness(),
strategySelectionRequest.isUserSetBrightnessChanged());
return mAutomaticBrightnessStrategy1.isAutoBrightnessValid();
@@ -331,12 +333,14 @@
/**
* Validates if the conditions are met to qualify for the DozeBrightnessStrategy.
*/
- private boolean shouldUseDozeBrightnessStrategy(
- DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
+ private boolean shouldUseDozeBrightnessStrategy(DisplayPowerRequest displayPowerRequest) {
// We are not checking the targetDisplayState, but rather relying on the policy because
// a user can define a different display state(displayPowerRequest.dozeScreenState) too
- // in the request with the Doze policy
- return displayPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE
+ // in the request with the Doze policy and user might request an override to force certain
+ // brightness.
+ return (!mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()
+ || !displayPowerRequest.useNormalBrightnessForDoze)
+ && displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE
&& !mAllowAutoBrightnessWhileDozing
&& BrightnessUtils.isValidBrightnessValue(displayPowerRequest.dozeScreenBrightness);
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 706b4a6..bf01f2d 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -131,13 +131,14 @@
*/
public void setAutoBrightnessState(int targetDisplayState,
boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
- float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
+ boolean useNormalBrightnessForDoze, float lastUserSetScreenBrightness,
+ boolean userSetBrightnessChanged) {
// We are still in the process of updating the power state, so there's no need to trigger
// an update again
- switchMode(targetDisplayState, /* sendUpdate= */ false);
+ switchMode(targetDisplayState, useNormalBrightnessForDoze, policy, /* sendUpdate= */ false);
- // If the policy is POLICY_DOZE and the display state is STATE_ON, auto-brightness should
- // only be enabled if the config allows it
+ // If the policy is POLICY_DOZE and the display state is not STATE_OFF, auto-brightness
+ // should only be enabled if the config allows it
final boolean autoBrightnessEnabledInDoze = allowAutoBrightnessWhileDozingConfig
&& policy == POLICY_DOZE && targetDisplayState != Display.STATE_OFF;
@@ -157,7 +158,8 @@
: AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
accommodateUserBrightnessChanges(userSetBrightnessChanged, lastUserSetScreenBrightness,
- policy, targetDisplayState, mBrightnessConfiguration, autoBrightnessState);
+ policy, targetDisplayState, useNormalBrightnessForDoze, mBrightnessConfiguration,
+ autoBrightnessState);
mIsConfigured = true;
}
@@ -344,6 +346,8 @@
strategySelectionNotifyRequest.getSelectedDisplayBrightnessStrategy()
.getReason(),
strategySelectionNotifyRequest.getDisplayPowerRequest().policy,
+ strategySelectionNotifyRequest.getDisplayPowerRequest()
+ .useNormalBrightnessForDoze,
strategySelectionNotifyRequest.getLastUserSetScreenBrightness(),
strategySelectionNotifyRequest.isUserSetBrightnessChanged());
}
@@ -469,7 +473,8 @@
@VisibleForTesting
void accommodateUserBrightnessChanges(boolean userSetBrightnessChanged,
float lastUserSetScreenBrightness, int policy, int displayState,
- BrightnessConfiguration brightnessConfiguration, int autoBrightnessState) {
+ boolean useNormalBrightnessForDoze, BrightnessConfiguration brightnessConfiguration,
+ int autoBrightnessState) {
// Update the pending auto-brightness adjustments if any. This typically checks and adjusts
// the state of the class if the user moves the brightness slider and has settled to a
// different value
@@ -485,8 +490,12 @@
mAutomaticBrightnessController.configure(autoBrightnessState,
brightnessConfiguration,
lastUserSetScreenBrightness,
- userSetBrightnessChanged, autoBrightnessAdjustment,
- mAutoBrightnessAdjustmentChanged, policy, displayState,
+ userSetBrightnessChanged,
+ autoBrightnessAdjustment,
+ mAutoBrightnessAdjustmentChanged,
+ policy,
+ displayState,
+ useNormalBrightnessForDoze,
mShouldResetShortTermModel);
mShouldResetShortTermModel = false;
// We take note if the user brightness point is still being used in the current
@@ -494,11 +503,18 @@
mIsShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints();
}
}
- private void switchMode(int state, boolean sendUpdate) {
+
+ private void switchMode(int state, boolean useNormalBrightnessForDoze, int policy,
+ boolean sendUpdate) {
if (mDisplayManagerFlags.areAutoBrightnessModesEnabled()
&& mAutomaticBrightnessController != null
&& !mAutomaticBrightnessController.isInIdleMode()) {
- mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
+
+ boolean shouldUseDozeMode =
+ mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()
+ ? !useNormalBrightnessForDoze && policy == POLICY_DOZE
+ : Display.isDozeState(state);
+ mAutomaticBrightnessController.switchMode(shouldUseDozeMode
? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT, sendUpdate);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
index c87872c..5e79565 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
@@ -108,9 +108,10 @@
*/
public void setAutoBrightnessState(int targetDisplayState,
boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
- float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
- // If the policy is POLICY_DOZE and the display state is STATE_ON, auto-brightness should
- // only be enabled if the config allows it
+ boolean useNormalBrightnessForDoze, float lastUserSetScreenBrightness,
+ boolean userSetBrightnessChanged) {
+ // If the policy is POLICY_DOZE and the display state is not STATE_OFF, auto-brightness
+ // should only be enabled if the config allows it
final boolean autoBrightnessEnabledInDoze = allowAutoBrightnessWhileDozingConfig
&& policy == POLICY_DOZE && targetDisplayState != Display.STATE_OFF;
@@ -120,7 +121,7 @@
&& brightnessReason != BrightnessReason.REASON_OVERRIDE
&& mAutomaticBrightnessController != null;
mAutoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness()
- && !((targetDisplayState == Display.STATE_ON && policy != POLICY_DOZE)
+ && !((targetDisplayState == Display.STATE_ON && policy != POLICY_DOZE)
|| autoBrightnessEnabledInDoze);
final int autoBrightnessState = mIsAutoBrightnessEnabled
&& brightnessReason != BrightnessReason.REASON_FOLLOWER
@@ -130,7 +131,8 @@
: AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
accommodateUserBrightnessChanges(userSetBrightnessChanged, lastUserSetScreenBrightness,
- policy, targetDisplayState, mBrightnessConfiguration, autoBrightnessState);
+ policy, targetDisplayState, useNormalBrightnessForDoze, mBrightnessConfiguration,
+ autoBrightnessState);
}
public boolean isAutoBrightnessEnabled() {
@@ -367,7 +369,8 @@
@VisibleForTesting
void accommodateUserBrightnessChanges(boolean userSetBrightnessChanged,
float lastUserSetScreenBrightness, int policy, int displayState,
- BrightnessConfiguration brightnessConfiguration, int autoBrightnessState) {
+ boolean useNormalBrightnessForDoze, BrightnessConfiguration brightnessConfiguration,
+ int autoBrightnessState) {
// Update the pending auto-brightness adjustments if any. This typically checks and adjusts
// the state of the class if the user moves the brightness slider and has settled to a
// different value
@@ -384,7 +387,10 @@
brightnessConfiguration,
lastUserSetScreenBrightness,
userSetBrightnessChanged, autoBrightnessAdjustment,
- mAutoBrightnessAdjustmentChanged, policy, displayState,
+ mAutoBrightnessAdjustmentChanged,
+ policy,
+ displayState,
+ useNormalBrightnessForDoze,
mShouldResetShortTermModel);
mShouldResetShortTermModel = false;
// We take note if the user brightness point is still being used in the current
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index e1934b0..f0f6db2 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -193,6 +193,11 @@
Flags.FLAG_NEW_HDR_BRIGHTNESS_MODIFIER,
Flags::newHdrBrightnessModifier);
+ private final FlagState mNormalBrightnessForDozeParameter = new FlagState(
+ Flags.FLAG_NORMAL_BRIGHTNESS_FOR_DOZE_PARAMETER,
+ Flags::normalBrightnessForDozeParameter
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -393,6 +398,13 @@
}
/**
+ * @return Whether the useDozeBrightness parameter should be used
+ */
+ public boolean isNormalBrightnessForDozeParameterEnabled() {
+ return mNormalBrightnessForDozeParameter.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -431,6 +443,7 @@
pw.println(" " + mOffloadDozeOverrideHoldsWakelock);
pw.println(" " + mOffloadSessionCancelBlockScreenOn);
pw.println(" " + mNewHdrBrightnessModifier);
+ pw.println(" " + mNormalBrightnessForDozeParameter);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index ac5f97f..d929249 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -250,6 +250,13 @@
namespace: "display_manager"
description: "Define doze brightness in the float scale [0, 1]."
bug: "343796384"
+}
+
+flag {
+ name: "normal_brightness_for_doze_parameter"
+ namespace: "wear_frameworks"
+ description: "Feature flag for the parameter specifying whether brightness should be adjusted while dozing."
+ bug: "343283838"
is_fixed_read_only: true
}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 5e471c8..31f5a41 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -1592,6 +1592,12 @@
- SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
SYNCHRONIZED_REFRESH_RATE_TARGET
+ SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE,
+ Vote.forRenderFrameRates(
+ SYNCHRONIZED_REFRESH_RATE_TARGET
+ - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
+ SYNCHRONIZED_REFRESH_RATE_TARGET
+ + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
}
private void removeDisplaysSynchronizedPeakRefreshRate(final int displayId) {
@@ -1603,11 +1609,12 @@
return;
}
mExternalDisplaysConnected.remove(displayId);
- if (mExternalDisplaysConnected.size() != 0) {
+ if (!mExternalDisplaysConnected.isEmpty()) {
return;
}
}
mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE, null);
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE, null);
}
private void updateDisplayDeviceConfig(int displayId) {
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index 7cbdd13..88ee04481 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -92,48 +92,55 @@
// render rate vote can still apply
int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 9;
- // Restrict all displays to 60Hz when external display is connected. It votes [59Hz, 61Hz].
+ // Restrict all displays physical refresh rate to 60Hz when external display is connected.
+ // It votes [59Hz, 61Hz].
int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 10;
+ // PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE has a higher priority than
+ // PRIORITY_SYNCHRONIZED_REFRESH_RATE and will limit render rate to [59Hz, 61Hz].
+ // In case physical refresh rate vote discarded (due to physical refresh rate not supported),
+ // render rate vote can still apply.
+ int PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE = 11;
+
// Restrict displays max available resolution and refresh rates. It votes [0, LIMIT]
- int PRIORITY_LIMIT_MODE = 11;
+ int PRIORITY_LIMIT_MODE = 12;
// To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
// rate to max value (same as for PRIORITY_UDFPS) on lock screen
- int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 12;
+ int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 13;
// For concurrent displays we want to limit refresh rate on all displays
- int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 13;
+ int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 14;
// For internal application to limit display modes to specific ids
- int PRIORITY_SYSTEM_REQUESTED_MODES = 14;
+ int PRIORITY_SYSTEM_REQUESTED_MODES = 15;
// PRIORITY_LOW_POWER_MODE_MODES limits display modes to specific refreshRate-vsync pairs if
// Settings.Global.LOW_POWER_MODE is on.
// Lower priority that PRIORITY_LOW_POWER_MODE_RENDER_RATE and if discarded (due to other
// higher priority votes), render rate limit can still apply
- int PRIORITY_LOW_POWER_MODE_MODES = 15;
+ int PRIORITY_LOW_POWER_MODE_MODES = 16;
// PRIORITY_LOW_POWER_MODE_RENDER_RATE force the render frame rate to [0, 60HZ] if
// Settings.Global.LOW_POWER_MODE is on.
- int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 16;
+ int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 17;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
- int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 17;
+ int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 18;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
- int PRIORITY_SKIN_TEMPERATURE = 18;
+ int PRIORITY_SKIN_TEMPERATURE = 19;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
- int PRIORITY_PROXIMITY = 19;
+ int PRIORITY_PROXIMITY = 20;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
- int PRIORITY_UDFPS = 20;
+ int PRIORITY_UDFPS = 21;
@IntDef(prefix = { "PRIORITY_" }, value = {
PRIORITY_DEFAULT_RENDER_FRAME_RATE,
@@ -147,6 +154,7 @@
PRIORITY_USER_SETTING_PEAK_REFRESH_RATE,
PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
PRIORITY_SYNCHRONIZED_REFRESH_RATE,
+ PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE,
PRIORITY_LIMIT_MODE,
PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE,
PRIORITY_LAYOUT_LIMITED_FRAME_RATE,
@@ -267,6 +275,8 @@
return "PRIORITY_LIMIT_MODE";
case PRIORITY_SYNCHRONIZED_REFRESH_RATE:
return "PRIORITY_SYNCHRONIZED_REFRESH_RATE";
+ case PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE:
+ return "PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE";
case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index a3b77e8..19305de 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -543,12 +543,14 @@
}
private void startDozingInternal(IBinder token, int screenState,
- @Display.StateReason int reason, float screenBrightnessFloat, int screenBrightnessInt) {
+ @Display.StateReason int reason, float screenBrightnessFloat, int screenBrightnessInt,
+ boolean useNormalBrightnessForDoze) {
Slog.d(TAG, "Dream requested to start dozing: " + token
+ ", screenState=" + Display.stateToString(screenState)
+ ", reason=" + Display.stateReasonToString(reason)
+ ", screenBrightnessFloat=" + screenBrightnessFloat
- + ", screenBrightnessInt=" + screenBrightnessInt);
+ + ", screenBrightnessInt=" + screenBrightnessInt
+ + ", useNormalBrightnessForDoze=" + useNormalBrightnessForDoze);
synchronized (mLock) {
if (mCurrentDream != null && mCurrentDream.token == token && mCurrentDream.canDoze) {
@@ -556,7 +558,8 @@
mCurrentDream.dozeScreenBrightness = screenBrightnessInt;
mCurrentDream.dozeScreenBrightnessFloat = screenBrightnessFloat;
mPowerManagerInternal.setDozeOverrideFromDreamManager(
- screenState, reason, screenBrightnessFloat, screenBrightnessInt);
+ screenState, reason, screenBrightnessFloat, screenBrightnessInt,
+ useNormalBrightnessForDoze);
if (!mCurrentDream.isDozing) {
mCurrentDream.isDozing = true;
mDozeWakeLock.acquire();
@@ -578,7 +581,8 @@
Display.STATE_UNKNOWN,
Display.STATE_REASON_DREAM_MANAGER,
PowerManager.BRIGHTNESS_INVALID_FLOAT,
- PowerManager.BRIGHTNESS_DEFAULT);
+ PowerManager.BRIGHTNESS_DEFAULT,
+ /* useNormalBrightnessForDoze= */ false);
}
}
}
@@ -1098,7 +1102,8 @@
@Override // Binder call
public void startDozing(
IBinder token, int screenState, @Display.StateReason int reason,
- float screenBrightnessFloat, int screeBrightnessInt) {
+ float screenBrightnessFloat, int screeBrightnessInt,
+ boolean useNormalBrightnessForDoze) {
// Requires no permission, called by Dream from an arbitrary process.
if (token == null) {
throw new IllegalArgumentException("token must not be null");
@@ -1107,7 +1112,7 @@
final long ident = Binder.clearCallingIdentity();
try {
startDozingInternal(token, screenState, reason, screenBrightnessFloat,
- screeBrightnessInt);
+ screeBrightnessInt, useNormalBrightnessForDoze);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1116,7 +1121,8 @@
@Override // Binder call
public void startDozingOneway(
IBinder token, int screenState, @Display.StateReason int reason,
- float screenBrightnessFloat, int screeBrightnessInt) {
+ float screenBrightnessFloat, int screeBrightnessInt,
+ boolean useNormalBrightnessForDoze) {
// Requires no permission, called by Dream from an arbitrary process.
if (token == null) {
throw new IllegalArgumentException("token must not be null");
@@ -1125,7 +1131,7 @@
final long ident = Binder.clearCallingIdentity();
try {
startDozingInternal(token, screenState, reason, screenBrightnessFloat,
- screeBrightnessInt);
+ screeBrightnessInt, useNormalBrightnessForDoze);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 819b9a1..73f18d1 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -21,7 +21,7 @@
import android.annotation.UserIdInt;
import android.graphics.PointF;
import android.hardware.display.DisplayViewport;
-import android.hardware.input.KeyboardSystemShortcut;
+import android.hardware.input.KeyGestureEvent;
import android.os.IBinder;
import android.view.InputChannel;
import android.view.inputmethod.InputMethodSubtype;
@@ -230,18 +230,14 @@
public abstract int getLastUsedInputDeviceId();
/**
- * Notify Keyboard system shortcut was triggered by the user and handled by the framework.
+ * Notify key gesture was completed by the user.
*
- * NOTE: This is just to notify that a system shortcut was triggered. No further action is
- * required to execute the said shortcut. This callback is meant for purposes of providing user
- * hints or logging, etc.
- *
- * @param deviceId the device ID of the keyboard using which the shortcut was triggered
- * @param keycodes the keys pressed for triggering the shortcut
- * @param modifierState the modifier state of the key event that triggered the shortcut
- * @param shortcut the shortcut that was triggered
+ * @param deviceId the device ID of the keyboard using which the event was completed
+ * @param keycodes the keys pressed for the event
+ * @param modifierState the modifier state
+ * @param event the gesture event that was completed
*
*/
- public abstract void notifyKeyboardShortcutTriggered(int deviceId, int[] keycodes,
- int modifierState, @KeyboardSystemShortcut.SystemShortcut int shortcut);
+ public abstract void notifyKeyGestureCompleted(int deviceId, int[] keycodes, int modifierState,
+ @KeyGestureEvent.KeyGestureType int event);
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index a69c7ef..a8fc862 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -48,18 +48,18 @@
import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.IInputManager;
import android.hardware.input.IInputSensorEventListener;
+import android.hardware.input.IKeyGestureEventListener;
import android.hardware.input.IKeyboardBacklightListener;
-import android.hardware.input.IKeyboardSystemShortcutListener;
import android.hardware.input.IStickyModifierStateListener;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputSensorInfo;
import android.hardware.input.InputSettings;
+import android.hardware.input.KeyGestureEvent;
import android.hardware.input.KeyGlyphMap;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.KeyboardLayoutSelectionResult;
-import android.hardware.input.KeyboardSystemShortcut;
import android.hardware.input.TouchCalibration;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
@@ -162,7 +162,7 @@
private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
private static final int MSG_RELOAD_DEVICE_ALIASES = 2;
private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 3;
- private static final int MSG_KEYBOARD_SYSTEM_SHORTCUT_TRIGGERED = 4;
+ private static final int MSG_KEY_GESTURE_COMPLETED = 4;
private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
private static final AdditionalDisplayInputProperties
@@ -314,9 +314,7 @@
// Manages Sticky modifier state
private final StickyModifierStateController mStickyModifierStateController;
-
- // Manages keyboard system shortcut callbacks
- private final KeyboardShortcutCallbackHandler mKeyboardShortcutCallbackHandler;
+ private final KeyGestureController mKeyGestureController;
// Manages Keyboard microphone mute led
private final KeyboardLedController mKeyboardLedController;
@@ -476,7 +474,7 @@
injector.getLooper(), injector.getUEventManager())
: new KeyboardBacklightControllerInterface() {};
mStickyModifierStateController = new StickyModifierStateController();
- mKeyboardShortcutCallbackHandler = new KeyboardShortcutCallbackHandler();
+ mKeyGestureController = new KeyGestureController();
mKeyboardLedController = new KeyboardLedController(mContext, injector.getLooper(),
mNative);
mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
@@ -2723,33 +2721,32 @@
}
@Override
- @EnforcePermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
- public void registerKeyboardSystemShortcutListener(
- @NonNull IKeyboardSystemShortcutListener listener) {
- super.registerKeyboardSystemShortcutListener_enforcePermission();
+ @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
+ public void registerKeyGestureEventListener(
+ @NonNull IKeyGestureEventListener listener) {
+ super.registerKeyGestureEventListener_enforcePermission();
Objects.requireNonNull(listener);
- mKeyboardShortcutCallbackHandler.registerKeyboardSystemShortcutListener(listener,
+ mKeyGestureController.registerKeyGestureEventListener(listener,
Binder.getCallingPid());
}
@Override
- @EnforcePermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
- public void unregisterKeyboardSystemShortcutListener(
- @NonNull IKeyboardSystemShortcutListener listener) {
- super.unregisterKeyboardSystemShortcutListener_enforcePermission();
+ @EnforcePermission(Manifest.permission.MANAGE_KEY_GESTURES)
+ public void unregisterKeyGestureEventListener(
+ @NonNull IKeyGestureEventListener listener) {
+ super.unregisterKeyGestureEventListener_enforcePermission();
Objects.requireNonNull(listener);
- mKeyboardShortcutCallbackHandler.unregisterKeyboardSystemShortcutListener(listener,
+ mKeyGestureController.unregisterKeyGestureEventListener(listener,
Binder.getCallingPid());
}
- private void handleKeyboardSystemShortcutTriggered(int deviceId,
- KeyboardSystemShortcut shortcut) {
- InputDevice device = getInputDevice(deviceId);
- if (device == null || device.isVirtual() || !device.isFullKeyboard()) {
+ private void handleKeyGestureCompleted(KeyGestureEvent event) {
+ InputDevice device = getInputDevice(event.getDeviceId());
+ if (device == null || device.isVirtual()) {
return;
}
- KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(device, shortcut);
- mKeyboardShortcutCallbackHandler.onKeyboardSystemShortcutTriggered(deviceId, shortcut);
+ KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(device, event);
+ mKeyGestureController.onKeyGestureEvent(event);
}
/**
@@ -2920,10 +2917,9 @@
boolean inTabletMode = (boolean) args.arg1;
deliverTabletModeChanged(whenNanos, inTabletMode);
break;
- case MSG_KEYBOARD_SYSTEM_SHORTCUT_TRIGGERED:
- int deviceId = msg.arg1;
- KeyboardSystemShortcut shortcut = (KeyboardSystemShortcut) msg.obj;
- handleKeyboardSystemShortcutTriggered(deviceId, shortcut);
+ case MSG_KEY_GESTURE_COMPLETED:
+ KeyGestureEvent event = (KeyGestureEvent) msg.obj;
+ handleKeyGestureCompleted(event);
}
}
}
@@ -3251,10 +3247,11 @@
}
@Override
- public void notifyKeyboardShortcutTriggered(int deviceId, int[] keycodes, int modifierState,
- @KeyboardSystemShortcut.SystemShortcut int shortcut) {
- mHandler.obtainMessage(MSG_KEYBOARD_SYSTEM_SHORTCUT_TRIGGERED, deviceId, 0,
- new KeyboardSystemShortcut(keycodes, modifierState, shortcut)).sendToTarget();
+ public void notifyKeyGestureCompleted(int deviceId, int[] keycodes, int modifierState,
+ @KeyGestureEvent.KeyGestureType int gestureType) {
+ mHandler.obtainMessage(MSG_KEY_GESTURE_COMPLETED,
+ new KeyGestureEvent(deviceId, keycodes, modifierState,
+ gestureType)).sendToTarget();
}
}
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
new file mode 100644
index 0000000..674d3c4
--- /dev/null
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input;
+
+import android.annotation.BinderThread;
+import android.hardware.input.IKeyGestureEventListener;
+import android.hardware.input.KeyGestureEvent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * A thread-safe component of {@link InputManagerService} responsible for managing callbacks when a
+ * key gesture event occurs.
+ */
+final class KeyGestureController {
+
+ private static final String TAG = "KeyGestureController";
+
+ // To enable these logs, run:
+ // 'adb shell setprop log.tag.KeyGestureController DEBUG' (requires restart)
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ // List of currently registered key gesture event listeners keyed by process pid
+ @GuardedBy("mKeyGestureEventListenerRecords")
+ private final SparseArray<KeyGestureEventListenerRecord>
+ mKeyGestureEventListenerRecords = new SparseArray<>();
+
+ public void onKeyGestureEvent(KeyGestureEvent event) {
+ if (DEBUG) {
+ Slog.d(TAG, "Key gesture event occurred, event = " + event);
+ }
+
+ synchronized (mKeyGestureEventListenerRecords) {
+ for (int i = 0; i < mKeyGestureEventListenerRecords.size(); i++) {
+ mKeyGestureEventListenerRecords.valueAt(i).onKeyGestureEvent(event);
+ }
+ }
+ }
+
+ /** Register the key gesture event listener for a process. */
+ @BinderThread
+ public void registerKeyGestureEventListener(IKeyGestureEventListener listener,
+ int pid) {
+ synchronized (mKeyGestureEventListenerRecords) {
+ if (mKeyGestureEventListenerRecords.get(pid) != null) {
+ throw new IllegalStateException("The calling process has already registered "
+ + "a KeyGestureEventListener.");
+ }
+ KeyGestureEventListenerRecord record = new KeyGestureEventListenerRecord(
+ pid, listener);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ mKeyGestureEventListenerRecords.put(pid, record);
+ }
+ }
+
+ /** Unregister the key gesture event listener for a process. */
+ @BinderThread
+ public void unregisterKeyGestureEventListener(IKeyGestureEventListener listener,
+ int pid) {
+ synchronized (mKeyGestureEventListenerRecords) {
+ KeyGestureEventListenerRecord record =
+ mKeyGestureEventListenerRecords.get(pid);
+ if (record == null) {
+ throw new IllegalStateException("The calling process has no registered "
+ + "KeyGestureEventListener.");
+ }
+ if (record.mListener.asBinder() != listener.asBinder()) {
+ throw new IllegalStateException("The calling process has a different registered "
+ + "KeyGestureEventListener.");
+ }
+ record.mListener.asBinder().unlinkToDeath(record, 0);
+ mKeyGestureEventListenerRecords.remove(pid);
+ }
+ }
+
+ private void onKeyGestureEventListenerDied(int pid) {
+ synchronized (mKeyGestureEventListenerRecords) {
+ mKeyGestureEventListenerRecords.remove(pid);
+ }
+ }
+
+ // A record of a registered key gesture event listener from one process.
+ private class KeyGestureEventListenerRecord implements IBinder.DeathRecipient {
+ public final int mPid;
+ public final IKeyGestureEventListener mListener;
+
+ KeyGestureEventListenerRecord(int pid, IKeyGestureEventListener listener) {
+ mPid = pid;
+ mListener = listener;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Key gesture event listener for pid " + mPid + " died.");
+ }
+ onKeyGestureEventListenerDied(mPid);
+ }
+
+ public void onKeyGestureEvent(KeyGestureEvent event) {
+ try {
+ mListener.onKeyGestureEvent(event.getDeviceId(), event.getKeycodes(),
+ event.getModifierState(), event.getKeyGestureType());
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid
+ + " that key gesture event occurred, assuming it died.", ex);
+ binderDied();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index 3d2f951..1daf4db 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -24,9 +24,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.input.KeyGestureEvent;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria;
-import android.hardware.input.KeyboardSystemShortcut;
import android.icu.util.ULocale;
import android.text.TextUtils;
import android.util.Log;
@@ -66,14 +66,17 @@
* defined in "stats/atoms/input/input_extension_atoms.proto"
*/
public static void logKeyboardSystemsEventReportedAtom(@NonNull InputDevice inputDevice,
- @NonNull KeyboardSystemShortcut keyboardSystemShortcut) {
+ @NonNull KeyGestureEvent keyGestureEvent) {
+ if (inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
+ return;
+ }
FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
inputDevice.getVendorId(), inputDevice.getProductId(),
- keyboardSystemShortcut.getSystemShortcut(), keyboardSystemShortcut.getKeycodes(),
- keyboardSystemShortcut.getModifierState(), inputDevice.getDeviceBus());
+ keyGestureEvent.getKeyGestureType(), keyGestureEvent.getKeycodes(),
+ keyGestureEvent.getModifierState(), inputDevice.getDeviceBus());
if (DEBUG) {
- Slog.d(TAG, "Logging Keyboard system event: " + keyboardSystemShortcut);
+ Slog.d(TAG, "Logging Keyboard system event: " + keyGestureEvent);
}
}
diff --git a/services/core/java/com/android/server/input/KeyboardShortcutCallbackHandler.java b/services/core/java/com/android/server/input/KeyboardShortcutCallbackHandler.java
deleted file mode 100644
index 092058e..0000000
--- a/services/core/java/com/android/server/input/KeyboardShortcutCallbackHandler.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.input;
-
-import android.annotation.BinderThread;
-import android.hardware.input.IKeyboardSystemShortcutListener;
-import android.hardware.input.KeyboardSystemShortcut;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-
-/**
- * A thread-safe component of {@link InputManagerService} responsible for managing callbacks when a
- * keyboard shortcut is triggered.
- */
-final class KeyboardShortcutCallbackHandler {
-
- private static final String TAG = "KeyboardShortcut";
-
- // To enable these logs, run:
- // 'adb shell setprop log.tag.KeyboardShortcutCallbackHandler DEBUG' (requires restart)
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- // List of currently registered keyboard system shortcut listeners keyed by process pid
- @GuardedBy("mKeyboardSystemShortcutListenerRecords")
- private final SparseArray<KeyboardSystemShortcutListenerRecord>
- mKeyboardSystemShortcutListenerRecords = new SparseArray<>();
-
- public void onKeyboardSystemShortcutTriggered(int deviceId,
- KeyboardSystemShortcut systemShortcut) {
- if (DEBUG) {
- Slog.d(TAG, "Keyboard system shortcut triggered, deviceId = " + deviceId
- + ", systemShortcut = " + systemShortcut);
- }
-
- synchronized (mKeyboardSystemShortcutListenerRecords) {
- for (int i = 0; i < mKeyboardSystemShortcutListenerRecords.size(); i++) {
- mKeyboardSystemShortcutListenerRecords.valueAt(i).onKeyboardSystemShortcutTriggered(
- deviceId, systemShortcut);
- }
- }
- }
-
- /** Register the keyboard system shortcut listener for a process. */
- @BinderThread
- public void registerKeyboardSystemShortcutListener(IKeyboardSystemShortcutListener listener,
- int pid) {
- synchronized (mKeyboardSystemShortcutListenerRecords) {
- if (mKeyboardSystemShortcutListenerRecords.get(pid) != null) {
- throw new IllegalStateException("The calling process has already registered "
- + "a KeyboardSystemShortcutListener.");
- }
- KeyboardSystemShortcutListenerRecord record = new KeyboardSystemShortcutListenerRecord(
- pid, listener);
- try {
- listener.asBinder().linkToDeath(record, 0);
- } catch (RemoteException ex) {
- throw new RuntimeException(ex);
- }
- mKeyboardSystemShortcutListenerRecords.put(pid, record);
- }
- }
-
- /** Unregister the keyboard system shortcut listener for a process. */
- @BinderThread
- public void unregisterKeyboardSystemShortcutListener(IKeyboardSystemShortcutListener listener,
- int pid) {
- synchronized (mKeyboardSystemShortcutListenerRecords) {
- KeyboardSystemShortcutListenerRecord record =
- mKeyboardSystemShortcutListenerRecords.get(pid);
- if (record == null) {
- throw new IllegalStateException("The calling process has no registered "
- + "KeyboardSystemShortcutListener.");
- }
- if (record.mListener.asBinder() != listener.asBinder()) {
- throw new IllegalStateException("The calling process has a different registered "
- + "KeyboardSystemShortcutListener.");
- }
- record.mListener.asBinder().unlinkToDeath(record, 0);
- mKeyboardSystemShortcutListenerRecords.remove(pid);
- }
- }
-
- private void onKeyboardSystemShortcutListenerDied(int pid) {
- synchronized (mKeyboardSystemShortcutListenerRecords) {
- mKeyboardSystemShortcutListenerRecords.remove(pid);
- }
- }
-
- // A record of a registered keyboard system shortcut listener from one process.
- private class KeyboardSystemShortcutListenerRecord implements IBinder.DeathRecipient {
- public final int mPid;
- public final IKeyboardSystemShortcutListener mListener;
-
- KeyboardSystemShortcutListenerRecord(int pid, IKeyboardSystemShortcutListener listener) {
- mPid = pid;
- mListener = listener;
- }
-
- @Override
- public void binderDied() {
- if (DEBUG) {
- Slog.d(TAG, "Keyboard system shortcut listener for pid " + mPid + " died.");
- }
- onKeyboardSystemShortcutListenerDied(mPid);
- }
-
- public void onKeyboardSystemShortcutTriggered(int deviceId, KeyboardSystemShortcut data) {
- try {
- mListener.onKeyboardSystemShortcutTriggered(deviceId, data.getKeycodes(),
- data.getModifierState(), data.getSystemShortcut());
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify process " + mPid
- + " that keyboard system shortcut was triggered, assuming it died.", ex);
- binderDied();
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index 9818916..abb2132 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -489,6 +489,7 @@
} else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
hasValidSound = false;
+ hasValidVibrate = false;
}
}
}
@@ -753,6 +754,13 @@
// notifying app does not have the VIBRATE permission.
final long identity = Binder.clearCallingIdentity();
try {
+ // Need to explicitly cancel a previously playing vibration
+ // Otherwise a looping vibration will not be stopped when starting a new one.
+ if (mVibrateNotificationKey != null
+ && !mVibrateNotificationKey.equals(record.getKey())) {
+ mVibrateNotificationKey = null;
+ mVibratorHelper.cancelVibration();
+ }
final float scale = getVibrationIntensity(record);
final VibrationEffect scaledEffect = Float.compare(scale, DEFAULT_VOLUME) != 0
? mVibratorHelper.scale(effect, scale) : effect;
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 5a45186..7ed8972 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -29,8 +29,7 @@
import android.content.pm.PackageManager;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
-import android.hardware.input.InputManager;
-import android.hardware.input.KeyboardSystemShortcut;
+import android.hardware.input.KeyGestureEvent;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -476,7 +475,7 @@
+ "keyCode=" + KeyEvent.keyCodeToString(keyCode) + ","
+ " category=" + category + " role=" + role);
}
- notifyKeyboardShortcutTriggered(keyEvent, getSystemShortcutFromIntent(intent));
+ notifyKeyGestureCompleted(keyEvent, getKeyGestureTypeFromIntent(intent));
return true;
} else {
return false;
@@ -497,19 +496,19 @@
+ "the activity to which it is registered was not found: "
+ "META+ or SEARCH" + KeyEvent.keyCodeToString(keyCode));
}
- notifyKeyboardShortcutTriggered(keyEvent, getSystemShortcutFromIntent(shortcutIntent));
+ notifyKeyGestureCompleted(keyEvent, getKeyGestureTypeFromIntent(shortcutIntent));
return true;
}
return false;
}
- private void notifyKeyboardShortcutTriggered(KeyEvent event,
- @KeyboardSystemShortcut.SystemShortcut int systemShortcut) {
- if (systemShortcut == KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED) {
+ private void notifyKeyGestureCompleted(KeyEvent event,
+ @KeyGestureEvent.KeyGestureType int gestureType) {
+ if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
return;
}
- mInputManagerInternal.notifyKeyboardShortcutTriggered(event.getDeviceId(),
- new int[]{event.getKeyCode()}, event.getMetaState(), systemShortcut);
+ mInputManagerInternal.notifyKeyGestureCompleted(event.getDeviceId(),
+ new int[]{event.getKeyCode()}, event.getMetaState(), gestureType);
}
/**
@@ -710,21 +709,21 @@
/**
- * Find Keyboard shortcut event corresponding to intent filter category. Returns
- * {@code SYSTEM_SHORTCUT_UNSPECIFIED if no matching event found}
+ * Find Key gesture type corresponding to intent filter category. Returns
+ * {@code KEY_GESTURE_TYPE_UNSPECIFIED if no matching event found}
*/
- @KeyboardSystemShortcut.SystemShortcut
- private static int getSystemShortcutFromIntent(Intent intent) {
+ @KeyGestureEvent.KeyGestureType
+ private static int getKeyGestureTypeFromIntent(Intent intent) {
Intent selectorIntent = intent.getSelector();
if (selectorIntent != null) {
Set<String> selectorCategories = selectorIntent.getCategories();
if (selectorCategories != null && !selectorCategories.isEmpty()) {
for (String intentCategory : selectorCategories) {
- int systemShortcut = getEventFromSelectorCategory(intentCategory);
- if (systemShortcut == KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED) {
+ int keyGestureType = getKeyGestureTypeFromSelectorCategory(intentCategory);
+ if (keyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
continue;
}
- return systemShortcut;
+ return keyGestureType;
}
}
}
@@ -733,69 +732,68 @@
// so check for that.
String role = intent.getStringExtra(ModifierShortcutManager.EXTRA_ROLE);
if (!TextUtils.isEmpty(role)) {
- return getLogEventFromRole(role);
+ return getKeyGestureTypeFromRole(role);
}
Set<String> intentCategories = intent.getCategories();
if (intentCategories == null || intentCategories.isEmpty()
|| !intentCategories.contains(Intent.CATEGORY_LAUNCHER)) {
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
}
if (intent.getComponent() == null) {
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
}
// TODO(b/280423320): Add new field package name associated in the
// KeyboardShortcutEvent atom and log it accordingly.
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME;
}
- @KeyboardSystemShortcut.SystemShortcut
- private static int getEventFromSelectorCategory(String category) {
+ @KeyGestureEvent.KeyGestureType
+ private static int getKeyGestureTypeFromSelectorCategory(String category) {
switch (category) {
case Intent.CATEGORY_APP_BROWSER:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER;
case Intent.CATEGORY_APP_EMAIL:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL;
case Intent.CATEGORY_APP_CONTACTS:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS;
case Intent.CATEGORY_APP_CALENDAR:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR;
case Intent.CATEGORY_APP_CALCULATOR:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR;
case Intent.CATEGORY_APP_MUSIC:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC;
case Intent.CATEGORY_APP_MAPS:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS;
case Intent.CATEGORY_APP_MESSAGING:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING;
case Intent.CATEGORY_APP_GALLERY:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY;
case Intent.CATEGORY_APP_FILES:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES;
case Intent.CATEGORY_APP_WEATHER:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER;
case Intent.CATEGORY_APP_FITNESS:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS;
default:
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
}
}
/**
- * Find KeyboardLogEvent corresponding to the provide system role name.
- * Returns {@code null} if no matching event found.
+ * Find KeyGestureType corresponding to the provide system role name.
+ * Returns {@code KEY_GESTURE_TYPE_UNSPECIFIED} if no matching event found.
*/
- @KeyboardSystemShortcut.SystemShortcut
- private static int getLogEventFromRole(String role) {
+ @KeyGestureEvent.KeyGestureType
+ private static int getKeyGestureTypeFromRole(String role) {
if (RoleManager.ROLE_BROWSER.equals(role)) {
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER;
} else if (RoleManager.ROLE_SMS.equals(role)) {
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING;
} else {
- Log.w(TAG, "Keyboard shortcut to launch "
- + role + " not supported for logging");
- return KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED;
+ Log.w(TAG, "Keyboard gesture event to launch " + role + " not supported for logging");
+ return KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 720c1c2..aa56e8d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -139,7 +139,7 @@
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.hardware.input.InputManager;
-import android.hardware.input.KeyboardSystemShortcut;
+import android.hardware.input.KeyGestureEvent;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioSystem;
@@ -1819,7 +1819,7 @@
}
private void handleShortPressOnHome(KeyEvent event) {
- notifyKeyboardShortcutTriggered(event, KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME);
+ notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
// Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
final HdmiControl hdmiControl = getHdmiControl();
@@ -2053,8 +2053,8 @@
}
switch (mDoubleTapOnHomeBehavior) {
case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH);
mHomeConsumed = true;
toggleRecentApps();
break;
@@ -2082,23 +2082,23 @@
case LONG_PRESS_HOME_ALL_APPS:
if (mHasFeatureLeanback) {
launchAllAppsAction();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_ALL_APPS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
} else {
launchAllAppsViaA11y();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
}
break;
case LONG_PRESS_HOME_ASSIST:
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
launchAssistAction(null, event.getDeviceId(), event.getEventTime(),
AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
break;
case LONG_PRESS_HOME_NOTIFICATION_PANEL:
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
toggleNotificationPanel();
break;
default:
@@ -3285,29 +3285,29 @@
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
};
- private void notifyKeyboardShortcutTriggeredOnActionUp(KeyEvent event,
- @KeyboardSystemShortcut.SystemShortcut int systemShortcut) {
+ private void notifyKeyGestureCompletedOnActionUp(KeyEvent event,
+ @KeyGestureEvent.KeyGestureType int gestureType) {
if (event.getAction() != KeyEvent.ACTION_UP) {
return;
}
- notifyKeyboardShortcutTriggered(event, systemShortcut);
+ notifyKeyGestureCompleted(event, gestureType);
}
- private void notifyKeyboardShortcutTriggeredOnActionDown(KeyEvent event,
- @KeyboardSystemShortcut.SystemShortcut int systemShortcut) {
+ private void notifyKeyGestureCompletedOnActionDown(KeyEvent event,
+ @KeyGestureEvent.KeyGestureType int gestureType) {
if (event.getAction() != KeyEvent.ACTION_DOWN) {
return;
}
- notifyKeyboardShortcutTriggered(event, systemShortcut);
+ notifyKeyGestureCompleted(event, gestureType);
}
- private void notifyKeyboardShortcutTriggered(KeyEvent event,
- @KeyboardSystemShortcut.SystemShortcut int systemShortcut) {
- if (systemShortcut == KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED) {
+ private void notifyKeyGestureCompleted(KeyEvent event,
+ @KeyGestureEvent.KeyGestureType int gestureType) {
+ if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
return;
}
- mInputManagerInternal.notifyKeyboardShortcutTriggered(event.getDeviceId(),
- new int[]{event.getKeyCode()}, event.getMetaState(), systemShortcut);
+ mInputManagerInternal.notifyKeyGestureCompleted(event.getDeviceId(),
+ new int[]{event.getKeyCode()}, event.getMetaState(), gestureType);
}
@Override
@@ -3417,8 +3417,8 @@
case KeyEvent.KEYCODE_RECENT_APPS:
if (firstDown) {
showRecentApps(false /* triggeredFromAltTab */);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS);
}
return true;
case KeyEvent.KEYCODE_APP_SWITCH:
@@ -3427,8 +3427,8 @@
preloadRecentApps();
} else if (!down) {
toggleRecentApps();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH);
}
}
return true;
@@ -3437,8 +3437,8 @@
launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
deviceId, event.getEventTime(),
AssistUtils.INVOCATION_TYPE_UNKNOWN);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
return true;
}
break;
@@ -3451,16 +3451,16 @@
case KeyEvent.KEYCODE_I:
if (firstDown && event.isMetaPressed()) {
showSystemSettings();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
return true;
}
break;
case KeyEvent.KEYCODE_L:
if (firstDown && event.isMetaPressed()) {
lockNow(null /* options */);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LOCK_SCREEN);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN);
return true;
}
break;
@@ -3468,12 +3468,12 @@
if (firstDown && event.isMetaPressed()) {
if (event.isCtrlPressed()) {
sendSystemKeyToStatusBarAsync(event);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_OPEN_NOTES);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES);
} else {
toggleNotificationPanel();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
}
return true;
}
@@ -3481,8 +3481,8 @@
case KeyEvent.KEYCODE_S:
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TAKE_SCREENSHOT);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT);
return true;
}
break;
@@ -3495,16 +3495,16 @@
} catch (RemoteException e) {
Slog.d(TAG, "Error taking bugreport", e);
}
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT);
return true;
}
}
// fall through
case KeyEvent.KEYCODE_ESCAPE:
if (firstDown && event.isMetaPressed()) {
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
injectBackGesture(event.getDownTime());
return true;
}
@@ -3513,8 +3513,8 @@
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.moveFocusedTaskToFullscreen(getTargetDisplayIdForKeyEvent(event));
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION);
return true;
}
}
@@ -3524,8 +3524,8 @@
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.moveFocusedTaskToDesktop(getTargetDisplayIdForKeyEvent(event));
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_DESKTOP_MODE);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE);
return true;
}
}
@@ -3535,15 +3535,15 @@
if (event.isCtrlPressed()) {
moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
true /* leftOrTop */);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION);
} else if (event.isAltPressed()) {
setSplitscreenFocus(true /* leftOrTop */);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS);
} else {
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
injectBackGesture(event.getDownTime());
}
return true;
@@ -3554,13 +3554,13 @@
if (event.isCtrlPressed()) {
moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
false /* leftOrTop */);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION);
return true;
} else if (event.isAltPressed()) {
setSplitscreenFocus(false /* leftOrTop */);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS);
return true;
}
}
@@ -3568,8 +3568,8 @@
case KeyEvent.KEYCODE_SLASH:
if (firstDown && event.isMetaPressed() && !keyguardOn) {
toggleKeyboardShortcutsMenu(event.getDeviceId());
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER);
return true;
}
break;
@@ -3622,31 +3622,31 @@
intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
- int systemShortcut = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_DOWN
- ? KeyboardSystemShortcut.SYSTEM_SHORTCUT_BRIGHTNESS_DOWN
- : KeyboardSystemShortcut.SYSTEM_SHORTCUT_BRIGHTNESS_UP;
- notifyKeyboardShortcutTriggered(event, systemShortcut);
+ int gestureType = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_DOWN
+ ? KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN
+ : KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP;
+ notifyKeyGestureCompleted(event, gestureType);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
if (down) {
mInputManagerInternal.decrementKeyboardBacklight(event.getDeviceId());
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
if (down) {
mInputManagerInternal.incrementKeyboardBacklight(event.getDeviceId());
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
// TODO: Add logic
if (!down) {
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE);
}
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
@@ -3673,8 +3673,8 @@
if (firstDown && !keyguardOn && isUserSetupComplete()) {
if (event.isMetaPressed()) {
showRecentApps(false);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS);
return true;
} else if (mRecentAppsHeldModifiers == 0) {
final int shiftlessModifiers =
@@ -3683,8 +3683,8 @@
shiftlessModifiers, KeyEvent.META_ALT_ON)) {
mRecentAppsHeldModifiers = shiftlessModifiers;
showRecentApps(true);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS);
return true;
}
}
@@ -3697,20 +3697,20 @@
Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
msg.setAsynchronous(true);
msg.sendToTarget();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_ALL_APPS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
} else {
launchAllAppsViaA11y();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
}
}
return true;
case KeyEvent.KEYCODE_NOTIFICATION:
if (!down) {
toggleNotificationPanel();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
}
return true;
case KeyEvent.KEYCODE_SEARCH:
@@ -3718,8 +3718,8 @@
switch (mSearchKeyBehavior) {
case SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY: {
launchTargetSearchActivity();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_SEARCH);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH);
return true;
}
case SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH:
@@ -3732,8 +3732,8 @@
if (firstDown) {
int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
sendSwitchKeyboardLayout(event, focusedToken, direction);
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LANGUAGE_SWITCH);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH);
return true;
}
break;
@@ -3752,13 +3752,13 @@
if (mPendingCapsLockToggle) {
mInputManagerInternal.toggleCapsLock(event.getDeviceId());
mPendingCapsLockToggle = false;
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
} else if (mPendingMetaAction) {
if (!canceled) {
launchAllAppsViaA11y();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
}
mPendingMetaAction = false;
}
@@ -3786,16 +3786,16 @@
if (mPendingCapsLockToggle) {
mInputManagerInternal.toggleCapsLock(event.getDeviceId());
mPendingCapsLockToggle = false;
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
return true;
}
}
break;
case KeyEvent.KEYCODE_CAPS_LOCK:
if (!down) {
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
}
break;
case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
@@ -3809,12 +3809,12 @@
if (firstDown) {
if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL) {
toggleNotificationPanel();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
} else if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY) {
showSystemSettings();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
}
}
return true;
@@ -4760,8 +4760,8 @@
// Handle special keys.
switch (keyCode) {
case KeyEvent.KEYCODE_BACK: {
- notifyKeyboardShortcutTriggeredOnActionUp(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK);
+ notifyKeyGestureCompletedOnActionUp(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
if (down) {
// There may have other embedded activities on the same Task. Try to move the
// focus before processing the back event.
@@ -4782,12 +4782,12 @@
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
- int systemShortcut = keyCode == KEYCODE_VOLUME_DOWN
- ? KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_DOWN
+ int gestureType = keyCode == KEYCODE_VOLUME_DOWN
+ ? KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_DOWN
: keyCode == KEYCODE_VOLUME_UP
- ? KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_UP
- : KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_MUTE;
- notifyKeyboardShortcutTriggeredOnActionDown(event, systemShortcut);
+ ? KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_UP
+ : KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_MUTE;
+ notifyKeyGestureCompletedOnActionDown(event, gestureType);
if (down) {
sendSystemKeyToStatusBarAsync(event);
@@ -4888,8 +4888,8 @@
}
case KeyEvent.KEYCODE_TV_POWER: {
- notifyKeyboardShortcutTriggeredOnActionUp(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_POWER);
+ notifyKeyGestureCompletedOnActionUp(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down && hdmiControlManager != null) {
@@ -4899,8 +4899,8 @@
}
case KeyEvent.KEYCODE_POWER: {
- notifyKeyboardShortcutTriggeredOnActionUp(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_POWER);
+ notifyKeyGestureCompletedOnActionUp(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER);
EventLogTags.writeInterceptPower(
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0,
@@ -4923,16 +4923,16 @@
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
// fall through
case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: {
- notifyKeyboardShortcutTriggeredOnActionUp(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_NAVIGATION);
+ notifyKeyGestureCompletedOnActionUp(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION);
result &= ~ACTION_PASS_TO_USER;
interceptSystemNavigationKey(event);
break;
}
case KeyEvent.KEYCODE_SLEEP: {
- notifyKeyboardShortcutTriggeredOnActionUp(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SLEEP);
+ notifyKeyGestureCompletedOnActionUp(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SLEEP);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false;
if (!mPowerManager.isInteractive()) {
@@ -4948,8 +4948,8 @@
}
case KeyEvent.KEYCODE_SOFT_SLEEP: {
- notifyKeyboardShortcutTriggeredOnActionUp(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SLEEP);
+ notifyKeyGestureCompletedOnActionUp(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SLEEP);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false;
if (!down) {
@@ -4960,8 +4960,8 @@
}
case KeyEvent.KEYCODE_WAKEUP: {
- notifyKeyboardShortcutTriggeredOnActionUp(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_WAKEUP);
+ notifyKeyGestureCompletedOnActionUp(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_WAKEUP);
result &= ~ACTION_PASS_TO_USER;
isWakeKey = true;
break;
@@ -4970,8 +4970,8 @@
case KeyEvent.KEYCODE_MUTE:
result &= ~ACTION_PASS_TO_USER;
if (down && event.getRepeatCount() == 0) {
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_MUTE);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_MUTE);
toggleMicrophoneMuteFromKey();
}
break;
@@ -4986,8 +4986,8 @@
case KeyEvent.KEYCODE_MEDIA_RECORD:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
- notifyKeyboardShortcutTriggeredOnActionUp(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_MEDIA_KEY);
+ notifyKeyGestureCompletedOnActionUp(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY);
if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) {
// If the global session is active pass all media keys to it
// instead of the active window.
@@ -5032,8 +5032,8 @@
0 /* unused */, event.getEventTime() /* eventTime */);
msg.setAsynchronous(true);
msg.sendToTarget();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
}
result &= ~ACTION_PASS_TO_USER;
break;
@@ -5044,8 +5044,8 @@
Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK);
msg.setAsynchronous(true);
msg.sendToTarget();
- notifyKeyboardShortcutTriggered(event,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT);
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT);
}
result &= ~ACTION_PASS_TO_USER;
break;
diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java
index 72594b3..2e8a0c6 100644
--- a/services/core/java/com/android/server/power/PowerGroup.java
+++ b/services/core/java/com/android/server/power/PowerGroup.java
@@ -433,7 +433,8 @@
boolean updateLocked(float screenBrightnessOverride, CharSequence overrideTag,
boolean useProximitySensor, boolean boostScreenBrightness, int dozeScreenState,
@Display.StateReason int dozeScreenStateReason,
- float dozeScreenBrightness, boolean overrideDrawWakeLock,
+ float dozeScreenBrightness, boolean useNormalBrightnessForDoze,
+ boolean overrideDrawWakeLock,
PowerSaveState powerSaverState, boolean quiescent,
boolean dozeAfterScreenOff, boolean bootCompleted,
boolean screenBrightnessBoostInProgress, boolean waitForNegativeProximity,
@@ -461,11 +462,13 @@
}
}
mDisplayPowerRequest.dozeScreenBrightness = dozeScreenBrightness;
+ mDisplayPowerRequest.useNormalBrightnessForDoze = useNormalBrightnessForDoze;
} else {
mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mDisplayPowerRequest.dozeScreenStateReason =
Display.STATE_REASON_DEFAULT_POLICY;
+ mDisplayPowerRequest.useNormalBrightnessForDoze = false;
}
mDisplayPowerRequest.lowPowerMode = powerSaverState.batterySaverEnabled;
mDisplayPowerRequest.screenLowPowerBrightnessFactor = powerSaverState.brightnessFactor;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 699c9b5..b22b726 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -662,6 +662,7 @@
*/
private float mDozeScreenBrightnessOverrideFromDreamManagerFloat =
PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ private boolean mUseNormalBrightnessForDoze;
// Keep display state when dozing.
private boolean mDrawWakeLockOverrideFromSidekick;
@@ -3656,6 +3657,7 @@
mDozeScreenStateOverrideFromDreamManager,
mDozeScreenStateOverrideReasonFromDreamManager,
mDozeScreenBrightnessOverrideFromDreamManagerFloat,
+ mUseNormalBrightnessForDoze,
mDrawWakeLockOverrideFromSidekick,
mBatterySaverSupported
?
@@ -4470,13 +4472,13 @@
private void setDozeOverrideFromDreamManagerInternal(
int screenState, @Display.StateReason int reason, float screenBrightnessFloat,
- int screenBrightnessInt) {
+ int screenBrightnessInt, boolean useNormalBrightnessForDoze) {
synchronized (mLock) {
if (mDozeScreenStateOverrideFromDreamManager != screenState
|| mDozeScreenBrightnessOverrideFromDreamManager != screenBrightnessInt
|| !BrightnessSynchronizer.floatEquals(
- mDozeScreenBrightnessOverrideFromDreamManagerFloat,
- screenBrightnessFloat)) {
+ mDozeScreenBrightnessOverrideFromDreamManagerFloat, screenBrightnessFloat)
+ || mUseNormalBrightnessForDoze != useNormalBrightnessForDoze) {
mDozeScreenStateOverrideFromDreamManager = screenState;
mDozeScreenStateOverrideReasonFromDreamManager = reason;
mDozeScreenBrightnessOverrideFromDreamManager = screenBrightnessInt;
@@ -4484,6 +4486,7 @@
isValidBrightnessValue(screenBrightnessFloat)
? screenBrightnessFloat
: BrightnessSynchronizer.brightnessIntToFloat(screenBrightnessInt);
+ mUseNormalBrightnessForDoze = useNormalBrightnessForDoze;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
@@ -4798,6 +4801,7 @@
pw.println(" mDrawWakeLockOverrideFromSidekick=" + mDrawWakeLockOverrideFromSidekick);
pw.println(" mDozeScreenBrightnessOverrideFromDreamManager="
+ mDozeScreenBrightnessOverrideFromDreamManager);
+ pw.println(" mUseNormalBrightnessForDoze=" + mUseNormalBrightnessForDoze);
pw.println(" mScreenBrightnessMinimum=" + mScreenBrightnessMinimum);
pw.println(" mScreenBrightnessMaximum=" + mScreenBrightnessMaximum);
pw.println(" mScreenBrightnessDefault=" + mScreenBrightnessDefault);
@@ -7115,7 +7119,8 @@
@Override
public void setDozeOverrideFromDreamManager(
- int screenState, int reason, float screenBrightnessFloat, int screenBrightnessInt) {
+ int screenState, int reason, float screenBrightnessFloat, int screenBrightnessInt,
+ boolean useNormalBrightnessForDoze) {
switch (screenState) {
case Display.STATE_UNKNOWN:
case Display.STATE_OFF:
@@ -7138,7 +7143,7 @@
screenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
setDozeOverrideFromDreamManagerInternal(screenState, reason, screenBrightnessFloat,
- screenBrightnessInt);
+ screenBrightnessInt, useNormalBrightnessForDoze);
}
@Override
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
index 68eb8eb..480db25 100644
--- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -76,8 +76,6 @@
// Keep this in sync with the definitions in TraceService
private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED =
"com.android.traceur.NOTIFY_SESSION_STOPPED";
- private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN =
- "com.android.traceur.NOTIFY_SESSION_STOLEN";
private static final int REPORT_BEGIN =
TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BEGIN;
@@ -97,13 +95,12 @@
private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
/**
- * Notifies system tracing app that a tracing session has ended. If a session is repurposed
- * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
- * there is no buffer available to dump.
+ * Notifies system tracing app that a tracing session has ended. sessionStolen is ignored,
+ * as trace sessions are no longer stolen and are always cloned instead.
*/
@Override
- public void notifyTraceSessionEnded(boolean sessionStolen) {
- TracingServiceProxy.this.notifyTraceur(sessionStolen);
+ public void notifyTraceSessionEnded(boolean sessionStolen /* unused */) {
+ TracingServiceProxy.this.notifyTraceur();
}
@Override
@@ -132,7 +129,7 @@
}
}
- private void notifyTraceur(boolean sessionStolen) {
+ private void notifyTraceur() {
final Intent intent = new Intent();
try {
@@ -141,11 +138,7 @@
PackageManager.MATCH_SYSTEM_ONLY);
intent.setClassName(info.packageName, TRACING_APP_ACTIVITY);
- if (sessionStolen) {
- intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOLEN);
- } else {
- intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED);
- }
+ intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED);
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 0c10551..e27b2be 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1156,7 +1156,7 @@
}
if (rootTask.inFreeformWindowingMode()) {
- rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ rootTask.setRootTaskWindowingMode(WINDOWING_MODE_FULLSCREEN);
rootTask.setBounds(null);
} else if (!r.supportsFreeform()) {
throw new IllegalStateException(
@@ -1165,9 +1165,9 @@
// If the window is on a freeform display, set it to undefined. It will be
// resolved to freeform and it can adjust windowing mode when the display mode
// changes in runtime.
- rootTask.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ rootTask.setRootTaskWindowingMode(WINDOWING_MODE_UNDEFINED);
} else {
- rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ rootTask.setRootTaskWindowingMode(WINDOWING_MODE_FREEFORM);
}
}
} finally {
@@ -1278,7 +1278,7 @@
if (fullscreenRequest == FULLSCREEN_MODE_REQUEST_ENTER) {
final int restoreWindowingMode = requester.getRequestedOverrideWindowingMode();
targetWindowingMode = WINDOWING_MODE_FULLSCREEN;
- requester.setWindowingMode(targetWindowingMode);
+ requester.setRootTaskWindowingMode(targetWindowingMode);
// The restore windowing mode must be set after the windowing mode is set since
// Task#setWindowingMode resets the restore windowing mode to WINDOWING_MODE_INVALID.
requester.mMultiWindowRestoreWindowingMode = restoreWindowingMode;
@@ -1297,9 +1297,8 @@
public void startLockTaskModeByToken(IBinder token) {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r != null) {
- mService.startLockTaskMode(r.getTask(), false /* isSystemCaller */);
- }
+ if (r == null) return;
+ mService.startLockTaskMode(r.getTask(), false /* isSystemCaller */);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a76f1b6..3ac91b3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -789,8 +789,6 @@
*/
private boolean mWillCloseOrEnterPip;
- final LetterboxUiController mLetterboxUiController;
-
/**
* App Compat Facade
*/
@@ -1324,7 +1322,7 @@
pw.print(prefix); pw.println("mWaitForEnteringPinnedMode=true");
}
- mLetterboxUiController.dump(pw, prefix);
+ mAppCompatController.dump(pw, prefix);
}
static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r,
@@ -1988,12 +1986,10 @@
}
// Don't move below setActivityType since it triggers onConfigurationChange ->
- // resolveOverrideConfiguration that requires having mLetterboxUiController initialised.
+ // resolveOverrideConfiguration that requires having mAppCompatController initialised.
// Don't move below setOrientation(info.screenOrientation) since it triggers
- // getOverrideOrientation that requires having mLetterboxUiController
- // initialised.
+ // getOverrideOrientation that requires having mAppCompatController initialised.
mAppCompatController = new AppCompatController(mWmService, this);
- mLetterboxUiController = new LetterboxUiController(mWmService, this);
mResolveConfigHint = new TaskFragment.ConfigOverrideHint();
if (mWmService.mFlags.mInsetsDecoupledConfiguration) {
// When the stable configuration is the default behavior, override for the legacy apps
@@ -2016,7 +2012,7 @@
mIsUserAlwaysVisible = properties != null && properties.getAlwaysVisible();
mShowForAllUsers = (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0 || mIsUserAlwaysVisible;
- setOrientation(info.screenOrientation);
+ setOverrideOrientation(info.screenOrientation);
mRotationAnimationHint = info.rotationAnimation;
mShowWhenLocked = (aInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
@@ -3626,10 +3622,10 @@
final WindowContainer<?> trigger = endTask ? task : this;
final Transition newTransition =
mTransitionController.requestCloseTransitionIfNeeded(trigger);
- if (newTransition != null) {
- newTransition.collectClose(trigger);
- } else if (mTransitionController.isCollecting()) {
- mTransitionController.getCollectingTransition().collectClose(trigger);
+ final Transition transition = newTransition != null
+ ? newTransition : mTransitionController.getCollectingTransition();
+ if (transition != null) {
+ transition.collectClose(trigger);
}
if (isState(RESUMED)) {
if (endTask) {
@@ -4302,9 +4298,9 @@
// closing the task.
final WindowContainer trigger = remove && task != null && task.getChildCount() == 1
? task : this;
- final Transition newTransit = mTransitionController.requestCloseTransitionIfNeeded(trigger);
- if (newTransit != null) {
- newTransit.collectClose(trigger);
+ final Transition tr = mTransitionController.requestCloseTransitionIfNeeded(trigger);
+ if (tr != null) {
+ tr.collectClose(trigger);
} else if (mTransitionController.isCollecting()) {
mTransitionController.getCollectingTransition().collectClose(trigger);
}
@@ -5320,12 +5316,14 @@
task.lastDescription = description;
}
- void setDeferHidingClient(boolean deferHidingClient) {
- if (mDeferHidingClient == deferHidingClient) {
- return;
- }
- mDeferHidingClient = deferHidingClient;
- if (!mDeferHidingClient && !mVisibleRequested) {
+ void setDeferHidingClient() {
+ mDeferHidingClient = true;
+ }
+
+ void clearDeferHidingClient() {
+ if (!mDeferHidingClient) return;
+ mDeferHidingClient = false;
+ if (!mVisibleRequested) {
// Hiding the client is no longer deferred and the app isn't visible still, go ahead and
// update the visibility.
setVisibility(false);
@@ -5449,8 +5447,8 @@
boolean isCollecting = false;
boolean inFinishingTransition = false;
if (mTransitionController.isShellTransitionsEnabled()) {
- isCollecting = mTransitionController.isCollecting();
- if (isCollecting) {
+ if (mTransitionController.isCollecting()) {
+ isCollecting = true;
mTransitionController.collect(this);
} else {
// Failsafe to make sure that we show any activities that were incorrectly hidden
@@ -6195,7 +6193,11 @@
// stopped or stopping. This gives it a chance to enter Pip in onPause().
final boolean deferHidingClient = canEnterPictureInPicture
&& !isState(STARTED, STOPPING, STOPPED, PAUSED);
- setDeferHidingClient(deferHidingClient);
+ if (deferHidingClient) {
+ setDeferHidingClient();
+ } else {
+ clearDeferHidingClient();
+ }
setVisibility(false);
switch (getState()) {
@@ -6234,7 +6236,7 @@
Slog.v(TAG_VISIBILITY, "Resume visible activity, " + this);
}
return getRootTask().resumeTopActivityUncheckedLocked(activeActivity /* prev */,
- null /* options */);
+ null /* options */, false /* skipPause */);
} else if (shouldPauseActivity(activeActivity)) {
if (DEBUG_VISIBILITY) {
Slog.v(TAG_VISIBILITY, "Pause visible activity, " + this);
@@ -6524,9 +6526,7 @@
void stopIfPossible() {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
if (finishing) {
- Slog.e(TAG, "Request to stop a finishing activity: " + this);
- destroyIfPossible("stopIfPossible-finishing");
- return;
+ throw new IllegalStateException("Request to stop a finishing activity: " + this);
}
if (isNoHistory()) {
if (!task.shouldSleepActivities()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index a0ef030..1660ca9 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -612,11 +612,10 @@
final Task task = r.getTask();
mService.deferWindowLayout();
try {
- final TransitionController controller = r.mTransitionController;
- final Transition transition = controller.getCollectingTransition();
+ final Transition transition = r.mTransitionController.getCollectingTransition();
if (transition != null) {
transition.setRemoteAnimationApp(r.app.getThread());
- controller.setTransientLaunch(r, TaskDisplayArea.getRootTaskAbove(rootTask));
+ transition.setTransientLaunch(r, TaskDisplayArea.getRootTaskAbove(rootTask));
}
task.moveToFront("startExistingRecents");
task.mInResumeTopActivity = true;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 18aa9a0..6479111 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1355,9 +1355,21 @@
mService.resumeAppSwitches();
}
+ // Only do the create here since startActivityInner can abort. If it doesn't abort,
+ // the requestStart will be sent in handleStartRequest.
+ final Transition newTransition = r.mTransitionController.isShellTransitionsEnabled()
+ ? r.mTransitionController.createAndStartCollecting(TRANSIT_OPEN) : null;
+ // Because startActivity must run immediately, it can get combined with another
+ // transition meaning it is no-longer independent. This is NOT desirable, but is the
+ // only option for the time being.
+ final boolean isIndependent = newTransition != null;
+ final Transition transition = isIndependent ? newTransition
+ : mService.getTransitionController().getCollectingTransition();
+
mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
request.voiceInteractor, startFlags, checkedOptions,
- inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid);
+ inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid, transition,
+ isIndependent);
if (request.outActivity != null) {
request.outActivity[0] = mLastStartActivityRecord;
@@ -1509,33 +1521,27 @@
int startFlags, ActivityOptions options, Task inTask,
TaskFragment inTaskFragment,
BalVerdict balVerdict,
- NeededUriGrants intentGrants, int realCallingUid) {
+ NeededUriGrants intentGrants, int realCallingUid, Transition transition,
+ boolean isIndependentLaunch) {
int result = START_CANCELED;
final Task startedActivityRootTask;
- // Create a transition now to record the original intent of actions taken within
- // startActivityInner. Otherwise, logic in startActivityInner could start a different
- // transition based on a sub-action.
- // Only do the create here (and defer requestStart) since startActivityInner might abort.
- final TransitionController transitionController = r.mTransitionController;
- Transition newTransition = transitionController.isShellTransitionsEnabled()
- ? transitionController.createAndStartCollecting(TRANSIT_OPEN) : null;
RemoteTransition remoteTransition = r.takeRemoteTransition();
// Create a display snapshot as soon as possible.
- if (newTransition != null && mRequest.freezeScreen) {
+ if (isIndependentLaunch && mRequest.freezeScreen) {
final TaskDisplayArea tda = mLaunchParams.hasPreferredTaskDisplayArea()
? mLaunchParams.mPreferredTaskDisplayArea
: mRootWindowContainer.getDefaultTaskDisplayArea();
final DisplayContent dc = mRootWindowContainer.getDisplayContentOrCreate(
tda.getDisplayId());
if (dc != null) {
- transitionController.collect(dc);
- transitionController.collectVisibleChange(dc);
+ transition.collect(dc);
+ transition.collectVisibleChange(dc);
}
}
try {
mService.deferWindowLayout();
- transitionController.collect(r);
+ r.mTransitionController.collect(r);
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
@@ -1545,8 +1551,8 @@
Slog.e(TAG, "Exception on startActivityInner", ex);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- startedActivityRootTask = handleStartResult(r, options, result, newTransition,
- remoteTransition);
+ startedActivityRootTask = handleStartResult(r, options, result, isIndependentLaunch,
+ remoteTransition, transition);
}
} finally {
mService.continueWindowLayout();
@@ -1571,8 +1577,8 @@
* @return the root task where the successful started activity resides.
*/
private @Nullable Task handleStartResult(@NonNull ActivityRecord started,
- ActivityOptions options, int result, Transition newTransition,
- RemoteTransition remoteTransition) {
+ ActivityOptions options, int result, boolean isIndependentLaunch,
+ RemoteTransition remoteTransition, @NonNull Transition transition) {
final boolean userLeaving = mSupervisor.mUserLeaving;
mSupervisor.mUserLeaving = false;
final Task currentRootTask = started.getRootTask();
@@ -1596,8 +1602,9 @@
&& !startedActivityRootTask.mCreatedByOrganizer) {
startedActivityRootTask.removeIfPossible("handleStartResult");
}
- if (newTransition != null) {
- newTransition.abort();
+ if (isIndependentLaunch
+ && mService.getTransitionController().isShellTransitionsEnabled()) {
+ transition.abort();
}
return null;
}
@@ -1649,44 +1656,46 @@
// The activity is started new rather than just brought forward, so record it as an
// existence change.
transitionController.collectExistenceChange(started);
- } else if (result == START_DELIVERED_TO_TOP && newTransition != null
+ } else if (result == START_DELIVERED_TO_TOP && isIndependentLaunch
// An activity has changed order/visibility or the task is occluded by a transient
// activity, so this isn't just deliver-to-top
&& mMovedToTopActivity == null
&& !transitionController.hasOrderChanges()
&& !transitionController.isTransientHide(startedActivityRootTask)
- && !newTransition.hasChanged(mLastStartActivityRecord)) {
+ && !transition.hasChanged(mLastStartActivityRecord)) {
// We just delivered to top, so there isn't an actual transition here.
if (!forceTransientTransition) {
- newTransition.abort();
- newTransition = null;
+ transition.abort();
+ transition = null;
}
}
- if (forceTransientTransition) {
- transitionController.collect(mLastStartActivityRecord);
- transitionController.collect(mPriorAboveTask);
+ if (forceTransientTransition && transition != null) {
+ transition.collect(mLastStartActivityRecord);
+ transition.collect(mPriorAboveTask);
// If keyguard is active and occluded, the transient target won't be moved to front
// to be collected, so set transient again after it is collected.
- transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask);
+ transition.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask);
final DisplayContent dc = mLastStartActivityRecord.getDisplayContent();
// update wallpaper target to TransientHide
dc.mWallpaperController.adjustWallpaperWindows();
// execute transition because there is no change
- transitionController.setReady(dc, true /* ready */);
+ transition.setReady(dc, true /* ready */);
}
- if (!userLeaving) {
+ if (!userLeaving && transition != null) {
// no-user-leaving implies not entering PiP.
- transitionController.setCanPipOnFinish(false /* canPipOnFinish */);
+ transition.setCanPipOnFinish(false /* canPipOnFinish */);
}
- if (newTransition != null) {
- transitionController.requestStartTransition(newTransition,
+ if (isIndependentLaunch && transition != null) {
+ transitionController.requestStartTransition(transition,
mTargetTask == null ? started.getTask() : mTargetTask,
remoteTransition, null /* displayChange */);
} else if (result == START_SUCCESS && mStartActivity.isState(RESUMED)) {
// Do nothing if the activity is started and is resumed directly.
} else if (isStarted) {
// Make the collecting transition wait until this request is ready.
- transitionController.setReady(started, false);
+ if (transition != null) {
+ transition.setReady(started, false);
+ }
}
return startedActivityRootTask;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f23211b..121ab2c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2116,12 +2116,7 @@
Slog.w(TAG, "removeTask: No task remove with id=" + taskId);
return false;
}
-
- if (task.isLeafTask()) {
- mTaskSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task");
- } else {
- mTaskSupervisor.removeRootTask(task);
- }
+ removeTask(task);
return true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2129,6 +2124,14 @@
}
}
+ void removeTask(@NonNull Task task) {
+ if (task.isLeafTask()) {
+ mTaskSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task");
+ } else {
+ mTaskSupervisor.removeRootTask(task);
+ }
+ }
+
@Override
public void removeAllVisibleRecentTasks() {
mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
@@ -2938,7 +2941,7 @@
}
getTransitionController().requestStartTransition(transition, task,
null /* remoteTransition */, null /* displayChange */);
- getTransitionController().collect(task);
+ transition.collect(task);
task.resize(bounds, resizeMode, preserveWindow);
transition.setReady(task, true);
});
@@ -3768,6 +3771,7 @@
// Shell calls back into Core with the entry bounds to be applied with startWCT.
final Transition enterPipTransition = new Transition(TRANSIT_PIP,
0 /* flags */, getTransitionController(), mWindowManager.mSyncEngine);
+ r.setPictureInPictureParams(params);
enterPipTransition.setPipActivity(r);
r.mAutoEnteringPip = isAutoEnter;
getTransitionController().startCollectOrQueue(enterPipTransition, (deferred) -> {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 8ef2693..1446c35 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1528,9 +1528,12 @@
}
mService.deferWindowLayout();
- final Transition newTransition = task.mTransitionController.isShellTransitionsEnabled()
- ? task.mTransitionController.isCollecting() ? null
- : task.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null;
+ boolean newTransition = false;
+ Transition transition = task.mTransitionController.getCollectingTransition();
+ if (transition == null && task.mTransitionController.isShellTransitionsEnabled()) {
+ transition = task.mTransitionController.createTransition(TRANSIT_TO_FRONT);
+ newTransition = true;
+ }
task.mTransitionController.collect(task);
reason = reason + " findTaskToMoveToFront";
boolean reparented = false;
@@ -1574,8 +1577,8 @@
// transition to avoid delaying the starting window.
r.showStartingWindow(true /* taskSwitch */);
}
- if (newTransition != null) {
- task.mTransitionController.requestStartTransition(newTransition, task,
+ if (newTransition) {
+ task.mTransitionController.requestStartTransition(transition, task,
options != null ? options.getRemoteTransition() : null,
null /* displayChange */);
}
@@ -1644,7 +1647,7 @@
mService.deferWindowLayout();
try {
- rootTask.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ rootTask.setRootTaskWindowingMode(WINDOWING_MODE_UNDEFINED);
if (rootTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
rootTask.setBounds(null);
}
@@ -1705,7 +1708,10 @@
// Prevent recursion.
return;
}
- final Transition transit = task.mTransitionController.requestCloseTransitionIfNeeded(task);
+ Transition transit = task.mTransitionController.requestCloseTransitionIfNeeded(task);
+ if (transit == null) {
+ transit = task.mTransitionController.getCollectingTransition();
+ }
if (transit != null) {
transit.collectClose(task);
if (!task.mTransitionController.useFullReadyTracking()) {
@@ -1717,8 +1723,6 @@
// before anything that may need it to wait (setReady(false)).
transit.setReady(task, true);
}
- } else if (task.mTransitionController.isCollecting()) {
- task.mTransitionController.getCollectingTransition().collectClose(task);
}
// Consume the stopping activities immediately so activity manager won't skip killing
// the process because it is still foreground state, i.e. RESUMED -> PAUSING set from
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
index 1562cf6..a42b879 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -24,6 +24,7 @@
import android.content.res.Configuration;
import android.widget.Toast;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.window.flags.Flags;
/**
@@ -32,7 +33,8 @@
class AppCompatCameraPolicy {
@Nullable
- private final CameraStateMonitor mCameraStateMonitor;
+ @VisibleForTesting
+ final CameraStateMonitor mCameraStateMonitor;
@Nullable
private final ActivityRefresher mActivityRefresher;
@Nullable
@@ -122,6 +124,9 @@
}
void start() {
+ if (mDisplayRotationCompatPolicy != null) {
+ mDisplayRotationCompatPolicy.start();
+ }
if (mCameraCompatFreeformPolicy != null) {
mCameraCompatFreeformPolicy.start();
}
@@ -150,6 +155,10 @@
return mCameraCompatFreeformPolicy != null;
}
+ boolean hasCameraStateMonitor() {
+ return mCameraStateMonitor != null;
+ }
+
@ScreenOrientation
int getOrientation() {
return mDisplayRotationCompatPolicy != null
diff --git a/services/core/java/com/android/server/wm/AppCompatConfiguration.java b/services/core/java/com/android/server/wm/AppCompatConfiguration.java
index ffa4251..42378aa 100644
--- a/services/core/java/com/android/server/wm/AppCompatConfiguration.java
+++ b/services/core/java/com/android/server/wm/AppCompatConfiguration.java
@@ -30,6 +30,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.utils.DimenPxIntSupplier;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Function;
@@ -1382,6 +1383,26 @@
setUserAppAspectRatioFullscreenOverrideEnabled(false);
}
+ void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ // TODO(b/359438445): Add more useful information to dump().
+ pw.println(prefix + " letterboxPositionForHorizontalReachability="
+ + letterboxHorizontalReachabilityPositionToString(
+ getLetterboxPositionForHorizontalReachability(
+ /* isInFullScreenBookMode */ false)));
+ pw.println(prefix + " letterboxPositionForVerticalReachability="
+ + letterboxVerticalReachabilityPositionToString(
+ getLetterboxPositionForVerticalReachability(
+ /* isInFullScreenTabletopMode */ false)));
+ pw.println(prefix + " fixedOrientationLetterboxAspectRatio="
+ + getFixedOrientationLetterboxAspectRatio());
+ pw.println(prefix + " defaultMinAspectRatioForUnresizableApps="
+ + getDefaultMinAspectRatioForUnresizableApps());
+ pw.println(prefix + " isSplitScreenAspectRatioForUnresizableAppsEnabled="
+ + getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
+ pw.println(prefix + " isDisplayAspectRatioEnabledForFixedOrientationLetterbox="
+ + getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
+ }
+
/**
* Checks whether the multiplier is between [0,1].
*
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 4290051..906ee20 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -21,6 +21,8 @@
import com.android.server.wm.utils.OptPropFactory;
+import java.io.PrintWriter;
+
/**
* Allows the interaction with all the app compat policies and configurations
*/
@@ -59,7 +61,8 @@
mTransparentPolicy, mAppCompatOverrides);
mAppCompatReachabilityPolicy = new AppCompatReachabilityPolicy(mActivityRecord,
wmService.mAppCompatConfiguration);
- mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(mActivityRecord);
+ mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(mActivityRecord,
+ wmService.mAppCompatConfiguration);
}
@NonNull
@@ -140,4 +143,9 @@
return mAppCompatOverrides.getAppCompatLetterboxOverrides();
}
+ void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ getTransparentPolicy().dump(pw, prefix);
+ getAppCompatLetterboxPolicy().dump(pw, prefix);
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
index d602c47..afc6506 100644
--- a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
@@ -21,6 +21,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.AppCompatConfiguration.letterboxBackgroundTypeToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,6 +33,8 @@
import com.android.internal.statusbar.LetterboxDetails;
import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
+import java.io.PrintWriter;
+
/**
* Encapsulates the logic for the Letterboxing policy.
*/
@@ -43,15 +46,19 @@
private final LetterboxPolicyState mLetterboxPolicyState;
@NonNull
private final AppCompatRoundedCorners mAppCompatRoundedCorners;
+ @NonNull
+ private final AppCompatConfiguration mAppCompatConfiguration;
private boolean mLastShouldShowLetterboxUi;
- AppCompatLetterboxPolicy(@NonNull ActivityRecord activityRecord) {
+ AppCompatLetterboxPolicy(@NonNull ActivityRecord activityRecord,
+ @NonNull AppCompatConfiguration appCompatConfiguration) {
mActivityRecord = activityRecord;
mLetterboxPolicyState = new LetterboxPolicyState();
// TODO (b/358334569) Improve cutout logic dependency on app compat.
mAppCompatRoundedCorners = new AppCompatRoundedCorners(mActivityRecord,
this::isLetterboxedNotForDisplayCutout);
+ mAppCompatConfiguration = appCompatConfiguration;
}
/** Cleans up {@link Letterbox} if it exists.*/
@@ -156,6 +163,38 @@
return mAppCompatRoundedCorners.getRoundedCornersRadius(mainWindow);
}
+ void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ final WindowState mainWin = mActivityRecord.findMainWindow();
+ if (mainWin == null) {
+ return;
+ }
+ boolean areBoundsLetterboxed = mainWin.areAppWindowBoundsLetterboxed();
+ pw.println(prefix + "areBoundsLetterboxed=" + areBoundsLetterboxed);
+ pw.println(prefix + "isLetterboxRunning=" + isRunning());
+ if (!areBoundsLetterboxed) {
+ return;
+ }
+ pw.println(prefix + " letterboxReason="
+ + AppCompatUtils.getLetterboxReasonString(mActivityRecord, mainWin));
+ mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy().dump(pw, prefix);
+ final AppCompatLetterboxOverrides letterboxOverride = mActivityRecord.mAppCompatController
+ .getAppCompatLetterboxOverrides();
+ pw.println(prefix + " letterboxBackgroundColor=" + Integer.toHexString(
+ letterboxOverride.getLetterboxBackgroundColor().toArgb()));
+ pw.println(prefix + " letterboxBackgroundType="
+ + letterboxBackgroundTypeToString(letterboxOverride.getLetterboxBackgroundType()));
+ pw.println(prefix + " letterboxCornerRadius=" + getRoundedCornersRadius(mainWin));
+ if (letterboxOverride.getLetterboxBackgroundType() == LETTERBOX_BACKGROUND_WALLPAPER) {
+ pw.println(prefix + " isLetterboxWallpaperBlurSupported="
+ + letterboxOverride.isLetterboxWallpaperBlurSupported());
+ pw.println(prefix + " letterboxBackgroundWallpaperDarkScrimAlpha="
+ + letterboxOverride.getLetterboxWallpaperDarkScrimAlpha());
+ pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius="
+ + letterboxOverride.getLetterboxWallpaperBlurRadiusPx());
+ }
+ mAppCompatConfiguration.dump(pw, prefix);
+ }
+
private void updateWallpaperForLetterbox(@NonNull WindowState mainWindow) {
final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
.mAppCompatController.getAppCompatLetterboxOverrides();
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
index c3bf116..d03a803 100644
--- a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
@@ -33,6 +33,7 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
import java.util.function.Supplier;
/**
@@ -74,6 +75,25 @@
handleVerticalDoubleTap(y);
}
+ void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ final AppCompatReachabilityOverrides reachabilityOverrides =
+ mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides();
+ pw.println(prefix + " isVerticalThinLetterboxed=" + reachabilityOverrides
+ .isVerticalThinLetterboxed());
+ pw.println(prefix + " isHorizontalThinLetterboxed=" + reachabilityOverrides
+ .isHorizontalThinLetterboxed());
+ pw.println(prefix + " isHorizontalReachabilityEnabled="
+ + reachabilityOverrides.isHorizontalReachabilityEnabled());
+ pw.println(prefix + " isVerticalReachabilityEnabled="
+ + reachabilityOverrides.isVerticalReachabilityEnabled());
+ pw.println(prefix + " letterboxHorizontalPositionMultiplier="
+ + reachabilityOverrides.getHorizontalPositionMultiplier(
+ mActivityRecord.getParent().getConfiguration()));
+ pw.println(prefix + " letterboxVerticalPositionMultiplier="
+ + reachabilityOverrides.getVerticalPositionMultiplier(
+ mActivityRecord.getParent().getConfiguration()));
+ }
+
private void handleHorizontalDoubleTap(int x) {
final AppCompatReachabilityOverrides reachabilityOverrides =
mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides();
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 89e10d36..767effd 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -367,7 +367,7 @@
// client process to prevent the unexpected relayout when handling the back
// animation.
for (int i = prevActivities.size() - 1; i >= 0; --i) {
- prevActivities.get(i).setDeferHidingClient(true);
+ prevActivities.get(i).setDeferHidingClient();
}
} else {
scheduleAnimation(builder);
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
index 9b142f28..dda39a6 100644
--- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -60,6 +60,11 @@
@Nullable
private Task mCameraTask;
+ /**
+ * Value toggled on {@link #start()} to {@code true} and on {@link #dispose()} to {@code false}.
+ */
+ private boolean mIsRunning;
+
CameraCompatFreeformPolicy(@NonNull DisplayContent displayContent,
@NonNull CameraStateMonitor cameraStateMonitor,
@NonNull ActivityRefresher activityRefresher) {
@@ -71,12 +76,19 @@
void start() {
mCameraStateMonitor.addCameraStateListener(this);
mActivityRefresher.addEvaluator(this);
+ mIsRunning = true;
}
/** Releases camera callback listener. */
void dispose() {
mCameraStateMonitor.removeCameraStateListener(this);
mActivityRefresher.removeEvaluator(this);
+ mIsRunning = false;
+ }
+
+ @VisibleForTesting
+ boolean isRunning() {
+ return mIsRunning;
}
// Refreshing only when configuration changes after rotation or camera split screen aspect ratio
diff --git a/services/core/java/com/android/server/wm/CameraStateMonitor.java b/services/core/java/com/android/server/wm/CameraStateMonitor.java
index 63c90ff..8bfef6d 100644
--- a/services/core/java/com/android/server/wm/CameraStateMonitor.java
+++ b/services/core/java/com/android/server/wm/CameraStateMonitor.java
@@ -26,6 +26,7 @@
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import java.util.ArrayList;
@@ -73,6 +74,12 @@
private final ArrayList<CameraCompatStateListener> mCameraStateListeners = new ArrayList<>();
+ /**
+ * Value toggled on {@link #startListeningToCameraState()} to {@code true} and on {@link
+ * #dispose()} to {@code false}.
+ */
+ private boolean mIsRunning;
+
private final CameraManager.AvailabilityCallback mAvailabilityCallback =
new CameraManager.AvailabilityCallback() {
@Override
@@ -101,6 +108,7 @@
void startListeningToCameraState() {
mCameraManager.registerAvailabilityCallback(
mWmService.mContext.getMainExecutor(), mAvailabilityCallback);
+ mIsRunning = true;
}
/** Releases camera callback listener. */
@@ -108,6 +116,12 @@
if (mCameraManager != null) {
mCameraManager.unregisterAvailabilityCallback(mAvailabilityCallback);
}
+ mIsRunning = false;
+ }
+
+ @VisibleForTesting
+ boolean isRunning() {
+ return mIsRunning;
}
void addCameraStateListener(CameraCompatStateListener listener) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fcc6b11..c44e1b1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -45,7 +45,6 @@
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
import static android.view.Display.STATE_UNKNOWN;
import static android.view.Display.isSuspendedState;
import static android.view.InsetsSource.ID_IME;
@@ -80,6 +79,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -6556,7 +6556,12 @@
@VisibleForTesting
boolean shouldDestroyContentOnRemove() {
- return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
+ return getRemoveContentMode() == REMOVE_CONTENT_MODE_DESTROY;
+ }
+
+ @WindowManager.RemoveContentMode
+ int getRemoveContentMode() {
+ return mWmService.mDisplayWindowSettings.getRemoveContentModeLocked(this);
}
boolean shouldSleep() {
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 762180b..27d9767 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -76,6 +76,11 @@
@ScreenOrientation
private int mLastReportedOrientation = SCREEN_ORIENTATION_UNSET;
+ /**
+ * Value toggled on {@link #start()} to {@code true} and on {@link #dispose()} to {@code false}.
+ */
+ private boolean mIsRunning;
+
DisplayRotationCompatPolicy(@NonNull DisplayContent displayContent,
@NonNull CameraStateMonitor cameraStateMonitor,
@NonNull ActivityRefresher activityRefresher) {
@@ -90,12 +95,19 @@
void start() {
mCameraStateMonitor.addCameraStateListener(this);
mActivityRefresher.addEvaluator(this);
+ mIsRunning = true;
}
/** Releases camera state listener. */
void dispose() {
mCameraStateMonitor.removeCameraStateListener(this);
mActivityRefresher.removeEvaluator(this);
+ mIsRunning = false;
+ }
+
+ @VisibleForTesting
+ boolean isRunning() {
+ return mIsRunning;
}
/**
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
deleted file mode 100644
index 0e8291e..0000000
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ /dev/null
@@ -1,132 +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.server.wm;
-
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
-import static com.android.server.wm.AppCompatConfiguration.letterboxBackgroundTypeToString;
-
-import android.annotation.NonNull;
-
-import java.io.PrintWriter;
-
-/** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */
-// TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in
-// SizeCompatTests and LetterboxTests but not all.
-final class LetterboxUiController {
-
- private final AppCompatConfiguration mAppCompatConfiguration;
-
- private final ActivityRecord mActivityRecord;
-
- // TODO(b/356385137): Remove these we added to make dependencies temporarily explicit.
- @NonNull
- private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
- @NonNull
- private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
- @NonNull
- private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
-
- LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
- mAppCompatConfiguration = wmService.mAppCompatConfiguration;
- // Given activityRecord may not be fully constructed since LetterboxUiController
- // is created in its constructor. It shouldn't be used in this constructor but it's safe
- // to use it after since controller is only used in ActivityRecord.
- mActivityRecord = activityRecord;
- // TODO(b/356385137): Remove these we added to make dependencies temporarily explicit.
- mAppCompatReachabilityOverrides = mActivityRecord.mAppCompatController
- .getAppCompatReachabilityOverrides();
- mAppCompatLetterboxPolicy = mActivityRecord.mAppCompatController
- .getAppCompatLetterboxPolicy();
- mAppCompatLetterboxOverrides = mActivityRecord.mAppCompatController
- .getAppCompatLetterboxOverrides();
- }
-
- void dump(PrintWriter pw, String prefix) {
- final WindowState mainWin = mActivityRecord.findMainWindow();
- if (mainWin == null) {
- return;
- }
-
- pw.println(prefix + "isTransparentPolicyRunning="
- + mActivityRecord.mAppCompatController.getTransparentPolicy().isRunning());
-
- boolean areBoundsLetterboxed = mainWin.areAppWindowBoundsLetterboxed();
- pw.println(prefix + "areBoundsLetterboxed=" + areBoundsLetterboxed);
- if (!areBoundsLetterboxed) {
- return;
- }
-
- pw.println(prefix + " letterboxReason="
- + AppCompatUtils.getLetterboxReasonString(mActivityRecord, mainWin));
- pw.println(prefix + " activityAspectRatio="
- + AppCompatUtils.computeAspectRatio(mActivityRecord.getBounds()));
-
- boolean shouldShowLetterboxUi = mAppCompatLetterboxPolicy.shouldShowLetterboxUi(mainWin);
- pw.println(prefix + "shouldShowLetterboxUi=" + shouldShowLetterboxUi);
-
- if (!shouldShowLetterboxUi) {
- return;
- }
- pw.println(prefix + " isVerticalThinLetterboxed="
- + mAppCompatReachabilityOverrides.isVerticalThinLetterboxed());
- pw.println(prefix + " isHorizontalThinLetterboxed="
- + mAppCompatReachabilityOverrides.isHorizontalThinLetterboxed());
- pw.println(prefix + " letterboxBackgroundColor=" + Integer.toHexString(
- mAppCompatLetterboxOverrides.getLetterboxBackgroundColor().toArgb()));
- pw.println(prefix + " letterboxBackgroundType="
- + letterboxBackgroundTypeToString(
- mAppCompatConfiguration.getLetterboxBackgroundType()));
- pw.println(prefix + " letterboxCornerRadius="
- + mAppCompatLetterboxPolicy.getRoundedCornersRadius(mainWin));
- if (mAppCompatConfiguration.getLetterboxBackgroundType()
- == LETTERBOX_BACKGROUND_WALLPAPER) {
- pw.println(prefix + " isLetterboxWallpaperBlurSupported="
- + mAppCompatLetterboxOverrides.isLetterboxWallpaperBlurSupported());
- pw.println(prefix + " letterboxBackgroundWallpaperDarkScrimAlpha="
- + mAppCompatLetterboxOverrides.getLetterboxWallpaperDarkScrimAlpha());
- pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius="
- + mAppCompatLetterboxOverrides.getLetterboxWallpaperBlurRadiusPx());
- }
- final AppCompatReachabilityOverrides reachabilityOverrides = mActivityRecord
- .mAppCompatController.getAppCompatReachabilityOverrides();
- pw.println(prefix + " isHorizontalReachabilityEnabled="
- + reachabilityOverrides.isHorizontalReachabilityEnabled());
- pw.println(prefix + " isVerticalReachabilityEnabled="
- + reachabilityOverrides.isVerticalReachabilityEnabled());
- pw.println(prefix + " letterboxHorizontalPositionMultiplier="
- + mAppCompatReachabilityOverrides.getHorizontalPositionMultiplier(mActivityRecord
- .getParent().getConfiguration()));
- pw.println(prefix + " letterboxVerticalPositionMultiplier="
- + mAppCompatReachabilityOverrides.getVerticalPositionMultiplier(mActivityRecord
- .getParent().getConfiguration()));
- pw.println(prefix + " letterboxPositionForHorizontalReachability="
- + AppCompatConfiguration.letterboxHorizontalReachabilityPositionToString(
- mAppCompatConfiguration.getLetterboxPositionForHorizontalReachability(false)));
- pw.println(prefix + " letterboxPositionForVerticalReachability="
- + AppCompatConfiguration.letterboxVerticalReachabilityPositionToString(
- mAppCompatConfiguration.getLetterboxPositionForVerticalReachability(false)));
- pw.println(prefix + " fixedOrientationLetterboxAspectRatio="
- + mAppCompatConfiguration.getFixedOrientationLetterboxAspectRatio());
- pw.println(prefix + " defaultMinAspectRatioForUnresizableApps="
- + mAppCompatConfiguration.getDefaultMinAspectRatioForUnresizableApps());
- pw.println(prefix + " isSplitScreenAspectRatioForUnresizableAppsEnabled="
- + mAppCompatConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
- pw.println(prefix + " isDisplayAspectRatioEnabledForFixedOrientationLetterbox="
- + mAppCompatConfiguration
- .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
- }
-}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bded98c..6427c32 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1633,7 +1633,7 @@
// Only resume home activity if isn't finishing.
if (r != null && !r.finishing) {
r.moveFocusableActivityToTop(myReason);
- return resumeFocusedTasksTopActivities(r.getRootTask(), prev, null);
+ return resumeFocusedTasksTopActivities(r.getRootTask(), prev);
}
int userId = mWmService.getUserAssignedToDisplay(taskDisplayArea.getDisplayId());
return startHomeOnTaskDisplayArea(userId, myReason, taskDisplayArea,
@@ -2202,7 +2202,7 @@
// During recents animations, the original task is "occluded" by launcher but
// it wasn't paused (due to transient-launch). If we reparent to the (top) task
// now, it will take focus briefly which confuses the RecentTasks tracker.
- rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
+ rootTask.setRootTaskWindowingMode(WINDOWING_MODE_PINNED);
}
// Temporarily disable focus when reparenting to avoid intermediate focus change
// (because the task is on top and the activity is resumed), which could cause the
@@ -2235,7 +2235,7 @@
// TODO(remove-legacy-transit): Move this to the `singleActivity` case when removing
// legacy transit.
- rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
+ rootTask.setRootTaskWindowingMode(WINDOWING_MODE_PINNED);
if (isPip2ExperimentEnabled() && bounds != null) {
// set the final pip bounds in advance if pip2 is enabled
rootTask.setBounds(bounds);
@@ -2470,12 +2470,12 @@
}
boolean resumeFocusedTasksTopActivities() {
- return resumeFocusedTasksTopActivities(null, null, null);
+ return resumeFocusedTasksTopActivities(null, null, null, false /* deferPause */);
}
boolean resumeFocusedTasksTopActivities(
- Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions) {
- return resumeFocusedTasksTopActivities(targetRootTask, target, targetOptions,
+ Task targetRootTask, ActivityRecord target) {
+ return resumeFocusedTasksTopActivities(targetRootTask, target, null /* targetOptions */,
false /* deferPause */);
}
@@ -2527,7 +2527,8 @@
// activity is started and resumed, and no recursion occurs.
final Task focusedRoot = display.getFocusedRootTask();
if (focusedRoot != null) {
- result |= focusedRoot.resumeTopActivityUncheckedLocked(target, targetOptions);
+ result |= focusedRoot.resumeTopActivityUncheckedLocked(
+ target, targetOptions, false /* skipPause */);
} else if (targetRootTask == null) {
result |= resumeHomeActivity(null /* prev */, "no-focusable-task",
display.getDefaultTaskDisplayArea());
@@ -2632,7 +2633,7 @@
// process the keyguard going away, which can happen before the sleep
// token is released. As a result, it is important we resume the
// activity here.
- rootTask.resumeTopActivityUncheckedLocked(null, null);
+ rootTask.resumeTopActivityUncheckedLocked();
}
// The visibility update must not be called before resuming the top, so the
// display orientation can be updated first if needed. Otherwise there may
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 12e91ad..4340771 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4540,7 +4540,8 @@
reparent(parent, MAX_VALUE);
}
- setWindowingMode(mMultiWindowRestoreWindowingMode);
+ // mMultiWindowRestoreWindowingMode is INVALID for non-root tasks
+ setRootTaskWindowingMode(mMultiWindowRestoreWindowingMode);
}
@Override
@@ -4553,18 +4554,30 @@
return;
}
- setWindowingMode(windowingMode, false /* creating */);
+ setWindowingModeInner(windowingMode, false /* creating */);
}
/**
- * Specialization of {@link #setWindowingMode(int)} for this subclass.
+ * Version of {@link #setWindowingMode(int)} for root tasks.
*
* @param preferredWindowingMode the preferred windowing mode. This may not be honored depending
* on the state of things. For example, WINDOWING_MODE_UNDEFINED will resolve to the
* previous non-transient mode if this root task is currently in a transient mode.
+ */
+ public void setRootTaskWindowingMode(int preferredWindowingMode) {
+ if (!isRootTask()) {
+ Slog.wtf(TAG, "Trying to set root-task windowing-mode on a non-root-task: " + this,
+ new Throwable());
+ super.setWindowingMode(preferredWindowingMode);
+ return;
+ }
+ setWindowingModeInner(preferredWindowingMode, false /* creating */);
+ }
+
+ /**
* @param creating {@code true} if this is being run during task construction.
*/
- void setWindowingMode(int preferredWindowingMode, boolean creating) {
+ private void setWindowingModeInner(int preferredWindowingMode, boolean creating) {
final TaskDisplayArea taskDisplayArea = getDisplayArea();
if (taskDisplayArea == null) {
Slog.d(TAG, "taskDisplayArea is null, bail early");
@@ -4657,6 +4670,9 @@
mTransitionController.collect(topActivity);
final Task lastParentBeforePip = topActivity.getLastParentBeforePip();
+ // Reset the activity windowing mode to match the parent.
+ topActivity.getRequestedOverrideConfiguration()
+ .windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
topActivity.reparent(lastParentBeforePip,
lastParentBeforePip.getChildCount() /* top */,
"movePinnedActivityToOriginalTask");
@@ -4689,6 +4705,15 @@
// it does not follow the ActivityStarter path.
if (topActivity.shouldBeVisible()) {
mAtmService.resumeAppSwitches();
+ // In pip1, when expanding pip to full-screen, the "behind" task is not
+ // actually becoming invisible since task windowing mode is pinned.
+ if (!isPip2ExperimentEnabled) {
+ final ActivityRecord ar = mAtmService.mLastResumedActivity;
+ if (ar != null && ar.getTask() != null) {
+ mAtmService.takeTaskSnapshot(ar.getTask().mTaskId,
+ true /* updateCache */);
+ }
+ }
}
} else if (isPip2ExperimentEnabled) {
super.setWindowingMode(windowingMode);
@@ -4740,7 +4765,7 @@
}
}
if (isAttached()) {
- setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ setRootTaskWindowingMode(WINDOWING_MODE_UNDEFINED);
moveTaskToBackInner(this, null /* transition */);
}
if (top.isAttached()) {
@@ -5086,10 +5111,10 @@
return someActivityResumed;
}
- /** @see #resumeTopActivityUncheckedLocked(ActivityRecord, ActivityOptions, boolean) */
@GuardedBy("mService")
- boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
- return resumeTopActivityUncheckedLocked(prev, options, false /* skipPause */);
+ boolean resumeTopActivityUncheckedLocked() {
+ return resumeTopActivityUncheckedLocked(null /* prev */, null /* options */,
+ false /* skipPause */);
}
@GuardedBy("mService")
@@ -5138,8 +5163,7 @@
// Try to move focus to the next visible root task with a running activity if this
// root task is not covering the entire screen or is on a secondary display with
// no home root task.
- return mRootWindowContainer.resumeFocusedTasksTopActivities(nextFocusedTask,
- prev, null /* targetOptions */);
+ return mRootWindowContainer.resumeFocusedTasksTopActivities(nextFocusedTask, prev);
}
}
@@ -5661,8 +5685,10 @@
if (noAnimation) {
mDisplayContent.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(top);
- mTransitionController.collect(top);
- mTransitionController.setNoAnimation(top);
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ mTransitionController.collect(top);
+ mTransitionController.setNoAnimation(top);
+ }
ActivityOptions.abort(options);
} else {
updateTransitLocked(TRANSIT_TO_FRONT, options);
@@ -6002,7 +6028,7 @@
}
final Task task = getBottomMostTask();
- setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ setRootTaskWindowingMode(WINDOWING_MODE_UNDEFINED);
// Task could have been removed from the hierarchy due to windowing mode change
// where its only child is reparented back to their original parent task.
@@ -6674,7 +6700,7 @@
// Set windowing mode after attached to display area or it abort silently.
if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
- task.setWindowingMode(mWindowingMode, true /* creating */);
+ task.setWindowingModeInner(mWindowingMode, true /* creating */);
}
return task;
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index dba1c36..d9e88e1 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -923,7 +923,7 @@
if (windowingMode != WINDOWING_MODE_UNDEFINED && candidateTask.isRootTask()
&& candidateTask.getWindowingMode() != windowingMode) {
candidateTask.mTransitionController.collect(candidateTask);
- candidateTask.setWindowingMode(windowingMode);
+ candidateTask.setRootTaskWindowingMode(windowingMode);
}
return candidateTask.getRootTask();
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 2ee157f..329d11b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1927,7 +1927,7 @@
prev.setState(STOPPING, "completePausedLocked");
} else if (!prev.isVisibleRequested() || shouldSleepOrShutDownActivities()) {
// Clear out any deferred client hide we might currently have.
- prev.setDeferHidingClient(false);
+ prev.clearDeferHidingClient();
// If we were visible then resumeTopActivities will release resources before
// stopping.
prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
@@ -1948,8 +1948,7 @@
if (resumeNext) {
final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
- mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev,
- null /* targetOptions */);
+ mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev);
} else {
// checkReadyForSleep();
final ActivityRecord top =
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 9ee1fbe..de2e4f5 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -390,7 +390,7 @@
t.mTaskAppearedSent = false;
}
if (removeFromSystem) {
- mService.removeTask(t.mTaskId);
+ mService.removeTask(t);
}
return taskAppearedSent;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 836ac5f..b768bb1 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -420,6 +420,18 @@
updateTransientFlags(mChanges.valueAt(i));
}
}
+
+ // TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
+ // Also interpret HOME transient launch as recents
+ if (activity.isActivityTypeHomeOrRecents()) {
+ addFlag(TRANSIT_FLAG_IS_RECENTS);
+ // When starting recents animation, we assume the recents activity is behind the app
+ // task and should not affect system bar appearance,
+ // until WMS#setRecentsAppBehindSystemBars be called from launcher when passing
+ // the gesture threshold.
+ activity.getTask().setCanAffectSystemUiFlags(false);
+ }
+
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
+ "transient-launch", mSyncId, activity);
}
@@ -1721,6 +1733,7 @@
transaction.addTransactionCompletedListener(Runnable::run,
(stats) -> listener.run());
}
+ mTransactionCompletedListeners = null;
}
// Fall-back to the default display if there isn't one participating.
@@ -1763,6 +1776,7 @@
if (mPriorVisibilityMightBeDirty) {
updatePriorVisibility();
}
+
// Resolve the animating targets from the participants.
mTargets = calculateTargets(mParticipants, mChanges);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 67d7b37..ef25eda 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -19,7 +19,6 @@
import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
@@ -53,8 +52,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.ProtoLogGroup;
import com.android.server.FgThread;
import com.android.window.flags.Flags;
@@ -1311,23 +1310,6 @@
void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelowTask) {
if (mCollectingTransition == null) return;
mCollectingTransition.setTransientLaunch(activity, restoreBelowTask);
-
- // TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
- // Also interpret HOME transient launch as recents
- if (activity.isActivityTypeHomeOrRecents()) {
- mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
- // When starting recents animation, we assume the recents activity is behind the app
- // task and should not affect system bar appearance,
- // until WMS#setRecentsAppBehindSystemBars be called from launcher when passing
- // the gesture threshold.
- activity.getTask().setCanAffectSystemUiFlags(false);
- }
- }
-
- /** @see Transition#setCanPipOnFinish */
- void setCanPipOnFinish(boolean canPipOnFinish) {
- if (mCollectingTransition == null) return;
- mCollectingTransition.setCanPipOnFinish(canPipOnFinish);
}
void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
@@ -1459,7 +1441,7 @@
*
* WARNING: ONLY use this if the transition absolutely cannot be deferred!
*/
- @NonNull
+ @Nullable
Transition createAndStartCollecting(int type) {
if (mTransitionPlayers.isEmpty()) {
return null;
diff --git a/services/core/java/com/android/server/wm/TransparentPolicy.java b/services/core/java/com/android/server/wm/TransparentPolicy.java
index 39b2635..36bc846 100644
--- a/services/core/java/com/android/server/wm/TransparentPolicy.java
+++ b/services/core/java/com/android/server/wm/TransparentPolicy.java
@@ -31,6 +31,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -188,6 +189,10 @@
return mTransparentPolicyState.findOpaqueNotFinishingActivityBelow();
}
+ void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "isTransparentPolicyRunning=" + isRunning());
+ }
+
// We evaluate the case when the policy should not be applied.
private boolean shouldSkipTransparentPolicy(@Nullable ActivityRecord opaqueActivity) {
if (opaqueActivity == null || opaqueActivity.isEmbedded()) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 64f9c01..15d67eb 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1611,8 +1611,8 @@
*
* @param orientation the specified orientation.
*/
- void setOrientation(@ScreenOrientation int orientation) {
- setOrientation(orientation, null /* requestingContainer */);
+ protected void setOrientation(@ScreenOrientation int orientation) {
+ setOrientation(orientation, null /* requesting */);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5749272..243ab3a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7656,7 +7656,7 @@
displayId);
return REMOVE_CONTENT_MODE_UNDEFINED;
}
- return mDisplayWindowSettings.getRemoveContentModeLocked(displayContent);
+ return displayContent.getRemoveContentMode();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index c6d0b72..9d40b16 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -322,11 +322,11 @@
// This is a direct call from shell, so the entire transition lifecycle is
// contained in the provided transaction if provided. Thus, we can setReady
// immediately after apply.
- final Transition.ReadyCondition wctApplied =
- new Transition.ReadyCondition("start WCT applied");
final boolean needsSetReady = t != null;
final Transition nextTransition = new Transition(type, 0 /* flags */,
mTransitionController, mService.mWindowManager.mSyncEngine);
+ final Transition.ReadyCondition wctApplied =
+ new Transition.ReadyCondition("start WCT applied");
nextTransition.mReadyTracker.add(wctApplied);
nextTransition.calcParallelCollectType(wct);
mTransitionController.startCollectOrQueue(nextTransition,
@@ -342,6 +342,15 @@
});
return nextTransition.getToken();
}
+ // The transition already started collecting before sending a request to shell,
+ // so just start here.
+ if (!transition.isCollecting() && !transition.isForcePlaying()) {
+ Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
+ + " means Shell took too long to respond to a request. WM State may be"
+ + " incorrect now, please file a bug");
+ applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller);
+ return transition.getToken();
+ }
// Currently, application of wct can span multiple looper loops (ie.
// waitAsyncStart), so add a condition to ensure that it finishes applying.
final Transition.ReadyCondition wctApplied;
@@ -351,18 +360,6 @@
} else {
wctApplied = null;
}
- // The transition already started collecting before sending a request to shell,
- // so just start here.
- if (!transition.isCollecting() && !transition.isForcePlaying()) {
- Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
- + " means Shell took too long to respond to a request. WM State may be"
- + " incorrect now, please file a bug");
- applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller);
- if (wctApplied != null) {
- wctApplied.meet();
- }
- return transition.getToken();
- }
transition.mLogger.mStartWCT = wct;
if (transition.shouldApplyOnDisplayThread()) {
mService.mH.post(() -> {
@@ -851,7 +848,11 @@
}
final int prevMode = container.getRequestedOverrideWindowingMode();
- container.setWindowingMode(windowingMode);
+ if (container.asTask() != null && container.asTask().isRootTask()) {
+ container.asTask().setRootTaskWindowingMode(windowingMode);
+ } else {
+ container.setWindowingMode(windowingMode);
+ }
if (prevMode != container.getWindowingMode()) {
// The activity in the container may become focusable or non-focusable due to
// windowing modes changes (such as entering or leaving pinned windowing mode),
diff --git a/services/proguard.flags b/services/proguard.flags
index bf30781..35e27f8 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -52,9 +52,6 @@
-keep public class com.android.server.utils.Slogf { *; }
# Referenced in wear-service
-# HIDL interfaces
--keep public class android.hidl.base.** { *; }
--keep public class android.hidl.manager.** { *; }
-keep public class com.android.server.wm.WindowManagerInternal { *; }
# JNI keep rules
@@ -107,8 +104,16 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.SystemService { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.SystemService$TargetUser { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.usage.StorageStatsManagerLocal { *; }
--keep,allowoptimization,allowaccessmodification class com.android.internal.util.** { *; }
--keep,allowoptimization,allowaccessmodification class android.os.** { *; }
+
+# Prevent optimizations of any statically linked code that may shadow code in
+# the bootclasspath. See also StrictJavaPackagesTest for details on exceptions.
+# TODO(b/222468116): Resolve such collisions in the build system.
+-keep public class android.gsi.** { *; }
+-keep public class android.hidl.base.** { *; }
+-keep public class android.hidl.manager.** { *; }
+-keep public class android.os.** { *; }
+-keep public class com.android.internal.util.** { *; }
+-keep public class com.android.modules.utils.build.** { *; }
# CoverageService guards optional jacoco class references with a runtime guard, so we can safely
# suppress build-time warnings.
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 2a458c42..312df43 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -194,7 +194,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0 /* brightness= */, false /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
}
@Test
@@ -300,7 +300,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
// There should be a user data point added to the mapper.
verify(mBrightnessMappingStrategy).addUserDataPoint(/* lux= */ 1000f,
@@ -324,7 +324,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
//Recalculating the spline with RBC enabled, verifying that the short term model is reset,
//and the interaction is learnt in short term model
@@ -358,7 +358,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
/* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
/* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
@@ -398,7 +398,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0.51f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
when(mBrightnessMappingStrategy.shouldResetShortTermModel(
anyFloat(), anyFloat())).thenReturn(true);
@@ -438,7 +438,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
/* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
/* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f);
@@ -484,7 +484,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
/* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
/* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
@@ -571,7 +571,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
// There should be a user data point added to the mapper.
verify(mBrightnessMappingStrategy, times(1)).addUserDataPoint(/* lux= */ 1000f,
@@ -598,7 +598,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
// Ensure we use the correct mapping strategy
verify(mIdleBrightnessMappingStrategy, times(1)).addUserDataPoint(/* lux= */ 1000f,
@@ -759,7 +759,8 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
- Display.STATE_ON, /* shouldResetShortTermModel= */ true);
+ Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+ /* shouldResetShortTermModel= */ true);
assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f);
// The raw brightness value should not have throttling applied
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
@@ -770,7 +771,8 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
- Display.STATE_ON, /* shouldResetShortTermModel= */ true);
+ Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+ /* shouldResetShortTermModel= */ true);
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
}
@@ -907,13 +909,15 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
- Display.STATE_ON, /* shouldResetShortTermModel= */ false);
+ Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+ /* shouldResetShortTermModel= */ false);
verify(mBrightnessMappingStrategy, never()).clearUserDataPoints();
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
- Display.STATE_ON, /* shouldResetShortTermModel= */ true);
+ Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+ /* shouldResetShortTermModel= */ true);
verify(mBrightnessMappingStrategy).clearUserDataPoints();
}
@@ -1100,7 +1104,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
/* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_DOZE,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
// Send a new sensor value
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
@@ -1112,6 +1116,77 @@
}
@Test
+ public void testAutoBrightnessInDoze_useNormalBrightnessForDozeFalse_scaleScreenOn()
+ throws Exception {
+ when(mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Set up system to return 0.3f as a brightness value
+ float lux = 100.0f;
+ // Brightness as float (from 0.0f to 1.0f)
+ float normalizedBrightness = 0.3f;
+ when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
+ when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
+ when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
+ /* category= */ anyInt())).thenReturn(normalizedBrightness);
+ when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+
+ // Set policy to DOZE
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
+ /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
+ /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_ON,
+ /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
+
+ // Send a new sensor value
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+
+ // The brightness should be scaled by the doze factor
+ assertEquals(normalizedBrightness * DOZE_SCALE_FACTOR,
+ mController.getAutomaticScreenBrightness(
+ /* brightnessEvent= */ null), EPSILON);
+ }
+
+ @Test
+ public void testAutoBrightnessInDoze_useNormalBrightnessForDozeTrue_notScaleScreenOn()
+ throws Exception {
+ when(mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Set up system to return 0.3f as a brightness value
+ float lux = 100.0f;
+ // Brightness as float (from 0.0f to 1.0f)
+ float normalizedBrightness = 0.3f;
+ when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
+ when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
+ when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
+ /* category= */ anyInt())).thenReturn(normalizedBrightness);
+ when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+
+ // Set policy to DOZE
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
+ /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
+ /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_ON,
+ /* useNormalBrightnessForDoze= */ true, /* shouldResetShortTermModel= */ true);
+
+ // Send a new sensor value
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+
+ // The brightness should not be scaled by the doze factor
+ assertEquals(normalizedBrightness,
+ mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
+ }
+
+ @Test
public void testAutoBrightnessInDoze_ShouldNotScaleIfUsingDozeCurve() throws Exception {
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
@@ -1136,39 +1211,7 @@
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
/* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_DOZE,
- /* shouldResetShortTermModel= */ true);
-
- // Send a new sensor value
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
-
- // The brightness should not be scaled by the doze factor
- assertEquals(normalizedBrightness,
- mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
- }
-
- @Test
- public void testAutoBrightnessInDoze_ShouldNotScaleIfScreenOn() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- // Set up system to return 0.3f as a brightness value
- float lux = 100.0f;
- // Brightness as float (from 0.0f to 1.0f)
- float normalizedBrightness = 0.3f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
- when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
- /* category= */ anyInt())).thenReturn(normalizedBrightness);
- when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
-
- // Set policy to DOZE
- mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
- /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
- /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
+ /* useNormalBrightnessForDoze= */ true, /* shouldResetShortTermModel= */ true);
// Send a new sensor value
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index b5278a5..0bcc572 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1014,7 +1014,8 @@
/* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
/* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT,
- Display.STATE_ON, /* shouldResetShortTermModel= */ false
+ Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+ /* shouldResetShortTermModel= */ false
);
verify(mHolder.hbmController)
.setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
@@ -1040,7 +1041,8 @@
/* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
/* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
- Display.STATE_DOZE, /* shouldResetShortTermModel= */ false
+ Display.STATE_DOZE, /* useNormalBrightnessForDoze= */ false,
+ /* shouldResetShortTermModel= */ false
);
verify(mHolder.hbmController)
.setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
@@ -1070,7 +1072,8 @@
/* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
/* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
- Display.STATE_DOZE, /* shouldResetShortTermModel= */ false
+ Display.STATE_DOZE, /* useNormalBrightnessForDoze= */ false,
+ /* shouldResetShortTermModel= */ false
);
verify(mHolder.hbmController)
.setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
@@ -1093,7 +1096,8 @@
/* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
/* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT,
- Display.STATE_ON, /* shouldResetShortTermModel= */ false
+ Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+ /* shouldResetShortTermModel= */ false
);
verify(mHolder.hbmController)
.setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
@@ -1116,7 +1120,8 @@
/* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
/* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_OFF,
- Display.STATE_OFF, /* shouldResetShortTermModel= */ false
+ Display.STATE_OFF, /* useNormalBrightnessForDoze */ false,
+ /* shouldResetShortTermModel= */ false
);
verify(mHolder.hbmController).setAutoBrightnessEnabled(
AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
@@ -1142,7 +1147,8 @@
/* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
/* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
- Display.STATE_DOZE, /* shouldResetShortTermModel= */ false
+ Display.STATE_DOZE, /* useNormalBrightnessForDoze= */ false,
+ /* shouldResetShortTermModel= */ false
);
verify(mHolder.hbmController).setAutoBrightnessEnabled(
AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
@@ -1172,7 +1178,8 @@
/* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
/* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
- Display.STATE_DOZE, /* shouldResetShortTermModel= */ false
+ Display.STATE_DOZE, /* useNormalBrightnessForDoze= */ false,
+ /* shouldResetShortTermModel= */ false
);
verify(mHolder.hbmController).setAutoBrightnessEnabled(
AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
@@ -1196,7 +1203,8 @@
/* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
/* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT,
- Display.STATE_ON, /* shouldResetShortTermModel= */ false
+ Display.STATE_ON, /* useNormalBrightnessForDoze */ false,
+ /* shouldResetShortTermModel= */ false
);
// HBM should be allowed for the follower display
@@ -1955,6 +1963,7 @@
/* userChangedAutoBrightnessAdjustment= */ anyBoolean(),
/* displayPolicy= */ anyInt(),
/* displayState= */ anyInt(),
+ /* useNormalBrightnessForDoze= */ anyBoolean(),
/* shouldResetShortTermModel= */ anyBoolean());
verify(mBrightnessTrackerMock, never()).notifyBrightnessChanged(
/* brightness= */ anyFloat(),
@@ -2040,6 +2049,75 @@
}
@Test
+ public void testManualBrightness_stateOnPolicyDozeUseNormalBrightnessForDozeFalse_brightnessDoze() {
+ when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+ when(mDisplayManagerFlagsMock.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+ float brightness = 0.277f;
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
+ when(mHolder.hbmController.getCurrentBrightnessMax())
+ .thenReturn(PowerManager.BRIGHTNESS_MAX);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.dozeScreenState = Display.STATE_ON;
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ dpr.useNormalBrightnessForDoze = false;
+ // Confirm using doze brightness for dozing device.
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState, initialize
+
+ ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+ ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+ listener.onBrightnessChanged(brightness);
+ advanceTime(1); // Send messages, run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(brightness * DOZE_SCALE_FACTOR),
+ /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
+ /* ignoreAnimationLimits= */ anyBoolean());
+ }
+
+ @Test
+ public void testManualBrightness_stateOnPolicyDozeUseNormalBrightnessForDozeTrue_brightnessNormal() {
+ when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+ when(mDisplayManagerFlagsMock.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+ float brightness = 0.277f;
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
+ when(mHolder.hbmController.getCurrentBrightnessMax())
+ .thenReturn(PowerManager.BRIGHTNESS_MAX);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.dozeScreenState = Display.STATE_ON;
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ dpr.useNormalBrightnessForDoze = true;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState, initialize
+
+ ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+ ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+ listener.onBrightnessChanged(brightness);
+ advanceTime(1); // Send messages, run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(brightness),
+ /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
+ /* ignoreAnimationLimits= */ anyBoolean());
+ }
+
+ @Test
public void testDozeManualBrightness_AbcIsNull() {
when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index 639d06d..a44c517 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -187,7 +187,7 @@
}
@Test
- public void selectStrategySelectsDozeStrategyWhenValid() {
+ public void selectStrategyWhenValid_useNormalBrightnessForDozeFalse_SelectsDozeStrategy() {
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
@@ -201,6 +201,22 @@
}
@Test
+ public void selectStrategyWhenValid_useNormalBrightnessForDozeTrue_doNotSelectsDozeStrategy() {
+ when(mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+ DisplayManagerInternal.DisplayPowerRequest.class);
+ displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+ displayPowerRequest.dozeScreenBrightness = 0.2f;
+ displayPowerRequest.useNormalBrightnessForDoze = true;
+ when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
+ DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
+ assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
+ new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
+ 0.1f, false, mDisplayOffloadSession)),
+ mDozeBrightnessModeStrategy);
+ }
+
+ @Test
public void selectStrategyDoesNotSelectDozeStrategyWhenInvalidBrightness() {
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
@@ -353,7 +369,8 @@
mAutomaticBrightnessStrategy);
verify(mAutomaticBrightnessStrategy).setAutoBrightnessState(Display.STATE_ON,
true, BrightnessReason.REASON_UNKNOWN,
- DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, 0.1f, false);
+ DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT,
+ /* useNormalBrightnessForDoze= */ false, 0.1f, false);
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
index 4e10b98..e386542 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
@@ -56,6 +56,9 @@
@RunWith(AndroidJUnit4.class)
public class AutomaticBrightnessStrategy2Test {
private static final int DISPLAY_ID = 0;
+
+ private static final boolean DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE = false;
+
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@@ -78,7 +81,8 @@
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, Float.NaN);
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.5f);
- mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy2(mContext, DISPLAY_ID);
+ mAutomaticBrightnessStrategy =
+ new AutomaticBrightnessStrategy2(mContext, DISPLAY_ID);
mBrightnessConfiguration = new BrightnessConfiguration.Builder(
new float[]{0f, 1f}, new float[]{0, PowerManager.BRIGHTNESS_ON}).build();
@@ -106,15 +110,18 @@
boolean userSetBrightnessChanged = true;
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.5f,
/* userChangedAutoBrightnessAdjustment= */ false, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -130,15 +137,18 @@
boolean userSetBrightnessChanged = true;
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.5f,
/* userChangedAutoBrightnessAdjustment= */ false, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -152,17 +162,20 @@
int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
float lastUserSetBrightness = 0.2f;
boolean userSetBrightnessChanged = true;
+ boolean useNormalBrightnessForDoze = false;
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ useNormalBrightnessForDoze,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.5f,
/* userChangedAutoBrightnessAdjustment= */ false, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ targetDisplayState,
+ useNormalBrightnessForDoze, /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -178,15 +191,18 @@
boolean userSetBrightnessChanged = true;
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.5f,
/* userChangedAutoBrightnessAdjustment= */ false, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -200,19 +216,21 @@
int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
float lastUserSetBrightness = 0.2f;
boolean userSetBrightnessChanged = true;
+ boolean useNormalBrightnessForDoze = false;
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f);
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ useNormalBrightnessForDoze,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.4f,
- /* userChangedAutoBrightnessAdjustment= */ true, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+ useNormalBrightnessForDoze, /* shouldResetShortTermModel */ true);
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -226,19 +244,20 @@
int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
float lastUserSetBrightness = 0.2f;
boolean userSetBrightnessChanged = true;
+ boolean useNormalBrightnessForDoze = false;
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f);
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.4f,
- /* userChangedAutoBrightnessAdjustment= */ true, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+ useNormalBrightnessForDoze, /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -257,14 +276,16 @@
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, pendingBrightnessAdjustment,
/* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
/* shouldResetShortTermModel */ true);
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
@@ -280,19 +301,20 @@
boolean userSetBrightnessChanged = true;
int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
float pendingBrightnessAdjustment = 0.1f;
+ boolean useNormalBrightnessForDoze = false;
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, pendingBrightnessAdjustment,
/* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
- /* shouldResetShortTermModel */ true);
+ useNormalBrightnessForDoze, /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -312,13 +334,15 @@
mAutomaticBrightnessStrategy.setShouldResetShortTermModel(true);
setTemporaryAutoBrightnessAdjustment(temporaryAutoBrightnessAdjustments);
mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(userSetBrightnessChanged,
- lastUserSetScreenBrightness, policy, targetDisplayState, brightnessConfiguration,
- autoBrightnessState);
+ lastUserSetScreenBrightness, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ brightnessConfiguration, autoBrightnessState);
verify(mAutomaticBrightnessController).configure(autoBrightnessState,
brightnessConfiguration,
lastUserSetScreenBrightness,
userSetBrightnessChanged, temporaryAutoBrightnessAdjustments,
/* userChangedAutoBrightnessAdjustment= */ false, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
/* shouldResetShortTermModel= */ true);
assertTrue(mAutomaticBrightnessStrategy.isTemporaryAutoBrightnessAdjustmentApplied());
assertFalse(mAutomaticBrightnessStrategy.shouldResetShortTermModel());
@@ -328,8 +352,9 @@
mAutomaticBrightnessStrategy.setAutomaticBrightnessController(null);
mAutomaticBrightnessStrategy.setShouldResetShortTermModel(true);
mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(userSetBrightnessChanged,
- lastUserSetScreenBrightness, policy, targetDisplayState, brightnessConfiguration,
- autoBrightnessState);
+ lastUserSetScreenBrightness, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ brightnessConfiguration, autoBrightnessState);
assertFalse(mAutomaticBrightnessStrategy.isTemporaryAutoBrightnessAdjustmentApplied());
assertTrue(mAutomaticBrightnessStrategy.shouldResetShortTermModel());
assertFalse(mAutomaticBrightnessStrategy.isShortTermModelActive());
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 93ff9e1..50f814d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -23,7 +23,9 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -62,6 +64,9 @@
@RunWith(AndroidJUnit4.class)
public class AutomaticBrightnessStrategyTest {
private static final int DISPLAY_ID = 0;
+
+ private static final boolean DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE = false;
+
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@@ -116,15 +121,18 @@
boolean userSetBrightnessChanged = true;
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.5f,
/* userChangedAutoBrightnessAdjustment= */ false, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -140,15 +148,18 @@
boolean userSetBrightnessChanged = true;
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.5f,
/* userChangedAutoBrightnessAdjustment= */ false, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -164,15 +175,18 @@
boolean userSetBrightnessChanged = true;
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.5f,
/* userChangedAutoBrightnessAdjustment= */ false, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -188,15 +202,18 @@
boolean userSetBrightnessChanged = true;
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.5f,
/* userChangedAutoBrightnessAdjustment= */ false, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -214,15 +231,17 @@
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f);
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.4f,
- /* userChangedAutoBrightnessAdjustment= */ true, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ /* shouldResetShortTermModel */ true);
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -240,15 +259,17 @@
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f);
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, lastUserSetBrightness,
userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, /* adjustment */ 0.4f,
- /* userChangedAutoBrightnessAdjustment= */ true, policy,
- targetDisplayState, /* shouldResetShortTermModel */ true);
+ /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ /* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
}
@@ -267,14 +288,16 @@
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
mBrightnessConfiguration,
lastUserSetBrightness,
userSetBrightnessChanged, pendingBrightnessAdjustment,
/* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
/* shouldResetShortTermModel */ true);
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
@@ -294,7 +317,8 @@
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, lastUserSetBrightness,
userSetBrightnessChanged);
verify(mAutomaticBrightnessController)
.configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
@@ -302,6 +326,7 @@
lastUserSetBrightness,
userSetBrightnessChanged, pendingBrightnessAdjustment,
/* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
/* shouldResetShortTermModel */ true);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
@@ -318,6 +343,7 @@
boolean userSetBrightnessChanged = true;
int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
float pendingBrightnessAdjustment = 0.1f;
+ boolean useNormalBrightnessForDoze = false;
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
@@ -325,8 +351,8 @@
// Validate no interaction when automaticBrightnessController is in idle mode
when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(true);
mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController, never())
.switchMode(anyInt(), /* sendUpdate= */ anyBoolean());
@@ -334,20 +360,57 @@
// state is ON
when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(false);
mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
verify(mAutomaticBrightnessController).switchMode(
AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT,
/* sendUpdate= */ false);
- // Validate interaction when automaticBrightnessController is in non-idle mode, and display
- // state is DOZE
+ reset(mAutomaticBrightnessController);
+ when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(false);
+ when(mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+ policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+
+ // Validate interaction when automaticBrightnessController is in non-idle mode, display
+ // state is DOZE, policy is DOZE and useNormalBrightnessForDoze is false.
mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE,
- allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
- userSetBrightnessChanged);
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
+ // 1st AUTO_BRIGHTNESS_MODE_DOZE
verify(mAutomaticBrightnessController).switchMode(
AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE,
/* sendUpdate= */ false);
+
+ // Validate interaction when automaticBrightnessController is in non-idle mode, display
+ // state is ON, policy is DOZE and useNormalBrightnessForDoze is false.
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
+ // 2nd AUTO_BRIGHTNESS_MODE_DOZE
+ verify(mAutomaticBrightnessController, times(2)).switchMode(
+ AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE,
+ /* sendUpdate= */ false);
+
+ useNormalBrightnessForDoze = true;
+ // Validate interaction when automaticBrightnessController is in non-idle mode, display
+ // state is DOZE, policy is DOZE and useNormalBrightnessForDoze is true.
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
+ // 1st AUTO_BRIGHTNESS_MODE_DEFAULT
+ verify(mAutomaticBrightnessController).switchMode(
+ AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* sendUpdate= */ false);
+
+ // Validate interaction when automaticBrightnessController is in non-idle mode, display
+ // state is ON, policy is DOZE and useNormalBrightnessForDoze is true.
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy,
+ useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
+ // 2nd AUTO_BRIGHTNESS_MODE_DEFAULT
+ verify(mAutomaticBrightnessController, times(2)).switchMode(
+ AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* sendUpdate= */ false);
}
@Test
@@ -365,13 +428,15 @@
mAutomaticBrightnessStrategy.setShouldResetShortTermModel(true);
setTemporaryAutoBrightnessAdjustment(temporaryAutoBrightnessAdjustments);
mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(userSetBrightnessChanged,
- lastUserSetScreenBrightness, policy, targetDisplayState, brightnessConfiguration,
- autoBrightnessState);
+ lastUserSetScreenBrightness, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ brightnessConfiguration, autoBrightnessState);
verify(mAutomaticBrightnessController).configure(autoBrightnessState,
brightnessConfiguration,
lastUserSetScreenBrightness,
userSetBrightnessChanged, temporaryAutoBrightnessAdjustments,
/* userChangedAutoBrightnessAdjustment= */ false, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
/* shouldResetShortTermModel= */ true);
assertTrue(mAutomaticBrightnessStrategy.isTemporaryAutoBrightnessAdjustmentApplied());
assertFalse(mAutomaticBrightnessStrategy.shouldResetShortTermModel());
@@ -381,8 +446,9 @@
mAutomaticBrightnessStrategy.setAutomaticBrightnessController(null);
mAutomaticBrightnessStrategy.setShouldResetShortTermModel(true);
mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(userSetBrightnessChanged,
- lastUserSetScreenBrightness, policy, targetDisplayState, brightnessConfiguration,
- autoBrightnessState);
+ lastUserSetScreenBrightness, policy, targetDisplayState,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ brightnessConfiguration, autoBrightnessState);
assertFalse(mAutomaticBrightnessStrategy.isTemporaryAutoBrightnessAdjustmentApplied());
assertTrue(mAutomaticBrightnessStrategy.shouldResetShortTermModel());
assertFalse(mAutomaticBrightnessStrategy.isShortTermModelActive());
@@ -504,8 +570,9 @@
public void isAutoBrightnessValid_returnsFalseWhenBrightnessIsInvalid() {
mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, true,
BrightnessReason.REASON_UNKNOWN,
- DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, 0.1f,
- false);
+ DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, /* lastUserSetScreenBrightness= */ 0.1f,
+ /* userSetBrightnessChanged= */ false);
when(mAutomaticBrightnessController.getAutomaticScreenBrightness(null))
.thenReturn(Float.NaN);
assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessValid());
@@ -520,8 +587,9 @@
.thenReturn(0.1f);
mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, true,
BrightnessReason.REASON_UNKNOWN,
- DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, 0.1f,
- false);
+ DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT,
+ DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, /* lastUserSetScreenBrightness= */ 0.1f,
+ /* userSetBrightnessChanged= */ false);
when(mAutomaticBrightnessController.getAutomaticScreenBrightness(null))
.thenReturn(0.2f);
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessValid());
@@ -594,7 +662,8 @@
mAutomaticBrightnessStrategy.setTemporaryAutoBrightnessAdjustment(temporaryBrightness);
mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(true,
brightness, DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT,
- Display.STATE_ON, mock(BrightnessConfiguration.class),
+ Display.STATE_ON, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+ mock(BrightnessConfiguration.class),
AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
when(mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment()).thenReturn(
autoBrightnessAdjustment);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
index ee79d19..5e240cf 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -23,6 +23,7 @@
import static com.android.server.display.mode.DisplayModeDirector.SYNCHRONIZED_REFRESH_RATE_TOLERANCE;
import static com.android.server.display.mode.Vote.PRIORITY_LIMIT_MODE;
import static com.android.server.display.mode.Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE;
+import static com.android.server.display.mode.Vote.PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE;
import static com.android.server.display.mode.Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE;
import static com.android.server.display.mode.VotesStorage.GLOBAL_ID;
@@ -360,16 +361,22 @@
.thenReturn(true);
init();
assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(
Vote.forPhysicalRefreshRates(
MAX_REFRESH_RATE - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
MAX_REFRESH_RATE + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(
+ Vote.forRenderFrameRates(
+ MAX_REFRESH_RATE - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
+ MAX_REFRESH_RATE + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
// Remove external display and check that sync vote is no longer present.
mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
}
/** External display added, disabled feature refresh rates synchronization */
@@ -383,8 +390,10 @@
.thenReturn(true);
init();
assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
}
/** External display not applied refresh rates synchronization, because
@@ -397,8 +406,10 @@
when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(true);
init();
assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
}
private void init() {
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
index 39def75..473c8c5 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
@@ -79,8 +79,6 @@
private static final float BRIGHTNESS = 0.99f;
private static final float BRIGHTNESS_DOZE = 0.5f;
-
-
private PowerGroup mPowerGroup;
@Mock private PowerGroup.PowerGroupListener mWakefulnessCallbackMock;
@Mock private Notifier mNotifier;
@@ -264,6 +262,7 @@
/* dozeScreenStateOverride= */ Display.STATE_ON,
/* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
/* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+ /* useNormalBrightnessForDoze= */ false,
/* overrideDrawWakeLock= */ false,
powerSaveState,
/* quiescent= */ false,
@@ -282,6 +281,7 @@
assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
brightnessFactor);
@@ -289,6 +289,7 @@
@Test
public void testUpdateWhileDozing_UpdatesDisplayPowerRequest() {
+ final boolean useNormalBrightnessForDoze = false;
final boolean batterySaverEnabled = false;
float brightnessFactor = 0.3f;
PowerSaveState powerSaveState = new PowerSaveState.Builder()
@@ -306,6 +307,7 @@
/* dozeScreenStateOverride= */ Display.STATE_ON,
/* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
/* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+ useNormalBrightnessForDoze,
/* overrideDrawWakeLock= */ false,
powerSaveState,
/* quiescent= */ false,
@@ -323,6 +325,51 @@
assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_ON);
assertThat(displayPowerRequest.dozeScreenBrightness).isWithin(PRECISION).of(
BRIGHTNESS_DOZE);
+ assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
+ assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
+ assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
+ brightnessFactor);
+ }
+
+ @Test
+ public void testUpdateWhileDozing_useNormalBrightness() {
+ final boolean batterySaverEnabled = false;
+ final boolean useNormalBrightnessForDoze = true;
+ float brightnessFactor = 0.3f;
+ PowerSaveState powerSaveState = new PowerSaveState.Builder()
+ .setBatterySaverEnabled(batterySaverEnabled)
+ .setBrightnessFactor(brightnessFactor)
+ .build();
+ mPowerGroup.dozeLocked(TIMESTAMP1, UID, GO_TO_SLEEP_REASON_APPLICATION);
+ assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
+ mPowerGroup.setWakeLockSummaryLocked(WAKE_LOCK_DOZE);
+
+ mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+ /* overrideTag= */ null,
+ /* useProximitySensor= */ true,
+ /* boostScreenBrightness= */ true,
+ /* dozeScreenStateOverride= */ Display.STATE_ON,
+ /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
+ /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+ useNormalBrightnessForDoze,
+ /* overrideDrawWakeLock= */ false,
+ powerSaveState,
+ /* quiescent= */ false,
+ /* dozeAfterScreenOff= */ false,
+ /* bootCompleted= */ true,
+ /* screenBrightnessBoostInProgress= */ false,
+ /* waitForNegativeProximity= */ false,
+ /* brightWhenDozing= */ false);
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+ mPowerGroup.mDisplayPowerRequest;
+ assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DOZE);
+ assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
+ assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
+ assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
+ assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_ON);
+ assertThat(displayPowerRequest.dozeScreenBrightness).isWithin(PRECISION).of(
+ BRIGHTNESS_DOZE);
+ assertThat(displayPowerRequest.useNormalBrightnessForDoze).isTrue();
assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
brightnessFactor);
@@ -346,6 +393,7 @@
/* dozeScreenStateOverride= */ Display.STATE_ON,
/* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
/* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+ /* useNormalBrightnessForDoze= */ false,
/* overrideDrawWakeLock= */ false,
powerSaveState,
/* quiescent= */ false,
@@ -363,6 +411,7 @@
assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
brightnessFactor);
@@ -385,6 +434,7 @@
/* dozeScreenStateOverride= */ Display.STATE_ON,
/* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
/* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+ /* useNormalBrightnessForDoze= */ false,
/* overrideDrawWakeLock= */ false,
powerSaveState,
/* quiescent= */ true,
@@ -402,6 +452,7 @@
assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
brightnessFactor);
@@ -424,6 +475,7 @@
/* dozeScreenStateOverride= */ Display.STATE_ON,
/* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
/* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+ /* useNormalBrightnessForDoze= */ false,
/* overrideDrawWakeLock= */ false,
powerSaveState,
/* quiescent= */ false,
@@ -441,6 +493,7 @@
assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
brightnessFactor);
@@ -464,6 +517,7 @@
/* dozeScreenStateOverride= */ Display.STATE_ON,
/* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
/* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+ /* useNormalBrightnessForDoze= */ false,
/* overrideDrawWakeLock= */ false,
powerSaveState,
/* quiescent= */ false,
@@ -481,6 +535,7 @@
assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
brightnessFactor);
@@ -502,6 +557,7 @@
/* dozeScreenStateOverride= */ Display.STATE_ON,
/* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
/* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+ /* useNormalBrightnessForDoze= */ false,
/* overrideDrawWakeLock= */ false,
powerSaveState,
/* quiescent= */ false,
@@ -519,6 +575,7 @@
assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
brightnessFactor);
@@ -541,6 +598,7 @@
/* dozeScreenStateOverride= */ Display.STATE_ON,
/* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
/* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+ /* useNormalBrightnessForDoze= */ false,
/* overrideDrawWakeLock= */ false,
powerSaveState,
/* quiescent= */ false,
@@ -558,6 +616,7 @@
assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
brightnessFactor);
@@ -579,6 +638,7 @@
/* dozeScreenStateOverride= */ Display.STATE_ON,
/* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
/* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+ /* useNormalBrightnessForDoze= */ false,
/* overrideDrawWakeLock= */ false,
powerSaveState,
/* quiescent= */ false,
@@ -596,6 +656,7 @@
assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
brightnessFactor);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index 40c521a..b58c28b 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -1305,7 +1305,8 @@
Display.STATE_ON,
Display.STATE_REASON_DEFAULT_POLICY,
PowerManager.BRIGHTNESS_INVALID_FLOAT,
- PowerManager.BRIGHTNESS_DEFAULT);
+ PowerManager.BRIGHTNESS_DEFAULT,
+ /* useDozeBrightness= */ false);
assertTrue(isAcquired[0]);
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index a86289b..701c350 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -272,6 +272,10 @@
"$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
}
+FLAKY = [
+ "androidx.test.filters.FlakyTest",
+]
+
FLAKY_AND_IGNORED = [
"androidx.test.filters.FlakyTest",
"org.junit.Ignore",
@@ -328,7 +332,7 @@
base: "FrameworksServicesTests",
test_suites: ["device-tests"],
include_filters: ["com.android.server.recoverysystem."],
- exclude_annotations: ["androidx.test.filters.FlakyTest"],
+ exclude_annotations: FLAKY,
}
// server pm TEST_MAPPING
@@ -357,3 +361,319 @@
test_suites: ["device-tests"],
include_filters: ["com.android.server.os."],
}
+
+test_module_config {
+ name: "FrameworksServicesTests_presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+ exclude_annotations: FLAKY_AND_IGNORED,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_com_android_server_job_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.job"],
+ exclude_annotations: [
+ "androidx.test.filters.LargeTest",
+ "androidx.test.filters.FlakyTest",
+ ],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_com_android_server_job",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.job"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_com_android_server_tare_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.tare"],
+ exclude_annotations: FLAKY,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_com_android_server_tare",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.tare"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_com_android_server_usage_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.usage"],
+ exclude_annotations: FLAKY,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_com_android_server_usage",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.usage"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_battery_stats",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.am.BatteryStatsServiceTest"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_accessibility_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.accessibility"],
+ exclude_annotations: FLAKY,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_accessibility",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.accessibility"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_binary_transparency",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.BinaryTransparencyServiceTest"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_pinner_service",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.PinnerServiceTest"],
+ exclude_annotations: ["org.junit.Ignore"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_am_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.am."],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+ exclude_annotations: FLAKY,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_am",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.am."],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_appop",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.appop"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_audio",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.audio"],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+ exclude_annotations: FLAKY_AND_IGNORED,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_compat",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.compat"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_hdmi_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.hdmi"],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+ exclude_annotations: FLAKY_AND_IGNORED,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_hdmi",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.hdmi"],
+ exclude_annotations: ["org.junit.Ignore"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_integrity",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.integrity."],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_lights",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.lights"],
+ exclude_annotations: FLAKY,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_locales",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.locales."],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_location_contexthub_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.location.contexthub."],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+ exclude_annotations: FLAKY_AND_IGNORED,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_locksettings",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.locksettings."],
+ exclude_annotations: FLAKY,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_logcat_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.logcat"],
+ exclude_annotations: FLAKY,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_logcat",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.logcat"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_net_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.net."],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+ exclude_annotations: FLAKY,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_om",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.om."],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_pdb",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.pdb.PersistentDataBlockServiceTest"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_pm_dex",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.pm.dex"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_policy_Presubmit",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.policy."],
+ include_annotations: ["android.platform.test.annotations.Presubmit"],
+ exclude_annotations: FLAKY,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_policy",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.policy."],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_power",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.power"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_power_hint",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.power.hint"],
+ exclude_annotations: FLAKY,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_powerstats",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.powerstats"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_rollback",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.rollback"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_uri",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.uri."],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_com_android_server_location_contexthub",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.location.contexthub."],
+ include_annotations: ["android.platform.test.annotations.Postsubmit"],
+ exclude_annotations: FLAKY_AND_IGNORED,
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_usage",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.usage"],
+ exclude_filters: ["com.android.server.usage.StorageStatsServiceTest"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_soundtrigger_middleware",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.soundtrigger_middleware"],
+}
+
+test_module_config {
+ name: "FrameworksServicesTests_android_server_input",
+ base: "FrameworksServicesTests",
+ test_suites: ["device-tests"],
+ include_filters: ["com.android.server.input"],
+}
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS b/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS
index 24561c5..3d09da3 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS
@@ -1,3 +1,4 @@
-# Bug component: 544200
+# Bug component: 1040349
-include /core/java/android/view/contentcapture/OWNERS
+include /core/java/android/view/contentprotection/OWNERS
+
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 643ee4a..62e5b9a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -2007,6 +2007,25 @@
}
@Test
+ public void testCanInterruptNonRingtoneInsistentBuzzWithOtherBuzzyNotification() {
+ NotificationRecord r = getInsistentBuzzyNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyVibrateLooped();
+ assertTrue(r.isInterruptive());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ Mockito.reset(mVibrator);
+
+ // New buzzy notification stops previous looping vibration
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);
+ verifyStopVibrate();
+ // And then vibrates itself
+ verifyVibrate(1);
+ assertTrue(interrupter.isInterruptive());
+ assertNotEquals(-1, interrupter.getLastAudiblyAlertedMs());
+ }
+
+ @Test
public void testRingtoneInsistentBeep_doesNotBlockFutureSoundsOnceStopped() throws Exception {
NotificationChannel ringtoneChannel =
new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyboardSystemShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
similarity index 69%
rename from services/tests/wmtests/src/com/android/server/policy/KeyboardSystemShortcutTests.java
rename to services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index e26f3e0..8f3adba 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyboardSystemShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -22,7 +22,7 @@
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL;
import static com.android.server.policy.PhoneWindowManager.SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL;
-import android.hardware.input.KeyboardSystemShortcut;
+import android.hardware.input.KeyGestureEvent;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -44,7 +44,7 @@
@Presubmit
@MediumTest
@RunWith(JUnitParamsRunner.class)
-public class KeyboardSystemShortcutTests extends ShortcutKeyTestBase {
+public class KeyGestureEventTests extends ShortcutKeyTestBase {
@Rule
public final CheckFlagsRule mCheckFlagsRule =
@@ -56,315 +56,313 @@
private static final int ALT_ON = MODIFIER.get(KeyEvent.KEYCODE_ALT_LEFT);
private static final int CTRL_KEY = KeyEvent.KEYCODE_CTRL_LEFT;
private static final int CTRL_ON = MODIFIER.get(KeyEvent.KEYCODE_CTRL_LEFT);
- private static final int SHIFT_KEY = KeyEvent.KEYCODE_SHIFT_LEFT;
- private static final int SHIFT_ON = MODIFIER.get(KeyEvent.KEYCODE_SHIFT_LEFT);
@Keep
private static Object[][] shortcutTestArguments() {
- // testName, testKeys, expectedSystemShortcut, expectedKey, expectedModifierState
+ // testName, testKeys, expectedKeyGestureType, expectedKey, expectedModifierState
return new Object[][]{
{"Meta + H -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_H},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME, KeyEvent.KEYCODE_H, META_ON},
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME, KeyEvent.KEYCODE_H, META_ON},
{"Meta + Enter -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME, KeyEvent.KEYCODE_ENTER,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME, KeyEvent.KEYCODE_ENTER,
META_ON},
{"HOME key -> Open Home", new int[]{KeyEvent.KEYCODE_HOME},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
KeyEvent.KEYCODE_HOME, 0},
{"RECENT_APPS key -> Open Overview", new int[]{KeyEvent.KEYCODE_RECENT_APPS},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
KeyEvent.KEYCODE_RECENT_APPS, 0},
{"Meta + Tab -> Open Overview", new int[]{META_KEY, KeyEvent.KEYCODE_TAB},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS, KeyEvent.KEYCODE_TAB,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, KeyEvent.KEYCODE_TAB,
META_ON},
{"Alt + Tab -> Open Overview", new int[]{ALT_KEY, KeyEvent.KEYCODE_TAB},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS, KeyEvent.KEYCODE_TAB,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, KeyEvent.KEYCODE_TAB,
ALT_ON},
{"BACK key -> Go back", new int[]{KeyEvent.KEYCODE_BACK},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
KeyEvent.KEYCODE_BACK, 0},
{"Meta + Escape -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_ESCAPE},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK, KeyEvent.KEYCODE_ESCAPE,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_ESCAPE,
META_ON},
{"Meta + Left arrow -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK, KeyEvent.KEYCODE_DPAD_LEFT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_DPAD_LEFT,
META_ON},
{"Meta + Del -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DEL},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK, KeyEvent.KEYCODE_DEL, META_ON},
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_DEL, META_ON},
{"APP_SWITCH key -> Open App switcher", new int[]{KeyEvent.KEYCODE_APP_SWITCH},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH,
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
KeyEvent.KEYCODE_APP_SWITCH, 0},
{"ASSIST key -> Launch assistant", new int[]{KeyEvent.KEYCODE_ASSIST},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
KeyEvent.KEYCODE_ASSIST, 0},
{"Meta + A -> Launch assistant", new int[]{META_KEY, KeyEvent.KEYCODE_A},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_A,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_A,
META_ON},
{"VOICE_ASSIST key -> Launch Voice Assistant",
new int[]{KeyEvent.KEYCODE_VOICE_ASSIST},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT,
KeyEvent.KEYCODE_VOICE_ASSIST, 0},
{"Meta + I -> Launch System Settings", new int[]{META_KEY, KeyEvent.KEYCODE_I},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
KeyEvent.KEYCODE_I, META_ON},
{"Meta + N -> Toggle Notification panel", new int[]{META_KEY, KeyEvent.KEYCODE_N},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
KeyEvent.KEYCODE_N, META_ON},
{"NOTIFICATION key -> Toggle Notification Panel",
new int[]{KeyEvent.KEYCODE_NOTIFICATION},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
KeyEvent.KEYCODE_NOTIFICATION,
0},
{"Meta + Ctrl + S -> Take Screenshot",
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_S},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TAKE_SCREENSHOT, KeyEvent.KEYCODE_S,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, KeyEvent.KEYCODE_S,
META_ON | CTRL_ON},
{"Meta + / -> Open Shortcut Helper", new int[]{META_KEY, KeyEvent.KEYCODE_SLASH},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER,
+ KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
KeyEvent.KEYCODE_SLASH, META_ON},
{"BRIGHTNESS_UP key -> Increase Brightness",
new int[]{KeyEvent.KEYCODE_BRIGHTNESS_UP},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_BRIGHTNESS_UP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
KeyEvent.KEYCODE_BRIGHTNESS_UP, 0},
{"BRIGHTNESS_DOWN key -> Decrease Brightness",
new int[]{KeyEvent.KEYCODE_BRIGHTNESS_DOWN},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_BRIGHTNESS_DOWN,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
KeyEvent.KEYCODE_BRIGHTNESS_DOWN, 0},
{"KEYBOARD_BACKLIGHT_UP key -> Increase Keyboard Backlight",
new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP, 0},
{"KEYBOARD_BACKLIGHT_DOWN key -> Decrease Keyboard Backlight",
new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN, 0},
{"KEYBOARD_BACKLIGHT_TOGGLE key -> Toggle Keyboard Backlight",
new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE, 0},
{"VOLUME_UP key -> Increase Volume", new int[]{KeyEvent.KEYCODE_VOLUME_UP},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_UP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_UP,
KeyEvent.KEYCODE_VOLUME_UP, 0},
{"VOLUME_DOWN key -> Decrease Volume", new int[]{KeyEvent.KEYCODE_VOLUME_DOWN},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_DOWN,
+ KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_DOWN,
KeyEvent.KEYCODE_VOLUME_DOWN, 0},
{"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_MUTE,
+ KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_MUTE,
KeyEvent.KEYCODE_VOLUME_MUTE, 0},
{"ALL_APPS key -> Open App Drawer in Accessibility mode",
new int[]{KeyEvent.KEYCODE_ALL_APPS},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
KeyEvent.KEYCODE_ALL_APPS, 0},
{"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_SEARCH,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
KeyEvent.KEYCODE_SEARCH, 0},
{"LANGUAGE_SWITCH key -> Switch Keyboard Language",
new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LANGUAGE_SWITCH,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
{"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS, META_KEY,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS, META_KEY,
META_ON},
{"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK, ALT_KEY,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, ALT_KEY,
META_ON | ALT_ON},
{"Alt + Meta -> Toggle CapsLock", new int[]{ALT_KEY, META_KEY},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK, META_KEY,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, META_KEY,
META_ON | ALT_ON},
{"CAPS_LOCK key -> Toggle CapsLock", new int[]{KeyEvent.KEYCODE_CAPS_LOCK},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
KeyEvent.KEYCODE_CAPS_LOCK, 0},
{"MUTE key -> Mute System Microphone", new int[]{KeyEvent.KEYCODE_MUTE},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_MUTE, KeyEvent.KEYCODE_MUTE,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_MUTE, KeyEvent.KEYCODE_MUTE,
0},
{"Meta + Ctrl + DPAD_UP -> Split screen navigation",
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_UP},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
KeyEvent.KEYCODE_DPAD_UP,
META_ON | CTRL_ON},
{"Meta + Ctrl + DPAD_LEFT -> Split screen navigation",
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
KeyEvent.KEYCODE_DPAD_LEFT,
META_ON | CTRL_ON},
{"Meta + Ctrl + DPAD_RIGHT -> Split screen navigation",
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_RIGHT},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
KeyEvent.KEYCODE_DPAD_RIGHT,
META_ON | CTRL_ON},
{"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LOCK_SCREEN, KeyEvent.KEYCODE_L,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN, KeyEvent.KEYCODE_L,
META_ON},
{"Meta + Ctrl + N -> Open Notes", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_N},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_OPEN_NOTES, KeyEvent.KEYCODE_N,
+ KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, KeyEvent.KEYCODE_N,
META_ON | CTRL_ON},
{"POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_POWER},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_POWER, KeyEvent.KEYCODE_POWER,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER, KeyEvent.KEYCODE_POWER,
0},
{"TV_POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_TV_POWER},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_POWER,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER,
KeyEvent.KEYCODE_TV_POWER, 0},
{"SYSTEM_NAVIGATION_DOWN key -> System Navigation",
new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_NAVIGATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
0},
{"SYSTEM_NAVIGATION_UP key -> System Navigation",
new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_NAVIGATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
0},
{"SYSTEM_NAVIGATION_LEFT key -> System Navigation",
new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_NAVIGATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT,
0},
{"SYSTEM_NAVIGATION_RIGHT key -> System Navigation",
new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_NAVIGATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT, 0},
{"SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SLEEP},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SLEEP, KeyEvent.KEYCODE_SLEEP, 0},
+ KeyGestureEvent.KEY_GESTURE_TYPE_SLEEP, KeyEvent.KEYCODE_SLEEP, 0},
{"SOFT_SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SOFT_SLEEP},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_SLEEP, KeyEvent.KEYCODE_SOFT_SLEEP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SLEEP, KeyEvent.KEYCODE_SOFT_SLEEP,
0},
{"WAKEUP key -> System Wakeup", new int[]{KeyEvent.KEYCODE_WAKEUP},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_WAKEUP, KeyEvent.KEYCODE_WAKEUP, 0},
+ KeyGestureEvent.KEY_GESTURE_TYPE_WAKEUP, KeyEvent.KEYCODE_WAKEUP, 0},
{"MEDIA_PLAY key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PLAY},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_MEDIA_KEY,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY,
KeyEvent.KEYCODE_MEDIA_PLAY, 0},
{"MEDIA_PAUSE key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PAUSE},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_MEDIA_KEY,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY,
KeyEvent.KEYCODE_MEDIA_PAUSE, 0},
{"MEDIA_PLAY_PAUSE key -> Media Control",
new int[]{KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_MEDIA_KEY,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY,
KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0},
{"Meta + B -> Launch Default Browser", new int[]{META_KEY, KeyEvent.KEYCODE_B},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
KeyEvent.KEYCODE_B, META_ON},
{"EXPLORER key -> Launch Default Browser", new int[]{KeyEvent.KEYCODE_EXPLORER},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
KeyEvent.KEYCODE_EXPLORER, 0},
{"Meta + C -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_C},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
KeyEvent.KEYCODE_C, META_ON},
{"CONTACTS key -> Launch Default Contacts", new int[]{KeyEvent.KEYCODE_CONTACTS},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
KeyEvent.KEYCODE_CONTACTS, 0},
{"Meta + E -> Launch Default Email", new int[]{META_KEY, KeyEvent.KEYCODE_E},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
KeyEvent.KEYCODE_E, META_ON},
{"ENVELOPE key -> Launch Default Email", new int[]{KeyEvent.KEYCODE_ENVELOPE},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
KeyEvent.KEYCODE_ENVELOPE, 0},
{"Meta + K -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_K},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
KeyEvent.KEYCODE_K, META_ON},
{"CALENDAR key -> Launch Default Calendar", new int[]{KeyEvent.KEYCODE_CALENDAR},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
KeyEvent.KEYCODE_CALENDAR, 0},
{"Meta + P -> Launch Default Music", new int[]{META_KEY, KeyEvent.KEYCODE_P},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
KeyEvent.KEYCODE_P, META_ON},
{"MUSIC key -> Launch Default Music", new int[]{KeyEvent.KEYCODE_MUSIC},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
KeyEvent.KEYCODE_MUSIC, 0},
{"Meta + U -> Launch Default Calculator", new int[]{META_KEY, KeyEvent.KEYCODE_U},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
KeyEvent.KEYCODE_U, META_ON},
{"CALCULATOR key -> Launch Default Calculator",
new int[]{KeyEvent.KEYCODE_CALCULATOR},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
KeyEvent.KEYCODE_CALCULATOR, 0},
{"Meta + M -> Launch Default Maps", new int[]{META_KEY, KeyEvent.KEYCODE_M},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS,
KeyEvent.KEYCODE_M, META_ON},
{"Meta + S -> Launch Default Messaging App",
new int[]{META_KEY, KeyEvent.KEYCODE_S},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
KeyEvent.KEYCODE_S, META_ON},
{"Meta + Ctrl + DPAD_DOWN -> Enter desktop mode",
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_DOWN},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_DESKTOP_MODE,
+ KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
KeyEvent.KEYCODE_DPAD_DOWN,
META_ON | CTRL_ON}};
}
@Keep
private static Object[][] longPressOnHomeTestArguments() {
- // testName, testKeys, longPressOnHomeBehavior, expectedSystemShortcut, expectedKey,
+ // testName, testKeys, longPressOnHomeBehavior, expectedKeyGestureType, expectedKey,
// expectedModifierState
return new Object[][]{
{"Long press HOME key -> Toggle Notification panel",
new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_NOTIFICATION_PANEL,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
KeyEvent.KEYCODE_HOME, 0},
{"Long press META + ENTER -> Toggle Notification panel",
new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
LONG_PRESS_HOME_NOTIFICATION_PANEL,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
KeyEvent.KEYCODE_ENTER,
META_ON},
{"Long press META + H -> Toggle Notification panel",
new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_NOTIFICATION_PANEL,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
KeyEvent.KEYCODE_H, META_ON},
{"Long press HOME key -> Launch assistant",
new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ASSIST,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
KeyEvent.KEYCODE_HOME, 0},
{"Long press META + ENTER -> Launch assistant",
new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ASSIST,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
KeyEvent.KEYCODE_ENTER, META_ON},
{"Long press META + H -> Launch assistant",
new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_ASSIST,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_H,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_H,
META_ON},
{"Long press HOME key -> Open App Drawer in Accessibility mode",
new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ALL_APPS,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
KeyEvent.KEYCODE_HOME, 0},
{"Long press META + ENTER -> Open App Drawer in Accessibility mode",
new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ALL_APPS,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
KeyEvent.KEYCODE_ENTER, META_ON},
{"Long press META + H -> Open App Drawer in Accessibility mode",
new int[]{META_KEY, KeyEvent.KEYCODE_H},
LONG_PRESS_HOME_ALL_APPS,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
KeyEvent.KEYCODE_H, META_ON}};
}
@Keep
private static Object[][] doubleTapOnHomeTestArguments() {
- // testName, testKeys, doubleTapOnHomeBehavior, expectedSystemShortcut, expectedKey,
+ // testName, testKeys, doubleTapOnHomeBehavior, expectedKeyGestureType, expectedKey,
// expectedModifierState
return new Object[][]{
{"Double tap HOME -> Open App switcher",
new int[]{KeyEvent.KEYCODE_HOME}, DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH, KeyEvent.KEYCODE_HOME,
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH, KeyEvent.KEYCODE_HOME,
0},
{"Double tap META + ENTER -> Open App switcher",
new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH,
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
KeyEvent.KEYCODE_ENTER, META_ON},
{"Double tap META + H -> Open App switcher",
new int[]{META_KEY, KeyEvent.KEYCODE_H}, DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH, KeyEvent.KEYCODE_H,
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH, KeyEvent.KEYCODE_H,
META_ON}};
}
@Keep
private static Object[][] settingsKeyTestArguments() {
- // testName, testKeys, settingsKeyBehavior, expectedSystemShortcut, expectedKey,
+ // testName, testKeys, settingsKeyBehavior, expectedKeyGestureType, expectedKey,
// expectedModifierState
return new Object[][]{
{"SETTINGS key -> Toggle Notification panel", new int[]{KeyEvent.KEYCODE_SETTINGS},
SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
KeyEvent.KEYCODE_SETTINGS, 0}};
}
@@ -387,21 +385,21 @@
@Test
@Parameters(method = "shortcutTestArguments")
public void testShortcut(String testName, int[] testKeys,
- @KeyboardSystemShortcut.SystemShortcut int expectedSystemShortcut, int expectedKey,
+ @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
int expectedModifierState) {
- testShortcutInternal(testName, testKeys, expectedSystemShortcut, expectedKey,
+ testShortcutInternal(testName, testKeys, expectedKeyGestureType, expectedKey,
expectedModifierState);
}
@Test
@Parameters(method = "longPressOnHomeTestArguments")
public void testLongPressOnHome(String testName, int[] testKeys, int longPressOnHomeBehavior,
- @KeyboardSystemShortcut.SystemShortcut int expectedSystemShortcut, int expectedKey,
+ @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
int expectedModifierState) {
mPhoneWindowManager.overrideLongPressOnHomeBehavior(longPressOnHomeBehavior);
sendLongPressKeyCombination(testKeys);
- mPhoneWindowManager.assertKeyboardShortcutTriggered(
- new int[]{expectedKey}, expectedModifierState, expectedSystemShortcut,
+ mPhoneWindowManager.assertKeyGestureCompleted(
+ new int[]{expectedKey}, expectedModifierState, expectedKeyGestureType,
"Failed while executing " + testName);
}
@@ -409,23 +407,23 @@
@Parameters(method = "doubleTapOnHomeTestArguments")
public void testDoubleTapOnHomeBehavior(String testName, int[] testKeys,
int doubleTapOnHomeBehavior,
- @KeyboardSystemShortcut.SystemShortcut int expectedSystemShortcut, int expectedKey,
+ @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
int expectedModifierState) {
mPhoneWindowManager.overriderDoubleTapOnHomeBehavior(doubleTapOnHomeBehavior);
sendKeyCombination(testKeys, 0 /* duration */);
sendKeyCombination(testKeys, 0 /* duration */);
- mPhoneWindowManager.assertKeyboardShortcutTriggered(
- new int[]{expectedKey}, expectedModifierState, expectedSystemShortcut,
+ mPhoneWindowManager.assertKeyGestureCompleted(
+ new int[]{expectedKey}, expectedModifierState, expectedKeyGestureType,
"Failed while executing " + testName);
}
@Test
@Parameters(method = "settingsKeyTestArguments")
public void testSettingsKey(String testName, int[] testKeys, int settingsKeyBehavior,
- @KeyboardSystemShortcut.SystemShortcut int expectedSystemShortcut, int expectedKey,
+ @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
int expectedModifierState) {
mPhoneWindowManager.overrideSettingsKeyBehavior(settingsKeyBehavior);
- testShortcutInternal(testName, testKeys, expectedSystemShortcut, expectedKey,
+ testShortcutInternal(testName, testKeys, expectedKeyGestureType, expectedKey,
expectedModifierState);
}
@@ -434,16 +432,16 @@
public void testBugreportShortcutPress() {
testShortcutInternal("Meta + Ctrl + Del -> Trigger bug report",
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DEL},
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT, KeyEvent.KEYCODE_DEL,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT, KeyEvent.KEYCODE_DEL,
META_ON | CTRL_ON);
}
private void testShortcutInternal(String testName, int[] testKeys,
- @KeyboardSystemShortcut.SystemShortcut int expectedSystemShortcut, int expectedKey,
+ @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
int expectedModifierState) {
sendKeyCombination(testKeys, 0 /* duration */);
- mPhoneWindowManager.assertKeyboardShortcutTriggered(
- new int[]{expectedKey}, expectedModifierState, expectedSystemShortcut,
+ mPhoneWindowManager.assertKeyGestureCompleted(
+ new int[]{expectedKey}, expectedModifierState, expectedKeyGestureType,
"Failed while executing " + testName);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index f9b5c2a..43b065d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -70,7 +70,6 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputManager;
-import android.hardware.input.KeyboardSystemShortcut;
import android.media.AudioManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
@@ -804,11 +803,11 @@
Assert.assertEquals(targetActivity, intentCaptor.getValue().getComponent());
}
- void assertKeyboardShortcutTriggered(int[] keycodes, int modifierState, int systemShortcut,
+ void assertKeyGestureCompleted(int[] keycodes, int modifierState, int gestureType,
String errorMsg) {
mTestLooper.dispatchAll();
- verify(mInputManagerInternal, description(errorMsg)).notifyKeyboardShortcutTriggered(
- anyInt(), eq(keycodes), eq(modifierState), eq(systemShortcut));
+ verify(mInputManagerInternal, description(errorMsg)).notifyKeyGestureCompleted(
+ anyInt(), eq(keycodes), eq(modifierState), eq(gestureType));
}
void assertSwitchToTask(int persistentId) throws RemoteException {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
index 14fbbe4..cb17f35f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
@@ -212,7 +212,6 @@
.build()
.getTopMostActivity();
- spyOn(mActivity.mLetterboxUiController);
spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
doReturn(true).when(mActivity).inFreeformWindowingMode();
doReturn(true).when(mActivity.mAppCompatController
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index a745724..1fa6868 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -420,7 +420,6 @@
*/
@CallSuper
void onPostActivityCreation(@NonNull ActivityRecord activity) {
- spyOn(activity.mLetterboxUiController);
if (mOnPostActivityCreation != null) {
mOnPostActivityCreation.accept(activity);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index 84ffcb8..ba2a733 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -374,7 +374,6 @@
* Runs a test scenario providing a Robot.
*/
void runTestScenario(@NonNull Consumer<CameraOverridesRobotTest> consumer) {
- spyOn(mWm.mAppCompatConfiguration);
final CameraOverridesRobotTest robot = new CameraOverridesRobotTest(mWm, mAtm, mSupervisor);
consumer.accept(robot);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
index c42228d..2ae23f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
@@ -20,6 +20,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import android.compat.testing.PlatformCompatChangeRule;
@@ -29,7 +31,6 @@
import androidx.annotation.NonNull;
-import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -53,18 +54,28 @@
@Test
public void testDisplayRotationCompatPolicy_presentWhenEnabled() {
runTestScenario((robot) -> {
- robot.conf().enableCameraCompatTreatmentAtBuildTime(true);
+ robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
robot.activity().createActivityWithComponentInNewTaskAndDisplay();
- robot.checkTopActivityHasDisplayRotationCompatPolicy(true);
+ robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ true);
});
}
@Test
public void testDisplayRotationCompatPolicy_notPresentWhenDisabled() {
runTestScenario((robot) -> {
- robot.conf().enableCameraCompatTreatmentAtBuildTime(false);
+ robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ false);
robot.activity().createActivityWithComponentInNewTaskAndDisplay();
- robot.checkTopActivityHasDisplayRotationCompatPolicy(false);
+ robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ false);
+ });
+ }
+
+ @Test
+ public void testDisplayRotationCompatPolicy_startedWhenEnabled() {
+ runTestScenario((robot) -> {
+ robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
+ robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+ robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ true);
+ robot.checkTopActivityDisplayRotationCompatPolicyIsRunning();
});
}
@@ -72,9 +83,9 @@
@EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
public void testCameraCompatFreeformPolicy_presentWhenEnabledAndDW() {
runTestScenario((robot) -> {
- robot.allowEnterDesktopMode(true);
+ robot.allowEnterDesktopMode(/* isAllowed= */ true);
robot.activity().createActivityWithComponentInNewTaskAndDisplay();
- robot.checkTopActivityHasCameraCompatFreeformPolicy(true);
+ robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
});
}
@@ -82,9 +93,9 @@
@EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
public void testCameraCompatFreeformPolicy_notPresentWhenNoDW() {
runTestScenario((robot) -> {
- robot.allowEnterDesktopMode(false);
+ robot.allowEnterDesktopMode(/* isAllowed= */ false);
robot.activity().createActivityWithComponentInNewTaskAndDisplay();
- robot.checkTopActivityHasCameraCompatFreeformPolicy(false);
+ robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
});
}
@@ -92,9 +103,9 @@
@DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
public void testCameraCompatFreeformPolicy_notPresentWhenNoFlag() {
runTestScenario((robot) -> {
- robot.allowEnterDesktopMode(true);
+ robot.allowEnterDesktopMode(/* isAllowed= */ true);
robot.activity().createActivityWithComponentInNewTaskAndDisplay();
- robot.checkTopActivityHasCameraCompatFreeformPolicy(false);
+ robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
});
}
@@ -102,19 +113,86 @@
@EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
public void testCameraCompatFreeformPolicy_notPresentWhenNoFlagAndNoDW() {
runTestScenario((robot) -> {
- robot.allowEnterDesktopMode(false);
+ robot.allowEnterDesktopMode(/* isAllowed= */ false);
robot.activity().createActivityWithComponentInNewTaskAndDisplay();
- robot.checkTopActivityHasCameraCompatFreeformPolicy(false);
+ robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
+ });
+ }
+
+ @Test
+ @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+ public void testCameraCompatFreeformPolicy_startedWhenEnabledAndDW() {
+ runTestScenario((robot) -> {
+ robot.allowEnterDesktopMode(/* isAllowed= */ true);
+ robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+ robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
+ robot.checkTopActivityCameraCompatFreeformPolicyIsRunning();
+ });
+ }
+
+ @Test
+ @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+ public void testCameraStateManager_existsWhenCameraCompatFreeformExists() {
+ runTestScenario((robot) -> {
+ robot.allowEnterDesktopMode(true);
+ robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+ robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
+ robot.checkTopActivityHasCameraStateMonitor(/* exists= */ true);
+ });
+ }
+
+ @Test
+ @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+ public void testCameraStateManager_startedWhenCameraCompatFreeformExists() {
+ runTestScenario((robot) -> {
+ robot.allowEnterDesktopMode(true);
+ robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+ robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
+ robot.checkTopActivityHasCameraStateMonitor(/* exists= */ true);
+ robot.checkTopActivityCameraStateMonitorIsRunning();
+ });
+ }
+
+ @Test
+ public void testCameraStateManager_existsWhenDisplayRotationCompatPolicyExists() {
+ runTestScenario((robot) -> {
+ robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
+ robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+ robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ true);
+ robot.checkTopActivityHasCameraStateMonitor(/* exists= */ true);
+ });
+ }
+
+ @Test
+ public void testCameraStateManager_startedWhenDisplayRotationCompatPolicyExists() {
+ runTestScenario((robot) -> {
+ robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
+ robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+ robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ true);
+ robot.checkTopActivityHasCameraStateMonitor(/* exists= */ true);
+ robot.checkTopActivityCameraStateMonitorIsRunning();
+ });
+ }
+
+ @Test
+ @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+ public void testCameraStateManager_doesNotExistWhenNoPolicyExists() {
+ runTestScenario((robot) -> {
+ robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ false);
+ robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+ robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ false);
+ robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
+ robot.checkTopActivityHasCameraStateMonitor(/* exists= */ false);
});
}
/**
* Runs a test scenario providing a Robot.
*/
- void runTestScenario(@NonNull Consumer<DisplayRotationPolicyRobotTest> consumer) {
+ void runTestScenario(@NonNull Consumer<AppCompatCameraPolicyRobotTest> consumer) {
spyOn(mWm.mAppCompatConfiguration);
- final DisplayRotationPolicyRobotTest robot =
- new DisplayRotationPolicyRobotTest(mWm, mAtm, mSupervisor);
+ final AppCompatCameraPolicyRobotTest robot =
+ new AppCompatCameraPolicyRobotTest(mWm, mAtm, mSupervisor);
consumer.accept(robot);
}
@@ -142,9 +220,8 @@
});
}
- private static class DisplayRotationPolicyRobotTest extends AppCompatRobotBase {
-
- DisplayRotationPolicyRobotTest(@NonNull WindowManagerService wm,
+ private static class AppCompatCameraPolicyRobotTest extends AppCompatRobotBase {
+ AppCompatCameraPolicyRobotTest(@NonNull WindowManagerService wm,
@NonNull ActivityTaskManagerService atm,
@NonNull ActivityTaskSupervisor supervisor) {
super(wm, atm, supervisor);
@@ -157,17 +234,37 @@
}
void checkTopActivityHasDisplayRotationCompatPolicy(boolean exists) {
- Assert.assertEquals(exists, activity().top().mDisplayContent
- .mAppCompatCameraPolicy.hasDisplayRotationCompatPolicy());
+ assertEquals(exists, activity().top().mDisplayContent.mAppCompatCameraPolicy
+ .hasDisplayRotationCompatPolicy());
}
void checkTopActivityHasCameraCompatFreeformPolicy(boolean exists) {
- Assert.assertEquals(exists, activity().top().mDisplayContent
- .mAppCompatCameraPolicy.hasCameraCompatFreeformPolicy());
+ assertEquals(exists, activity().top().mDisplayContent.mAppCompatCameraPolicy
+ .hasCameraCompatFreeformPolicy());
+ }
+
+ void checkTopActivityHasCameraStateMonitor(boolean exists) {
+ assertEquals(exists, activity().top().mDisplayContent.mAppCompatCameraPolicy
+ .hasCameraStateMonitor());
+ }
+
+ void checkTopActivityDisplayRotationCompatPolicyIsRunning() {
+ assertTrue(activity().top().mDisplayContent.mAppCompatCameraPolicy
+ .mDisplayRotationCompatPolicy.isRunning());
+ }
+
+ void checkTopActivityCameraCompatFreeformPolicyIsRunning() {
+ assertTrue(activity().top().mDisplayContent.mAppCompatCameraPolicy
+ .mCameraCompatFreeformPolicy.isRunning());
+ }
+
+ void checkTopActivityCameraStateMonitorIsRunning() {
+ assertTrue(activity().top().mDisplayContent.mAppCompatCameraPolicy
+ .mCameraStateMonitor.isRunning());
}
void checkIsCameraCompatTreatmentActiveForTopActivity(boolean active) {
- Assert.assertEquals(getTopAppCompatCameraPolicy()
+ assertEquals(getTopAppCompatCameraPolicy()
.isTreatmentEnabledForActivity(activity().top()), active);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java
index 27c5e4e..d8f8453 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java
@@ -19,8 +19,6 @@
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
import android.compat.testing.PlatformCompatChangeRule;
import android.platform.test.annotations.Presubmit;
@@ -177,7 +175,6 @@
* Runs a test scenario providing a Robot.
*/
void runTestScenario(@NonNull Consumer<FocusOverridesRobotTest> consumer) {
- spyOn(mWm.mAppCompatConfiguration);
final FocusOverridesRobotTest robot = new FocusOverridesRobotTest(mWm, mAtm, mSupervisor);
consumer.accept(robot);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
index f6d0744..9057b6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -514,7 +514,6 @@
*/
void runTestScenario(boolean withActivity,
@NonNull Consumer<OrientationPolicyRobotTest> consumer) {
- spyOn(mWm.mAppCompatConfiguration);
final OrientationPolicyRobotTest robot =
new OrientationPolicyRobotTest(mWm, mAtm, mSupervisor, withActivity);
consumer.accept(robot);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
index 5ff8f02..1edbcd5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
@@ -164,7 +164,6 @@
* Runs a test scenario providing a Robot.
*/
void runTestScenario(@NonNull Consumer<ReachabilityOverridesRobotTest> consumer) {
- spyOn(mWm.mAppCompatConfiguration);
final ReachabilityOverridesRobotTest robot =
new ReachabilityOverridesRobotTest(mWm, mAtm, mSupervisor);
consumer.accept(robot);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java
index 96734b3..ddc4de9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java
@@ -228,7 +228,6 @@
* Runs a test scenario providing a Robot.
*/
void runTestScenario(@NonNull Consumer<ReachabilityPolicyRobotTest> consumer) {
- spyOn(mWm.mAppCompatConfiguration);
final ReachabilityPolicyRobotTest robot =
new ReachabilityPolicyRobotTest(mWm, mAtm, mSupervisor);
consumer.accept(robot);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
index cade213..b8d554b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
@@ -20,8 +20,6 @@
import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
import android.compat.testing.PlatformCompatChangeRule;
import android.platform.test.annotations.Presubmit;
@@ -171,7 +169,6 @@
* Runs a test scenario providing a Robot.
*/
void runTestScenario(@NonNull Consumer<ResizeOverridesRobotTest> consumer) {
- spyOn(mWm.mAppCompatConfiguration);
final ResizeOverridesRobotTest robot = new ResizeOverridesRobotTest(mWm, mAtm, mSupervisor);
consumer.accept(robot);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index f2592d2..5a3ae76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -276,7 +276,6 @@
.setTask(mTask)
.build();
- spyOn(mActivity.mLetterboxUiController);
spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
spyOn(mActivity.info);
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
index d223272..ad80f82 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
@@ -189,7 +189,6 @@
.build();
spyOn(mActivity.mAtmService.getLifecycleManager());
- spyOn(mActivity.mLetterboxUiController);
doReturn(mActivity).when(mDisplayContent).topRunningActivity(anyBoolean());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index ab0c8d4..ec5e51e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -2656,6 +2656,7 @@
* display.
*/
@Test
+ @SuppressWarnings("GuardedBy")
public void testNotResumeHomeRootTaskOnRemovingDisplay() {
// Create a display which supports system decoration and allows reparenting root tasks to
// another display when the display is removed.
@@ -2678,7 +2679,8 @@
// The removed display should have no focused root task and its home root task should never
// resume.
assertNull(display.getFocusedRootTask());
- verify(homeRootTask, never()).resumeTopActivityUncheckedLocked(any(), any());
+ verify(homeRootTask, never()).resumeTopActivityUncheckedLocked(
+ any(), any(), anyBoolean());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index 2dea6ba..8cf593f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -597,7 +597,6 @@
.build();
spyOn(mActivity.mAtmService.getLifecycleManager());
- spyOn(mActivity.mLetterboxUiController);
spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
doReturn(mActivity).when(mDisplayContent).topRunningActivity(anyBoolean());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index ddadbc4..57839e2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -309,6 +309,16 @@
public void testPublicDisplayDefaultToMoveToPrimary() {
assertEquals(REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY,
mDisplayWindowSettings.getRemoveContentModeLocked(mSecondaryDisplay));
+
+ // Sets the remove-content-mode and make sure the mode is updated.
+ mDisplayWindowSettings.setRemoveContentModeLocked(mSecondaryDisplay,
+ REMOVE_CONTENT_MODE_DESTROY);
+ final int removeContentMode = mDisplayWindowSettings.getRemoveContentModeLocked(
+ mSecondaryDisplay);
+ assertEquals(REMOVE_CONTENT_MODE_DESTROY, removeContentMode);
+
+ doReturn(removeContentMode).when(mSecondaryDisplay).getRemoveContentMode();
+ assertTrue(mSecondaryDisplay.shouldDestroyContentOnRemove());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index cf321ed..8947522 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -77,7 +77,6 @@
private ActivityRecord mActivity;
private Task mTask;
private DisplayContent mDisplayContent;
- private LetterboxUiController mController;
private AppCompatConfiguration mAppCompatConfiguration;
private final Rect mLetterboxedPortraitTaskBounds = new Rect();
@@ -87,8 +86,6 @@
mAppCompatConfiguration = mWm.mAppCompatConfiguration;
spyOn(mAppCompatConfiguration);
-
- mController = new LetterboxUiController(mWm, mActivity);
}
@Test
@@ -276,10 +273,13 @@
final Resources resources = mWm.mContext.getResources();
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
+ final AppCompatAspectRatioPolicy aspectRatioPolicy = mActivity.mAppCompatController
+ .getAppCompatAspectRatioPolicy();
+
mainWindow.mInvGlobalScale = 1f;
spyOn(resources);
spyOn(mActivity);
- spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy());
+ spyOn(aspectRatioPolicy);
if (taskbar != null) {
taskbar.setVisible(true);
@@ -288,8 +288,8 @@
doReturn(mLetterboxedPortraitTaskBounds).when(mActivity).getBounds();
doReturn(false).when(mActivity).isInLetterboxAnimation();
doReturn(true).when(mActivity).isVisible();
- doReturn(true).when(mActivity.mAppCompatController
- .getAppCompatAspectRatioPolicy()).isLetterboxedForFixedOrientationAndAspectRatio();
+ doReturn(true).when(aspectRatioPolicy)
+ .isLetterboxedForFixedOrientationAndAspectRatio();
doReturn(insets).when(mainWindow).getInsetsState();
doReturn(attrs).when(mainWindow).getAttrs();
doReturn(true).when(mainWindow).isDrawn();
@@ -300,9 +300,6 @@
doReturn(TASKBAR_EXPANDED_HEIGHT).when(resources).getDimensionPixelSize(
R.dimen.taskbar_frame_height);
- // Need to reinitialise due to the change in resources getDimensionPixelSize output.
- mController = new LetterboxUiController(mWm, mActivity);
-
return mainWindow;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index d29505f..84c2c32 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -54,6 +54,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.refEq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -598,7 +599,7 @@
mRootWindowContainer.applySleepTokens(true);
verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleeping();
verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
- null /* target */, null /* targetOptions */);
+ isNull() /* target */, isNull() /* targetOptions */, eq(false) /* deferPause */);
}
@Test
@@ -790,8 +791,7 @@
doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
// Use the task as target to resume.
- mRootWindowContainer.resumeFocusedTasksTopActivities(rootTask, activity,
- null /* targetOptions */);
+ mRootWindowContainer.resumeFocusedTasksTopActivities(rootTask, activity);
// Verify the target task should resume its activity.
verify(rootTask, times(1)).resumeTopActivityUncheckedLocked(
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 36696a1..65e3baa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -3017,6 +3017,7 @@
}
@Test
+ @SuppressWarnings("GuardedBy")
public void testDisplayIgnoreOrientationRequest_pausedAppNotLostSizeCompat() {
// Set up a display in landscape and ignoring orientation request.
setUpDisplaySizeWithApp(2800, 1400);
@@ -3043,7 +3044,8 @@
assertActivityMaxBoundsSandboxed();
final Rect activityBounds = new Rect(mActivity.getBounds());
- mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
+ mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */,
+ false /* deferPause */);
// App still in size compat, and the bounds don't change.
verify(mActivity, never()).clearSizeCompatMode();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 18255b8..f56825f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -265,7 +265,7 @@
rootTask.setTaskOrganizer(null);
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
- verify(mWm.mAtmService).removeTask(eq(rootTask.mTaskId));
+ verify(mWm.mAtmService).removeTask(eq(rootTask));
}
@Test
@@ -283,7 +283,7 @@
rootTask.setTaskOrganizer(null);
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
- verify(mWm.mAtmService, never()).removeTask(eq(rootTask.mTaskId));
+ verify(mWm.mAtmService, never()).removeTask(eq(rootTask));
}
@Test
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
index 65e8e13..ddf5ef2 100644
--- a/telephony/java/android/telephony/CarrierRestrictionRules.java
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -463,8 +463,9 @@
public String toString() {
return "CarrierRestrictionRules(allowed:" + mAllowedCarriers + ", excluded:"
+ mExcludedCarriers + ", default:" + mCarrierRestrictionDefault
- + ", MultiSim policy:" + mMultiSimPolicy + getCarrierInfoList() +
- " mIsCarrierLockInfoSupported = " + mUseCarrierLockInfo + ")";
+ + ", MultiSim policy:" + mMultiSimPolicy + getCarrierInfoList()
+ + ", mIsCarrierLockInfoSupported = " + mUseCarrierLockInfo
+ + getCarrierRestrictionStatusToLog() + ")";
}
private String getCarrierInfoList() {
@@ -476,6 +477,13 @@
}
}
+ private String getCarrierRestrictionStatusToLog() {
+ if(android.os.Build.isDebuggable()) {
+ return ", CarrierRestrictionStatus = " + mCarrierRestrictionStatus;
+ }
+ return "";
+ }
+
/**
* Builder for a {@link CarrierRestrictionRules}.
*/
diff --git a/telephony/java/android/telephony/satellite/ISatelliteProvisionStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteProvisionStateCallback.aidl
index f981fb1..5f0d986 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteProvisionStateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteProvisionStateCallback.aidl
@@ -16,6 +16,8 @@
package android.telephony.satellite;
+import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
+
/**
* Interface for satellite provision state callback.
* @hide
@@ -27,4 +29,14 @@
* @param provisioned True means the service is provisioned and false means it is not.
*/
void onSatelliteProvisionStateChanged(in boolean provisioned);
+
+ /**
+ * Called when the provisioning state of one or more SatelliteSubscriberInfos changes.
+ *
+ * @param satelliteSubscriberProvisionStatus The List contains the latest provisioning states of
+ * the SatelliteSubscriberInfos.
+ * @hide
+ */
+ void onSatelliteSubscriptionProvisionStateChanged(in List<SatelliteSubscriberProvisionStatus>
+ satelliteSubscriberProvisionStatus);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index e657d7f..0c98327 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -1404,6 +1404,16 @@
() -> callback.onSatelliteProvisionStateChanged(
provisioned)));
}
+
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @Override
+ public void onSatelliteSubscriptionProvisionStateChanged(
+ @NonNull List<SatelliteSubscriberProvisionStatus>
+ satelliteSubscriberProvisionStatus) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onSatelliteSubscriptionProvisionStateChanged(
+ satelliteSubscriberProvisionStatus)));
+ }
};
sSatelliteProvisionStateCallbackMap.put(callback, internalCallback);
return telephony.registerForSatelliteProvisionStateChanged(
diff --git a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
index a12952b..e8ae0f5 100644
--- a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
@@ -17,10 +17,13 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import com.android.internal.telephony.flags.Flags;
+import java.util.List;
+
/**
* A callback class for monitoring satellite provision state change events.
*
@@ -39,4 +42,16 @@
*/
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSatelliteProvisionStateChanged(boolean provisioned);
+
+ /**
+ * Called when the provisioning state of one or more SatelliteSubscriberInfos changes.
+ *
+ * @param satelliteSubscriberProvisionStatus The List contains the latest provisioning states
+ * of the SatelliteSubscriberInfos.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ default void onSatelliteSubscriptionProvisionStateChanged(
+ @NonNull List<SatelliteSubscriberProvisionStatus>
+ satelliteSubscriberProvisionStatus) {};
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.aidl b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.aidl
new file mode 100644
index 0000000..80de779
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+parcelable SatelliteSubscriberProvisionStatus;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
new file mode 100644
index 0000000..e3d619e
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * Represents the provisioning state of SatelliteSubscriberInfo.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+public class SatelliteSubscriberProvisionStatus implements Parcelable {
+ private SatelliteSubscriberInfo mSubscriberInfo;
+ /** {@code true} mean the satellite subscriber is provisioned, {@code false} otherwise. */
+ private boolean mProvisionStatus;
+
+ public SatelliteSubscriberProvisionStatus(@NonNull Builder builder) {
+ mSubscriberInfo = builder.mSubscriberInfo;
+ mProvisionStatus = builder.mProvisionStatus;
+ }
+
+ /**
+ * Builder class for constructing SatelliteSubscriberProvisionStatus objects
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public static class Builder {
+ private SatelliteSubscriberInfo mSubscriberInfo;
+ private boolean mProvisionStatus;
+
+ /**
+ * Set the SatelliteSubscriberInfo and returns the Builder class.
+ * @hide
+ */
+ public Builder setSatelliteSubscriberInfo(SatelliteSubscriberInfo satelliteSubscriberInfo) {
+ mSubscriberInfo = satelliteSubscriberInfo;
+ return this;
+ }
+
+ /**
+ * Set the SatelliteSubscriberInfo's provisionStatus and returns the Builder class.
+ * @hide
+ */
+ @NonNull
+ public Builder setProvisionStatus(boolean provisionStatus) {
+ mProvisionStatus = provisionStatus;
+ return this;
+ }
+
+ /**
+ * Returns SatelliteSubscriberProvisionStatus object.
+ * @hide
+ */
+ @NonNull
+ public SatelliteSubscriberProvisionStatus build() {
+ return new SatelliteSubscriberProvisionStatus(this);
+ }
+ }
+
+ private SatelliteSubscriberProvisionStatus(Parcel in) {
+ readFromParcel(in);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ mSubscriberInfo.writeToParcel(out, flags);
+ out.writeBoolean(mProvisionStatus);
+ }
+
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public static final @android.annotation.NonNull Creator<SatelliteSubscriberProvisionStatus>
+ CREATOR =
+ new Creator<SatelliteSubscriberProvisionStatus>() {
+ @Override
+ public SatelliteSubscriberProvisionStatus createFromParcel(Parcel in) {
+ return new SatelliteSubscriberProvisionStatus(in);
+ }
+
+ @Override
+ public SatelliteSubscriberProvisionStatus[] newArray(int size) {
+ return new SatelliteSubscriberProvisionStatus[size];
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * SatelliteSubscriberInfo that has a provisioning state.
+ * @return SatelliteSubscriberInfo.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public @NonNull SatelliteSubscriberInfo getSatelliteSubscriberInfo() {
+ return mSubscriberInfo;
+ }
+
+ /**
+ * SatelliteSubscriberInfo's provisioning state.
+ * @return {@code true} means provisioning. {@code false} means deprovisioning.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public @NonNull boolean getProvisionStatus() {
+ return mProvisionStatus;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("SatelliteSubscriberInfo:");
+ sb.append(mSubscriberInfo);
+ sb.append(",");
+
+ sb.append("ProvisionStatus:");
+ sb.append(mProvisionStatus);
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSubscriberInfo, mProvisionStatus);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SatelliteSubscriberProvisionStatus)) return false;
+ SatelliteSubscriberProvisionStatus that = (SatelliteSubscriberProvisionStatus) o;
+ return Objects.equals(mSubscriberInfo, that.mSubscriberInfo)
+ && mProvisionStatus == that.mProvisionStatus;
+ }
+
+ private void readFromParcel(Parcel in) {
+ mSubscriberInfo = in.readParcelable(SatelliteSubscriberInfo.class.getClassLoader(),
+ SatelliteSubscriberInfo.class);
+ mProvisionStatus = in.readBoolean();
+ }
+}
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
new file mode 100644
index 0000000..14aac66
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.KeyEvent
+import androidx.test.core.app.ApplicationProvider
+import com.android.server.testutils.any
+import org.junit.After
+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
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.fail
+
+/**
+ * Tests for [InputManager.KeyGestureEventListener].
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyGestureEventListenerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class KeyGestureEventListenerTest {
+
+ companion object {
+ const val DEVICE_ID = 1
+ val HOME_GESTURE_EVENT = KeyGestureEvent(
+ DEVICE_ID,
+ intArrayOf(KeyEvent.KEYCODE_H),
+ KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+ )
+ }
+
+ @get:Rule
+ val rule = SetFlagsRule()
+
+ private val testLooper = TestLooper()
+ private val executor = HandlerExecutor(Handler(testLooper.looper))
+ private var registeredListener: IKeyGestureEventListener? = null
+ private lateinit var context: Context
+ private lateinit var inputManager: InputManager
+ private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+
+ @Mock
+ private lateinit var iInputManagerMock: IInputManager
+
+ @Before
+ fun setUp() {
+ context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
+ inputManager = InputManager(context)
+ `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
+ .thenReturn(inputManager)
+
+ // Handle key gesture event listener registration.
+ doAnswer {
+ val listener = it.getArgument(0) as IKeyGestureEventListener
+ if (registeredListener != null &&
+ registeredListener!!.asBinder() != listener.asBinder()) {
+ // There can only be one registered key gesture event listener per process.
+ fail("Trying to register a new listener when one already exists")
+ }
+ registeredListener = listener
+ null
+ }.`when`(iInputManagerMock).registerKeyGestureEventListener(any())
+
+ // Handle key gesture event listener being unregistered.
+ doAnswer {
+ val listener = it.getArgument(0) as IKeyGestureEventListener
+ if (registeredListener == null ||
+ registeredListener!!.asBinder() != listener.asBinder()) {
+ fail("Trying to unregister a listener that is not registered")
+ }
+ registeredListener = null
+ null
+ }.`when`(iInputManagerMock).unregisterKeyGestureEventListener(any())
+ }
+
+ @After
+ fun tearDown() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ }
+
+ private fun notifyKeyGestureEvent(event: KeyGestureEvent) {
+ registeredListener!!.onKeyGestureEvent(
+ event.deviceId,
+ event.keycodes,
+ event.modifierState,
+ event.keyGestureType
+ )
+ }
+
+ @Test
+ fun testListenerHasCorrectGestureNotified() {
+ var callbackCount = 0
+
+ // Add a key gesture event listener
+ inputManager.registerKeyGestureEventListener(executor) {
+ event: KeyGestureEvent ->
+ assertEquals(HOME_GESTURE_EVENT, event)
+ callbackCount++
+ }
+
+ // Notifying key gesture event will notify the listener.
+ notifyKeyGestureEvent(HOME_GESTURE_EVENT)
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount)
+ }
+
+ @Test
+ fun testAddingListenersRegistersInternalCallbackListener() {
+ // Set up two callbacks.
+ val callback1 = InputManager.KeyGestureEventListener { _ -> }
+ val callback2 = InputManager.KeyGestureEventListener { _ -> }
+
+ assertNull(registeredListener)
+
+ // Adding the listener should register the callback with InputManagerService.
+ inputManager.registerKeyGestureEventListener(executor, callback1)
+ assertNotNull(registeredListener)
+
+ // Adding another listener should not register new internal listener.
+ val currListener = registeredListener
+ inputManager.registerKeyGestureEventListener(executor, callback2)
+ assertEquals(currListener, registeredListener)
+ }
+
+ @Test
+ fun testRemovingListenersUnregistersInternalCallbackListener() {
+ // Set up two callbacks.
+ val callback1 = InputManager.KeyGestureEventListener { _ -> }
+ val callback2 = InputManager.KeyGestureEventListener { _ -> }
+
+ inputManager.registerKeyGestureEventListener(executor, callback1)
+ inputManager.registerKeyGestureEventListener(executor, callback2)
+
+ // Only removing all listeners should remove the internal callback
+ inputManager.unregisterKeyGestureEventListener(callback1)
+ assertNotNull(registeredListener)
+ inputManager.unregisterKeyGestureEventListener(callback2)
+ assertNull(registeredListener)
+ }
+
+ @Test
+ fun testMultipleListeners() {
+ // Set up two callbacks.
+ var callbackCount1 = 0
+ var callbackCount2 = 0
+ val callback1 = InputManager.KeyGestureEventListener { _ -> callbackCount1++ }
+ val callback2 = InputManager.KeyGestureEventListener { _ -> callbackCount2++ }
+
+ // Add both key gesture event listeners
+ inputManager.registerKeyGestureEventListener(executor, callback1)
+ inputManager.registerKeyGestureEventListener(executor, callback2)
+
+ // Notifying key gesture event, should notify both the callbacks.
+ notifyKeyGestureEvent(HOME_GESTURE_EVENT)
+ testLooper.dispatchAll()
+ assertEquals(1, callbackCount1)
+ assertEquals(1, callbackCount2)
+
+ inputManager.unregisterKeyGestureEventListener(callback2)
+ // Notifying key gesture event, should still trigger callback1 but not
+ // callback2.
+ notifyKeyGestureEvent(HOME_GESTURE_EVENT)
+ testLooper.dispatchAll()
+ assertEquals(2, callbackCount1)
+ assertEquals(1, callbackCount2)
+ }
+}
diff --git a/tests/Input/src/android/hardware/input/KeyboardSystemShortcutListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardSystemShortcutListenerTest.kt
deleted file mode 100644
index 24d7291..0000000
--- a/tests/Input/src/android/hardware/input/KeyboardSystemShortcutListenerTest.kt
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.input
-
-import android.content.Context
-import android.content.ContextWrapper
-import android.os.Handler
-import android.os.HandlerExecutor
-import android.os.test.TestLooper
-import android.platform.test.annotations.Presubmit
-import android.platform.test.flag.junit.SetFlagsRule
-import android.view.KeyEvent
-import androidx.test.core.app.ApplicationProvider
-import com.android.server.testutils.any
-import org.junit.After
-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
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnitRunner
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import kotlin.test.fail
-
-/**
- * Tests for [InputManager.KeyboardSystemShortcutListener].
- *
- * Build/Install/Run:
- * atest InputTests:KeyboardSystemShortcutListenerTest
- */
-@Presubmit
-@RunWith(MockitoJUnitRunner::class)
-class KeyboardSystemShortcutListenerTest {
-
- companion object {
- const val DEVICE_ID = 1
- val HOME_SHORTCUT = KeyboardSystemShortcut(
- intArrayOf(KeyEvent.KEYCODE_H),
- KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME
- )
- }
-
- @get:Rule
- val rule = SetFlagsRule()
-
- private val testLooper = TestLooper()
- private val executor = HandlerExecutor(Handler(testLooper.looper))
- private var registeredListener: IKeyboardSystemShortcutListener? = null
- private lateinit var context: Context
- private lateinit var inputManager: InputManager
- private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
- @Mock
- private lateinit var iInputManagerMock: IInputManager
-
- @Before
- fun setUp() {
- context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
- inputManager = InputManager(context)
- `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
- .thenReturn(inputManager)
-
- // Handle keyboard system shortcut listener registration.
- doAnswer {
- val listener = it.getArgument(0) as IKeyboardSystemShortcutListener
- if (registeredListener != null &&
- registeredListener!!.asBinder() != listener.asBinder()) {
- // There can only be one registered keyboard system shortcut listener per process.
- fail("Trying to register a new listener when one already exists")
- }
- registeredListener = listener
- null
- }.`when`(iInputManagerMock).registerKeyboardSystemShortcutListener(any())
-
- // Handle keyboard system shortcut listener being unregistered.
- doAnswer {
- val listener = it.getArgument(0) as IKeyboardSystemShortcutListener
- if (registeredListener == null ||
- registeredListener!!.asBinder() != listener.asBinder()) {
- fail("Trying to unregister a listener that is not registered")
- }
- registeredListener = null
- null
- }.`when`(iInputManagerMock).unregisterKeyboardSystemShortcutListener(any())
- }
-
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
- }
-
- private fun notifyKeyboardSystemShortcutTriggered(id: Int, shortcut: KeyboardSystemShortcut) {
- registeredListener!!.onKeyboardSystemShortcutTriggered(
- id,
- shortcut.keycodes,
- shortcut.modifierState,
- shortcut.systemShortcut
- )
- }
-
- @Test
- fun testListenerHasCorrectSystemShortcutNotified() {
- var callbackCount = 0
-
- // Add a keyboard system shortcut listener
- inputManager.registerKeyboardSystemShortcutListener(executor) {
- deviceId: Int, systemShortcut: KeyboardSystemShortcut ->
- assertEquals(DEVICE_ID, deviceId)
- assertEquals(HOME_SHORTCUT, systemShortcut)
- callbackCount++
- }
-
- // Notifying keyboard system shortcut triggered will notify the listener.
- notifyKeyboardSystemShortcutTriggered(DEVICE_ID, HOME_SHORTCUT)
- testLooper.dispatchNext()
- assertEquals(1, callbackCount)
- }
-
- @Test
- fun testAddingListenersRegistersInternalCallbackListener() {
- // Set up two callbacks.
- val callback1 = InputManager.KeyboardSystemShortcutListener {_, _ -> }
- val callback2 = InputManager.KeyboardSystemShortcutListener {_, _ -> }
-
- assertNull(registeredListener)
-
- // Adding the listener should register the callback with InputManagerService.
- inputManager.registerKeyboardSystemShortcutListener(executor, callback1)
- assertNotNull(registeredListener)
-
- // Adding another listener should not register new internal listener.
- val currListener = registeredListener
- inputManager.registerKeyboardSystemShortcutListener(executor, callback2)
- assertEquals(currListener, registeredListener)
- }
-
- @Test
- fun testRemovingListenersUnregistersInternalCallbackListener() {
- // Set up two callbacks.
- val callback1 = InputManager.KeyboardSystemShortcutListener {_, _ -> }
- val callback2 = InputManager.KeyboardSystemShortcutListener {_, _ -> }
-
- inputManager.registerKeyboardSystemShortcutListener(executor, callback1)
- inputManager.registerKeyboardSystemShortcutListener(executor, callback2)
-
- // Only removing all listeners should remove the internal callback
- inputManager.unregisterKeyboardSystemShortcutListener(callback1)
- assertNotNull(registeredListener)
- inputManager.unregisterKeyboardSystemShortcutListener(callback2)
- assertNull(registeredListener)
- }
-
- @Test
- fun testMultipleListeners() {
- // Set up two callbacks.
- var callbackCount1 = 0
- var callbackCount2 = 0
- val callback1 = InputManager.KeyboardSystemShortcutListener { _, _ -> callbackCount1++ }
- val callback2 = InputManager.KeyboardSystemShortcutListener { _, _ -> callbackCount2++ }
-
- // Add both keyboard system shortcut listeners
- inputManager.registerKeyboardSystemShortcutListener(executor, callback1)
- inputManager.registerKeyboardSystemShortcutListener(executor, callback2)
-
- // Notifying keyboard system shortcut triggered, should notify both the callbacks.
- notifyKeyboardSystemShortcutTriggered(DEVICE_ID, HOME_SHORTCUT)
- testLooper.dispatchAll()
- assertEquals(1, callbackCount1)
- assertEquals(1, callbackCount2)
-
- inputManager.unregisterKeyboardSystemShortcutListener(callback2)
- // Notifying keyboard system shortcut triggered, should still trigger callback1 but not
- // callback2.
- notifyKeyboardSystemShortcutTriggered(DEVICE_ID, HOME_SHORTCUT)
- testLooper.dispatchAll()
- assertEquals(2, callbackCount1)
- assertEquals(1, callbackCount2)
- }
-}
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
new file mode 100644
index 0000000..3f611e0
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.hardware.input.IKeyGestureEventListener
+import android.hardware.input.KeyGestureEvent
+import android.platform.test.annotations.Presubmit
+import android.view.KeyEvent
+import androidx.test.core.app.ApplicationProvider
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mockito
+import org.mockito.junit.MockitoJUnit
+
+/**
+ * Tests for {@link KeyGestureController}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyGestureControllerTests
+ */
+@Presubmit
+class KeyGestureControllerTests {
+
+ companion object {
+ val DEVICE_ID = 1
+ val HOME_GESTURE_EVENT = KeyGestureEvent(
+ DEVICE_ID,
+ intArrayOf(KeyEvent.KEYCODE_H),
+ KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+ )
+ }
+
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ private lateinit var keyGestureController: KeyGestureController
+ private lateinit var context: Context
+ private var lastEvent: KeyGestureEvent? = null
+
+ @Before
+ fun setup() {
+ context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ keyGestureController = KeyGestureController()
+ }
+
+ @Test
+ fun testKeyGestureEvent_registerUnregisterListener() {
+ val listener = KeyGestureEventListener()
+
+ // Register key gesture event listener
+ keyGestureController.registerKeyGestureEventListener(listener, 0)
+ keyGestureController.onKeyGestureEvent(HOME_GESTURE_EVENT)
+ assertEquals(
+ "Listener should get callback on key gesture event",
+ HOME_GESTURE_EVENT,
+ lastEvent!!
+ )
+
+ // Unregister listener
+ lastEvent = null
+ keyGestureController.unregisterKeyGestureEventListener(listener, 0)
+ keyGestureController.onKeyGestureEvent(HOME_GESTURE_EVENT)
+ assertNull("Listener should not get callback after being unregistered", lastEvent)
+ }
+
+ inner class KeyGestureEventListener : IKeyGestureEventListener.Stub() {
+ override fun onKeyGestureEvent(
+ deviceId: Int,
+ keycodes: IntArray,
+ modifierState: Int,
+ gestureType: Int
+ ) {
+ lastEvent = KeyGestureEvent(deviceId, keycodes, modifierState, gestureType)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/KeyboardShortcutCallbackHandlerTests.kt b/tests/Input/src/com/android/server/input/KeyboardShortcutCallbackHandlerTests.kt
deleted file mode 100644
index 5a40a1c..0000000
--- a/tests/Input/src/com/android/server/input/KeyboardShortcutCallbackHandlerTests.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.input
-
-import android.content.Context
-import android.content.ContextWrapper
-import android.hardware.input.IKeyboardSystemShortcutListener
-import android.hardware.input.KeyboardSystemShortcut
-import android.platform.test.annotations.Presubmit
-import android.view.KeyEvent
-import androidx.test.core.app.ApplicationProvider
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNull
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.mockito.Mockito
-import org.mockito.junit.MockitoJUnit
-
-/**
- * Tests for {@link KeyboardShortcutCallbackHandler}.
- *
- * Build/Install/Run:
- * atest InputTests:KeyboardShortcutCallbackHandlerTests
- */
-@Presubmit
-class KeyboardShortcutCallbackHandlerTests {
-
- companion object {
- val DEVICE_ID = 1
- val HOME_SHORTCUT = KeyboardSystemShortcut(
- intArrayOf(KeyEvent.KEYCODE_H),
- KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
- KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME
- )
- }
-
- @get:Rule
- val rule = MockitoJUnit.rule()!!
-
- private lateinit var keyboardShortcutCallbackHandler: KeyboardShortcutCallbackHandler
- private lateinit var context: Context
- private var lastShortcut: KeyboardSystemShortcut? = null
-
- @Before
- fun setup() {
- context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- keyboardShortcutCallbackHandler = KeyboardShortcutCallbackHandler()
- }
-
- @Test
- fun testKeyboardSystemShortcutTriggered_registerUnregisterListener() {
- val listener = KeyboardSystemShortcutListener()
-
- // Register keyboard system shortcut listener
- keyboardShortcutCallbackHandler.registerKeyboardSystemShortcutListener(listener, 0)
- keyboardShortcutCallbackHandler.onKeyboardSystemShortcutTriggered(DEVICE_ID, HOME_SHORTCUT)
- assertEquals(
- "Listener should get callback on keyboard system shortcut triggered",
- HOME_SHORTCUT,
- lastShortcut!!
- )
-
- // Unregister listener
- lastShortcut = null
- keyboardShortcutCallbackHandler.unregisterKeyboardSystemShortcutListener(listener, 0)
- keyboardShortcutCallbackHandler.onKeyboardSystemShortcutTriggered(DEVICE_ID, HOME_SHORTCUT)
- assertNull("Listener should not get callback after being unregistered", lastShortcut)
- }
-
- inner class KeyboardSystemShortcutListener : IKeyboardSystemShortcutListener.Stub() {
- override fun onKeyboardSystemShortcutTriggered(
- deviceId: Int,
- keycodes: IntArray,
- modifierState: Int,
- shortcut: Int
- ) {
- assertEquals(DEVICE_ID, deviceId)
- lastShortcut = KeyboardSystemShortcut(keycodes, modifierState, shortcut)
- }
- }
-}
\ No newline at end of file
diff --git a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java
index 5a48327..9657225 100644
--- a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java
@@ -214,6 +214,13 @@
verify(mReader, never()).getViewerString(anyLong());
}
+ @Test
+ public void loadViewerConfigOnLogcatGroupRegistration() {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ mProtoLog.registerGroups(TestProtoLogGroup.TEST_GROUP);
+ verify(mReader).loadViewerConfig(any(), any());
+ }
+
private static class ProtoLogData {
Long mMessageHash = null;
Long mElapsedTime = null;
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java
index 359eb35..5012c23 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java
@@ -84,6 +84,7 @@
content.addView(enableSyncButton,
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.BOTTOM));
+ content.setFitsSystemWindows(true);
setContentView(content);
mSv.setZOrderOnTop(false);
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
index 73e0163..4119ea2 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
@@ -37,6 +37,7 @@
protected void onCreate(Bundle savedInstanceState) {
FrameLayout content = new FrameLayout(this);
+ content.setFitsSystemWindows(true);
super.onCreate(savedInstanceState);
mView = new SurfaceView(this);
content.addView(mView, new FrameLayout.LayoutParams(
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
index ac7dc9e..5287068 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
@@ -88,6 +88,7 @@
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout content = new LinearLayout(this);
+ content.setFitsSystemWindows(true);
mLocalSurfaceView = new SurfaceView(this);
content.addView(mLocalSurfaceView, new LinearLayout.LayoutParams(
500, 500, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
diff --git a/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java b/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java
index 09236ff..459db8a 100644
--- a/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java
@@ -74,6 +74,9 @@
}
}
+ private ObjectAnimator mColorValueAnimator;
+ private ObjectAnimator mYAnimator;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -81,16 +84,28 @@
// animate color to force bitmap uploads
UploadView uploadView = findViewById(R.id.upload_view);
- ObjectAnimator colorValueAnimator = ObjectAnimator.ofInt(uploadView, "colorValue", 0, 255);
- colorValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
- colorValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
- colorValueAnimator.start();
+ mColorValueAnimator = ObjectAnimator.ofInt(uploadView, "colorValue", 0, 255);
+ mColorValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mColorValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ mColorValueAnimator.start();
// animate scene root to guarantee there's a minimum amount of GPU rendering work
View uploadRoot = findViewById(R.id.upload_root);
- ObjectAnimator yAnimator = ObjectAnimator.ofFloat(uploadRoot, "translationY", 0, 100);
- yAnimator.setRepeatMode(ValueAnimator.REVERSE);
- yAnimator.setRepeatCount(ValueAnimator.INFINITE);
- yAnimator.start();
+ mYAnimator = ObjectAnimator.ofFloat(uploadRoot, "translationY", 0, 100);
+ mYAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mYAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ mYAnimator.start();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mColorValueAnimator != null) {
+ mColorValueAnimator.cancel();
+ }
+
+ if (mYAnimator != null) {
+ mYAnimator.cancel();
+ }
}
}
diff --git a/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java b/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java
index 882163b..9d10f76 100644
--- a/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java
@@ -66,18 +66,29 @@
return PixelFormat.OPAQUE;
}
}
+
+ private ObjectAnimator mObjectAnimator;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OverdrawDrawable overdraw = new OverdrawDrawable();
getWindow().setBackgroundDrawable(overdraw);
-
setContentView(new View(this));
- ObjectAnimator objectAnimator = ObjectAnimator.ofInt(overdraw, "colorValue", 0, 255);
- objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
- objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
- objectAnimator.start();
+ mObjectAnimator = ObjectAnimator.ofInt(overdraw, "colorValue", 0, 255);
+ mObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
+
+ mObjectAnimator.start();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mObjectAnimator != null) {
+ mObjectAnimator.cancel();
+ }
}
}
diff --git a/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java b/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java
index b26a660..1b28dc2 100644
--- a/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java
@@ -33,6 +33,7 @@
public class GlTextureViewActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {
private ImageFlipRenderThread mRenderThread;
private TextureView mTextureView;
+ private ObjectAnimator mAnimator;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -54,17 +55,17 @@
int distance = Math.max(mTextureView.getWidth(), mTextureView.getHeight());
mTextureView.setCameraDistance(distance * metrics.density);
- ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
- animator.setRepeatMode(ObjectAnimator.REVERSE);
- animator.setRepeatCount(ObjectAnimator.INFINITE);
- animator.setDuration(4000);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ mAnimator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
+ mAnimator.setRepeatMode(ObjectAnimator.REVERSE);
+ mAnimator.setRepeatCount(ObjectAnimator.INFINITE);
+ mAnimator.setDuration(4000);
+ mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mTextureView.invalidate();
}
});
- animator.start();
+ mAnimator.start();
}
@Override
@@ -86,4 +87,11 @@
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java b/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java
index 76ed1ae..f1e96c8 100644
--- a/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java
@@ -51,6 +51,7 @@
}
private ColorView[][] mColorViews;
+ private ObjectAnimator mAnimator;
@SuppressWarnings("unused")
public void setColorValue(int colorValue) {
@@ -80,9 +81,17 @@
}
}
- ObjectAnimator animator = ObjectAnimator.ofInt(this, "colorValue", 0, 255);
- animator.setRepeatMode(ValueAnimator.REVERSE);
- animator.setRepeatCount(ValueAnimator.INFINITE);
- animator.start();
+ mAnimator = ObjectAnimator.ofInt(this, "colorValue", 0, 255);
+ mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ mAnimator.start();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
}
}
diff --git a/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java b/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java
index 804ced1..9563572 100644
--- a/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java
@@ -33,6 +33,7 @@
private final ArrayList<LinearLayout> mLayouts = new ArrayList<>();
private int mColorToggle = 0;
+ private ObjectAnimator mAnimator;
private void createQuadTree(LinearLayout parent, int remainingDepth) {
mLayouts.add(parent);
@@ -71,9 +72,17 @@
createQuadTree(root, 8);
setContentView(root);
- ObjectAnimator animator = ObjectAnimator.ofInt(this, "ignoredValue", 0, 1000);
- animator.setRepeatMode(ValueAnimator.REVERSE);
- animator.setRepeatCount(ValueAnimator.INFINITE);
- animator.start();
+ mAnimator = ObjectAnimator.ofInt(this, "ignoredValue", 0, 1000);
+ mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ mAnimator.start();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
}
}