Merge "Add featureFlag attribute for new RECORD_SENSITIVE_CONTENT permission" into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index e0adee3..78ac774 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3327,7 +3327,7 @@
method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @Nullable public static android.app.wearable.WearableSensingDataRequest getDataRequestFromIntent(@NonNull android.content.Intent);
method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideData(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void registerDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void startHotwordRecognition(@Nullable android.content.ComponentName, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void stopHotwordRecognition(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ed0c933..df566db 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -272,17 +272,10 @@
@UnsupportedAppUsage
private Context mOuterContext;
-
- private final Object mThemeLock = new Object();
-
@UnsupportedAppUsage
- @GuardedBy("mThemeLock")
private int mThemeResource = 0;
-
@UnsupportedAppUsage
- @GuardedBy("mThemeLock")
private Resources.Theme mTheme = null;
-
@UnsupportedAppUsage
private PackageManager mPackageManager;
private Context mReceiverRestrictedContext = null;
@@ -295,6 +288,7 @@
private ContentCaptureOptions mContentCaptureOptions = null;
+ private final Object mSync = new Object();
/**
* Indicates this {@link Context} can not handle UI components properly and is not associated
* with a {@link Display} instance.
@@ -346,18 +340,21 @@
*/
private boolean mOwnsToken = false;
- private final Object mDirsLock = new Object();
- private volatile File mDatabasesDir;
- @UnsupportedAppUsage private volatile File mPreferencesDir;
- private volatile File mFilesDir;
- private volatile File mCratesDir;
- private volatile File mNoBackupFilesDir;
- private volatile File[] mExternalFilesDirs;
- private volatile File[] mObbDirs;
- private volatile File mCacheDir;
- private volatile File mCodeCacheDir;
- private volatile File[] mExternalCacheDirs;
- private volatile File[] mExternalMediaDirs;
+ @GuardedBy("mSync")
+ private File mDatabasesDir;
+ @GuardedBy("mSync")
+ @UnsupportedAppUsage
+ private File mPreferencesDir;
+ @GuardedBy("mSync")
+ private File mFilesDir;
+ @GuardedBy("mSync")
+ private File mCratesDir;
+ @GuardedBy("mSync")
+ private File mNoBackupFilesDir;
+ @GuardedBy("mSync")
+ private File mCacheDir;
+ @GuardedBy("mSync")
+ private File mCodeCacheDir;
// The system service cache for the system services that are cached per-ContextImpl.
@UnsupportedAppUsage
@@ -461,7 +458,7 @@
@Override
public void setTheme(int resId) {
- synchronized (mThemeLock) {
+ synchronized (mSync) {
if (mThemeResource != resId) {
mThemeResource = resId;
initializeTheme();
@@ -471,14 +468,14 @@
@Override
public int getThemeResId() {
- synchronized (mThemeLock) {
+ synchronized (mSync) {
return mThemeResource;
}
}
@Override
public Resources.Theme getTheme() {
- synchronized (mThemeLock) {
+ synchronized (mSync) {
if (mTheme != null) {
return mTheme;
}
@@ -491,7 +488,6 @@
}
}
- @GuardedBy("mThemeLock")
private void initializeTheme() {
if (mTheme == null) {
mTheme = mResources.newTheme();
@@ -741,18 +737,12 @@
@UnsupportedAppUsage
private File getPreferencesDir() {
- File localPreferencesDir = mPreferencesDir;
- if (localPreferencesDir == null) {
- synchronized (mDirsLock) {
- localPreferencesDir = mPreferencesDir;
- if (localPreferencesDir == null) {
- localPreferencesDir = new File(getDataDir(), "shared_prefs");
- ensurePrivateDirExists(localPreferencesDir);
- mPreferencesDir = localPreferencesDir;
- }
+ synchronized (mSync) {
+ if (mPreferencesDir == null) {
+ mPreferencesDir = new File(getDataDir(), "shared_prefs");
}
+ return ensurePrivateDirExists(mPreferencesDir);
}
- return localPreferencesDir;
}
@Override
@@ -794,16 +784,16 @@
/**
* Common-path handling of app data dir creation
*/
- private static void ensurePrivateDirExists(File file) {
- ensurePrivateDirExists(file, 0771, -1, null);
+ private static File ensurePrivateDirExists(File file) {
+ return ensurePrivateDirExists(file, 0771, -1, null);
}
- private static void ensurePrivateCacheDirExists(File file, String xattr) {
+ private static File ensurePrivateCacheDirExists(File file, String xattr) {
final int gid = UserHandle.getCacheAppGid(Process.myUid());
- ensurePrivateDirExists(file, 02771, gid, xattr);
+ return ensurePrivateDirExists(file, 02771, gid, xattr);
}
- private static void ensurePrivateDirExists(File file, int mode, int gid, String xattr) {
+ private static File ensurePrivateDirExists(File file, int mode, int gid, String xattr) {
if (!file.exists()) {
final String path = file.getAbsolutePath();
try {
@@ -831,22 +821,17 @@
}
}
}
+ return file;
}
@Override
public File getFilesDir() {
- File localFilesDir = mFilesDir;
- if (localFilesDir == null) {
- localFilesDir = mFilesDir;
- synchronized (mDirsLock) {
- if (localFilesDir == null) {
- localFilesDir = new File(getDataDir(), "files");
- ensurePrivateDirExists(localFilesDir);
- mFilesDir = localFilesDir;
- }
+ synchronized (mSync) {
+ if (mFilesDir == null) {
+ mFilesDir = new File(getDataDir(), "files");
}
+ return ensurePrivateDirExists(mFilesDir);
}
- return localFilesDir;
}
@Override
@@ -856,37 +841,25 @@
final Path absoluteNormalizedCratePath = cratesRootPath.resolve(crateId)
.toAbsolutePath().normalize();
- File localCratesDir = mCratesDir;
- if (localCratesDir == null) {
- synchronized (mDirsLock) {
- localCratesDir = mCratesDir;
- if (localCratesDir == null) {
- localCratesDir = cratesRootPath.toFile();
- ensurePrivateDirExists(localCratesDir);
- mCratesDir = localCratesDir;
- }
+ synchronized (mSync) {
+ if (mCratesDir == null) {
+ mCratesDir = cratesRootPath.toFile();
}
+ ensurePrivateDirExists(mCratesDir);
}
- File crateDir = absoluteNormalizedCratePath.toFile();
- ensurePrivateDirExists(crateDir);
- return crateDir;
+ File cratedDir = absoluteNormalizedCratePath.toFile();
+ return ensurePrivateDirExists(cratedDir);
}
@Override
public File getNoBackupFilesDir() {
- File localNoBackupFilesDir = mNoBackupFilesDir;
- if (localNoBackupFilesDir == null) {
- synchronized (mDirsLock) {
- localNoBackupFilesDir = mNoBackupFilesDir;
- if (localNoBackupFilesDir == null) {
- localNoBackupFilesDir = new File(getDataDir(), "no_backup");
- ensurePrivateDirExists(localNoBackupFilesDir);
- mNoBackupFilesDir = localNoBackupFilesDir;
- }
+ synchronized (mSync) {
+ if (mNoBackupFilesDir == null) {
+ mNoBackupFilesDir = new File(getDataDir(), "no_backup");
}
+ return ensurePrivateDirExists(mNoBackupFilesDir);
}
- return localNoBackupFilesDir;
}
@Override
@@ -898,24 +871,13 @@
@Override
public File[] getExternalFilesDirs(String type) {
- File[] localExternalFilesDirs = mExternalFilesDirs;
- if (localExternalFilesDirs == null) {
- synchronized (mDirsLock) {
- localExternalFilesDirs = mExternalFilesDirs;
- if (localExternalFilesDirs == null) {
- localExternalFilesDirs =
- Environment.buildExternalStorageAppFilesDirs(getPackageName());
- if (type != null) {
- localExternalFilesDirs =
- Environment.buildPaths(localExternalFilesDirs, type);
- }
- localExternalFilesDirs = ensureExternalDirsExistOrFilter(
- localExternalFilesDirs, true /* tryCreateInProcess */);
- mExternalFilesDirs = localExternalFilesDirs;
- }
+ synchronized (mSync) {
+ File[] dirs = Environment.buildExternalStorageAppFilesDirs(getPackageName());
+ if (type != null) {
+ dirs = Environment.buildPaths(dirs, type);
}
+ return ensureExternalDirsExistOrFilter(dirs, true /* tryCreateInProcess */);
}
- return localExternalFilesDirs;
}
@Override
@@ -927,51 +889,30 @@
@Override
public File[] getObbDirs() {
- File[] localObbDirs = mObbDirs;
- if (mObbDirs == null) {
- synchronized (mDirsLock) {
- localObbDirs = mObbDirs;
- if (localObbDirs == null) {
- localObbDirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
- localObbDirs = ensureExternalDirsExistOrFilter(
- localObbDirs, true /* tryCreateInProcess */);
- mObbDirs = localObbDirs;
- }
- }
+ synchronized (mSync) {
+ File[] dirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
+ return ensureExternalDirsExistOrFilter(dirs, true /* tryCreateInProcess */);
}
- return localObbDirs;
}
@Override
public File getCacheDir() {
- File localCacheDir = mCacheDir;
- if (localCacheDir == null) {
- synchronized (mDirsLock) {
- localCacheDir = mCacheDir;
- if (localCacheDir == null) {
- localCacheDir = new File(getDataDir(), "cache");
- ensurePrivateCacheDirExists(localCacheDir, XATTR_INODE_CACHE);
- mCacheDir = localCacheDir;
- }
+ synchronized (mSync) {
+ if (mCacheDir == null) {
+ mCacheDir = new File(getDataDir(), "cache");
}
+ return ensurePrivateCacheDirExists(mCacheDir, XATTR_INODE_CACHE);
}
- return localCacheDir;
}
@Override
public File getCodeCacheDir() {
- File localCodeCacheDir = mCodeCacheDir;
- if (localCodeCacheDir == null) {
- synchronized (mDirsLock) {
- localCodeCacheDir = mCodeCacheDir;
- if (localCodeCacheDir == null) {
- localCodeCacheDir = getCodeCacheDirBeforeBind(getDataDir());
- ensurePrivateCacheDirExists(localCodeCacheDir, XATTR_INODE_CODE_CACHE);
- mCodeCacheDir = localCodeCacheDir;
- }
+ synchronized (mSync) {
+ if (mCodeCacheDir == null) {
+ mCodeCacheDir = getCodeCacheDirBeforeBind(getDataDir());
}
+ return ensurePrivateCacheDirExists(mCodeCacheDir, XATTR_INODE_CODE_CACHE);
}
- return localCodeCacheDir;
}
/**
@@ -992,37 +933,21 @@
@Override
public File[] getExternalCacheDirs() {
- File[] localExternalCacheDirs = mExternalCacheDirs;
- if (localExternalCacheDirs == null) {
- synchronized (mDirsLock) {
- localExternalCacheDirs = mExternalCacheDirs;
- if (localExternalCacheDirs == null) {
- localExternalCacheDirs =
- Environment.buildExternalStorageAppCacheDirs(getPackageName());
- localExternalCacheDirs = ensureExternalDirsExistOrFilter(
- localExternalCacheDirs, false /* tryCreateInProcess */);
- mExternalCacheDirs = localExternalCacheDirs;
- }
- }
+ synchronized (mSync) {
+ File[] dirs = Environment.buildExternalStorageAppCacheDirs(getPackageName());
+ // We don't try to create cache directories in-process, because they need special
+ // setup for accurate quota tracking. This ensures the cache dirs are always
+ // created through StorageManagerService.
+ return ensureExternalDirsExistOrFilter(dirs, false /* tryCreateInProcess */);
}
- return localExternalCacheDirs;
}
@Override
public File[] getExternalMediaDirs() {
- File[] localExternalMediaDirs = mExternalMediaDirs;
- if (localExternalMediaDirs == null) {
- synchronized (mDirsLock) {
- localExternalMediaDirs = mExternalMediaDirs;
- if (localExternalMediaDirs == null) {
- localExternalMediaDirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
- localExternalMediaDirs = ensureExternalDirsExistOrFilter(
- localExternalMediaDirs, true /* tryCreateInProcess */);
- mExternalMediaDirs = localExternalMediaDirs;
- }
- }
+ synchronized (mSync) {
+ File[] dirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
+ return ensureExternalDirsExistOrFilter(dirs, true /* tryCreateInProcess */);
}
- return localExternalMediaDirs;
}
/**
@@ -1121,22 +1046,16 @@
}
private File getDatabasesDir() {
- File localDatabasesDir = mDatabasesDir;
- if (localDatabasesDir == null) {
- synchronized (mDirsLock) {
- localDatabasesDir = mDatabasesDir;
- if (localDatabasesDir == null) {
- if ("android".equals(getPackageName())) {
- localDatabasesDir = new File("/data/system");
- } else {
- localDatabasesDir = new File(getDataDir(), "databases");
- }
- ensurePrivateDirExists(localDatabasesDir);
- mDatabasesDir = localDatabasesDir;
+ synchronized (mSync) {
+ if (mDatabasesDir == null) {
+ if ("android".equals(getPackageName())) {
+ mDatabasesDir = new File("/data/system");
+ } else {
+ mDatabasesDir = new File(getDataDir(), "databases");
}
}
+ return ensurePrivateDirExists(mDatabasesDir);
}
- return localDatabasesDir;
}
@Override
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index 9573e6f..df6d2a6 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -246,7 +246,10 @@
* @param executor Executor on which to run the consumer callback
* @param statusConsumer A consumer that handles the status codes, which is returned
* right after the call.
+ * @deprecated Use {@link #provideConnection(ParcelFileDescriptor, Executor, Consumer)} instead
+ * to provide a remote wearable device connection to the WearableSensingService
*/
+ @Deprecated
@RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
public void provideDataStream(
@NonNull ParcelFileDescriptor parcelFileDescriptor,
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 0208fed..be9f0a0 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -295,7 +295,7 @@
* called.
*
* @param view The customized view information.
- * @return This builder.re
+ * @return This builder.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
@NonNull
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 07cbaad..3a1e883 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -19,24 +19,23 @@
//#define LOG_NDEBUG 0
-#include <inttypes.h>
-
-#include <nativehelper/JNIHelp.h>
-
#include <android-base/stringprintf.h>
#include <android_runtime/AndroidRuntime.h>
+#include <input/InputConsumer.h>
#include <input/InputTransport.h>
+#include <inttypes.h>
#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
#include <utils/Looper.h>
+
#include <variant>
#include <vector>
+
#include "android_os_MessageQueue.h"
#include "android_view_InputChannel.h"
#include "android_view_KeyEvent.h"
#include "android_view_MotionEvent.h"
-
-#include <nativehelper/ScopedLocalRef.h>
-
#include "core_jni_helpers.h"
namespace android {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1357932..0869af5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6781,7 +6781,7 @@
@FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")
-->
<permission android:name="android.permission.SET_BIOMETRIC_DIALOG_LOGO"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to control keyguard. Only allowed for system processes.
@hide -->
diff --git a/data/etc/enhanced-confirmation.xml b/data/etc/enhanced-confirmation.xml
index 3b1867c..973bcb5 100644
--- a/data/etc/enhanced-confirmation.xml
+++ b/data/etc/enhanced-confirmation.xml
@@ -24,33 +24,49 @@
<enhanced-confirmation-trusted-package
package="com.example.app"
sha256-cert-digest="E9:7A:BC:2C:D1:CA:8D:58:6A:57:0B:8C:F8:60:AA:D2:8D:13:30:2A:FB:C9:00:2C:5D:53:B2:6C:09:A4:85:A0"/>
-
...
<enhanced-confirmation-trusted-installer
package="com.example.installer"
sha256-cert-digest="E9:7A:BC:2C:D1:CA:8D:58:6A:57:0B:8C:F8:60:AA:D2:8D:13:30:2A:FB:C9:00:2C:5D:53:B2:6C:09:A4:85:A0"/>
-
...
-The "enhanced-confirmation-trusted-package" entry shown above indicates that "com.example.app"
-should be considered a "trusted package". A "trusted package" will be exempt from ECM restrictions.
+The <enhanced-confirmation-trusted-package> entry shown in the above example indicates that
+"com.example.app" should be considered a "trusted package". A "trusted package" will be exempt from
+ECM restrictions.
-The "enhanced-confirmation-trusted-installer" entry shown above indicates that
-"com.example.installer" should be considered a "trusted installer". A "trusted installer", and all
-packages that it installs, will be exempt from ECM restrictions. (There are some exceptions to this.
-For example, a trusted installer, at the time of installing an app, can opt the app back in to ECM
-restrictions by setting the app's package source to PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
-or PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE.)
+The <enhanced-confirmation-trusted-installer> entry shown in the above example indicates that
+"com.example.app" should be considered a "trusted installer". Apps installed by "trusted installers"
+will be exempt from ECM restrictions, with conditions explained in the next few paragraphs.
-In either case:
+If zero <enhanced-confirmation-trusted-installer> entries are declared, then *all* packages will be
+exempt from ECM restrictions, except apps meeting *all* of the following criteria:
+
+ A. The app is not pre-installed, and
+ B. The app has no matching <enhanced-confirmation-trusted-package> entries declared, and
+ C. The app is marked by its installer as coming from an untrustworthy package source.
+
+(For example, an installer may set an app's package source to
+PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE or PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE,
+which are considered untrustworthy.)
+
+If one or more <enhanced-confirmation-trusted-installer> entries are declared, then packages must,
+in order to be exempt from ECM, meet at least one of the following criteria:
+
+ A. Be installed by an installer with a matching <enhanced-confirmation-trusted-installer> entry
+ declared, and be marked as coming from an "trustworthy" package source by the installer, or
+ B. Be installed via a pre-installed installer, and be marked as coming from a "trustworthy"
+ package source by the installer, or
+ C. Have a matching <enhanced-confirmation-trusted-package> entry declared.
+
+For either type of XML element:
- The "package" XML attribute refers to the app's package name.
- The "sha256-cert-digest" XML attribute refers to the SHA-256 hash of an app signing certificate.
For any entry to successfully apply to a package, both XML attributes must be present, and must
match the package. That is, the package name must match the "package" attribute, and the app must be
-signed by the signing certificate identified by the "sha256-cert-digest" attribute..
+signed by the signing certificate identified by the "sha256-cert-digest" attribute.
-->
<config></config>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index ad40493..f7ffdd1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -64,7 +64,9 @@
default void onSplitVisibilityChanged(boolean visible) {}
}
- /** Callback interface for listening to requests to enter split select */
+ /**
+ * Callback interface for listening to requests to enter split select. Used for desktop -> split
+ */
interface SplitSelectListener {
default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
int splitPosition, Rect taskBounds) {
@@ -72,6 +74,15 @@
}
}
+ interface SplitInvocationListener {
+ /**
+ * Called whenever shell starts or stops the split screen animation
+ * @param animationRunning if {@code true} the animation has begun, if {@code false} the
+ * animation has finished
+ */
+ default void onSplitAnimationInvoked(boolean animationRunning) { }
+ }
+
/** Registers listener that gets split screen callback. */
void registerSplitScreenListener(@NonNull SplitScreenListener listener,
@NonNull Executor executor);
@@ -79,6 +90,15 @@
/** Unregisters listener that gets split screen callback. */
void unregisterSplitScreenListener(@NonNull SplitScreenListener listener);
+ /**
+ * Registers a {@link SplitInvocationListener} to notify when the animation to enter split
+ * screen has started and stopped
+ *
+ * @param executor callbacks to the listener will be executed on this executor
+ */
+ void registerSplitAnimationListener(@NonNull SplitInvocationListener listener,
+ @NonNull Executor executor);
+
/** Called when device waking up finished. */
void onFinishedWakingUp();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 86c8f04..218abe2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -1138,6 +1138,12 @@
}
@Override
+ public void registerSplitAnimationListener(@NonNull SplitInvocationListener listener,
+ @NonNull Executor executor) {
+ mStageCoordinator.registerSplitAnimationListener(listener, executor);
+ }
+
+ @Override
public void onFinishedWakingUp() {
mMainExecutor.execute(SplitScreenController.this::onFinishedWakingUp);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 1a53a1d..e69ff70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -55,6 +55,7 @@
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
/** Manages transition animations for split-screen. */
class SplitScreenTransitions {
@@ -79,6 +80,8 @@
private Transitions.TransitionFinishCallback mFinishCallback = null;
private SurfaceControl.Transaction mFinishTransaction;
+ private SplitScreen.SplitInvocationListener mSplitInvocationListener;
+ private Executor mSplitInvocationListenerExecutor;
SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
@NonNull Runnable onFinishCallback, StageCoordinator stageCoordinator) {
@@ -353,6 +356,10 @@
+ " skip to start enter split transition since it already exist. ");
return null;
}
+ if (mSplitInvocationListenerExecutor != null && mSplitInvocationListener != null) {
+ mSplitInvocationListenerExecutor.execute(() -> mSplitInvocationListener
+ .onSplitAnimationInvoked(true /*animationRunning*/));
+ }
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim);
return transition;
@@ -529,6 +536,12 @@
mTransitions.getAnimExecutor().execute(va::start);
}
+ public void registerSplitAnimListener(@NonNull SplitScreen.SplitInvocationListener listener,
+ @NonNull Executor executor) {
+ mSplitInvocationListener = listener;
+ mSplitInvocationListenerExecutor = executor;
+ }
+
/** Calls when the transition got consumed. */
interface TransitionConsumedCallback {
void onConsumed(boolean aborted);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 36368df9..8ab6cf7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -156,6 +156,7 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.Executor;
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
@@ -236,6 +237,9 @@
private DefaultMixedHandler mMixedHandler;
private final Toast mSplitUnsupportedToast;
private SplitRequest mSplitRequest;
+ /** Used to notify others of when shell is animating into split screen */
+ private SplitScreen.SplitInvocationListener mSplitInvocationListener;
+ private Executor mSplitInvocationListenerExecutor;
/**
* Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support
@@ -246,6 +250,14 @@
return false;
}
+ /** NOTE: Will overwrite any previously set {@link #mSplitInvocationListener} */
+ public void registerSplitAnimationListener(
+ @NonNull SplitScreen.SplitInvocationListener listener, @NonNull Executor executor) {
+ mSplitInvocationListener = listener;
+ mSplitInvocationListenerExecutor = executor;
+ mSplitTransitions.registerSplitAnimListener(listener, executor);
+ }
+
class SplitRequest {
@SplitPosition
int mActivatePosition;
@@ -529,7 +541,7 @@
null /* childrenToTop */, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("startShortcut",
"side stage was not populated"));
- mSplitUnsupportedToast.show();
+ handleUnsupportedSplitStart();
}
if (finishedCallback != null) {
@@ -660,7 +672,7 @@
null /* childrenToTop */, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("startIntentLegacy",
"side stage was not populated"));
- mSplitUnsupportedToast.show();
+ handleUnsupportedSplitStart();
}
if (apps != null) {
@@ -1213,7 +1225,7 @@
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("onRemoteAnimationFinishedOrCancelled",
"main or side stage was not populated."));
- mSplitUnsupportedToast.show();
+ handleUnsupportedSplitStart();
} else {
mSyncQueue.queue(evictWct);
mSyncQueue.runInSync(t -> {
@@ -1234,7 +1246,7 @@
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("onRemoteAnimationFinished",
"main or side stage was not populated"));
- mSplitUnsupportedToast.show();
+ handleUnsupportedSplitStart();
return;
}
@@ -2801,6 +2813,7 @@
if (hasEnteringPip) {
mMixedHandler.animatePendingEnterPipFromSplit(transition, info,
startTransaction, finishTransaction, finishCallback);
+ notifySplitAnimationFinished();
return true;
}
@@ -2835,6 +2848,7 @@
// the transition, or synchronize task-org callbacks.
}
// Use normal animations.
+ notifySplitAnimationFinished();
return false;
} else if (mMixedHandler != null && TransitionUtil.hasDisplayChange(info)) {
// A display-change has been un-expectedly inserted into the transition. Redirect
@@ -2848,6 +2862,7 @@
mSplitLayout.update(startTransaction, true /* resetImePosition */);
startTransaction.apply();
}
+ notifySplitAnimationFinished();
return true;
}
}
@@ -3021,7 +3036,7 @@
pendingEnter.mRemoteHandler.onTransitionConsumed(transition,
false /*aborted*/, finishT);
}
- mSplitUnsupportedToast.show();
+ handleUnsupportedSplitStart();
return true;
}
}
@@ -3050,6 +3065,7 @@
final TransitionInfo.Change finalMainChild = mainChild;
final TransitionInfo.Change finalSideChild = sideChild;
enterTransition.setFinishedCallback((callbackWct, callbackT) -> {
+ notifySplitAnimationFinished();
if (finalMainChild != null) {
if (!mainNotContainOpenTask) {
mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId);
@@ -3466,6 +3482,19 @@
mSplitLayout.isLeftRightSplit());
}
+ private void handleUnsupportedSplitStart() {
+ mSplitUnsupportedToast.show();
+ notifySplitAnimationFinished();
+ }
+
+ private void notifySplitAnimationFinished() {
+ if (mSplitInvocationListener == null || mSplitInvocationListenerExecutor == null) {
+ return;
+ }
+ mSplitInvocationListenerExecutor.execute(() ->
+ mSplitInvocationListener.onSplitAnimationInvoked(false /*animationRunning*/));
+ }
+
/**
* Logs the exit of splitscreen to a specific stage. This must be called before the exit is
* executed.
@@ -3528,7 +3557,7 @@
if (!ENABLE_SHELL_TRANSITIONS) {
StageCoordinator.this.exitSplitScreen(isMainStage ? mMainStage : mSideStage,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
- mSplitUnsupportedToast.show();
+ handleUnsupportedSplitStart();
return;
}
@@ -3548,7 +3577,7 @@
"app package " + taskInfo.baseActivity.getPackageName()
+ " does not support splitscreen, or is a controlled activity type"));
if (splitScreenVisible) {
- mSplitUnsupportedToast.show();
+ handleUnsupportedSplitStart();
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index 4ea71490..5b402a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -212,6 +212,7 @@
switch (mType) {
case TYPE_RECENTS_DURING_DESKTOP:
case TYPE_RECENTS_DURING_SPLIT:
+ case TYPE_RECENTS_DURING_KEYGUARD:
mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
break;
default:
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index befc702..34b2eeb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -39,10 +39,13 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -63,6 +66,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -105,6 +109,8 @@
@Mock private ShellExecutor mMainExecutor;
@Mock private LaunchAdjacentController mLaunchAdjacentController;
@Mock private DefaultMixedHandler mMixedHandler;
+ @Mock private SplitScreen.SplitInvocationListener mInvocationListener;
+ private final TestShellExecutor mTestShellExecutor = new TestShellExecutor();
private SplitLayout mSplitLayout;
private MainStage mMainStage;
private SideStage mSideStage;
@@ -147,6 +153,7 @@
.setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager();
doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager();
+ mStageCoordinator.registerSplitAnimationListener(mInvocationListener, mTestShellExecutor);
}
@Test
@@ -452,6 +459,15 @@
mMainStage.activate(new WindowContainerTransaction(), true /* includingTopTask */);
}
+ @Test
+ @UiThreadTest
+ public void testSplitInvocationCallback() {
+ enterSplit();
+ mTestShellExecutor.flushAll();
+ verify(mInvocationListener, times(1))
+ .onSplitAnimationInvoked(eq(true));
+ }
+
private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) {
for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png
index bb518c0..d156f95 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png
index bb518c0..d156f95 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_spinner.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png
index 10869f2..fe877ee 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_footer.png
Binary files differ
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt
index 9a99649..f779cf36 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt
@@ -62,6 +62,7 @@
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
@@ -69,7 +70,6 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastFirst
import androidx.compose.ui.util.fastFirstOrNull
-import com.android.compose.theme.LocalAndroidColorScheme
/**
* Platform slider implementation that displays a slider with an [icon] and a [label] at the start.
@@ -143,12 +143,14 @@
thumb = { Spacer(Modifier.size(thumbSize)) },
)
- Spacer(
- Modifier.padding(8.dp)
- .size(4.dp)
- .align(Alignment.CenterEnd)
- .background(color = colors.indicatorColor, shape = CircleShape)
- )
+ if (enabled) {
+ Spacer(
+ Modifier.padding(8.dp)
+ .size(4.dp)
+ .align(Alignment.CenterEnd)
+ .background(color = colors.indicatorColor, shape = CircleShape)
+ )
+ }
}
}
@@ -219,9 +221,9 @@
)
Box(
modifier =
- Modifier.layoutId(TrackComponent.Label).offset {
- IntOffset(offsetX.toInt(), 0)
- },
+ Modifier.layoutId(TrackComponent.Label)
+ .offset { IntOffset(offsetX.toInt(), 0) }
+ .padding(end = 16.dp),
contentAlignment = Alignment.CenterStart,
) {
CompositionLocalProvider(
@@ -452,11 +454,11 @@
@Composable
private fun lightThemePlatformSliderColors() =
PlatformSliderColors(
- trackColor = LocalAndroidColorScheme.current.tertiaryFixedDim,
+ trackColor = colorResource(android.R.color.system_accent3_200),
indicatorColor = MaterialTheme.colorScheme.tertiary,
iconColor = MaterialTheme.colorScheme.onTertiary,
labelColorOnIndicator = MaterialTheme.colorScheme.onTertiary,
- labelColorOnTrack = LocalAndroidColorScheme.current.onTertiaryFixed,
+ labelColorOnTrack = MaterialTheme.colorScheme.onTertiaryContainer,
disabledTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
disabledIndicatorColor = MaterialTheme.colorScheme.surfaceContainerHighest,
disabledIconColor = MaterialTheme.colorScheme.outline,
@@ -467,11 +469,11 @@
@Composable
private fun darkThemePlatformSliderColors() =
PlatformSliderColors(
- trackColor = MaterialTheme.colorScheme.tertiary,
+ trackColor = colorResource(android.R.color.system_accent3_600),
indicatorColor = MaterialTheme.colorScheme.tertiary,
- iconColor = MaterialTheme.colorScheme.onTertiaryContainer,
+ iconColor = MaterialTheme.colorScheme.onTertiary,
labelColorOnIndicator = MaterialTheme.colorScheme.onTertiary,
- labelColorOnTrack = LocalAndroidColorScheme.current.onTertiaryFixed,
+ labelColorOnTrack = colorResource(android.R.color.system_accent3_900),
disabledTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
disabledIndicatorColor = MaterialTheme.colorScheme.surfaceContainerHighest,
disabledIconColor = MaterialTheme.colorScheme.outline,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index d624bf7..43266bf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -256,7 +256,7 @@
}
@Test
- fun hubTimeout_whenDreaming() =
+ fun hubTimeout_whenDreaming_goesToBlank() =
with(kosmos) {
testScope.runTest {
// Device is dreaming and on communal.
@@ -273,7 +273,22 @@
}
@Test
- fun hubTimeout_dreamStopped() =
+ fun hubTimeout_notDreaming_staysOnCommunal() =
+ with(kosmos) {
+ testScope.runTest {
+ // Device is not dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(false)
+ communalInteractor.onSceneChanged(CommunalScenes.Communal)
+
+ // Scene stays as Communal
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+ val scene by collectLastValue(communalInteractor.desiredScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ }
+ }
+
+ @Test
+ fun hubTimeout_dreamStopped_staysOnCommunal() =
with(kosmos) {
testScope.runTest {
// Device is dreaming and on communal.
@@ -296,6 +311,28 @@
}
@Test
+ fun hubTimeout_dreamStartedHalfway_goesToCommunal() =
+ with(kosmos) {
+ testScope.runTest {
+ // Device is on communal, but not dreaming.
+ fakeKeyguardRepository.setDreaming(false)
+ communalInteractor.onSceneChanged(CommunalScenes.Communal)
+
+ val scene by collectLastValue(communalInteractor.desiredScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+
+ // Wait a bit, but not long enough to timeout, then start dreaming.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ fakeKeyguardRepository.setDreaming(true)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+
+ // Device times out after one screen timeout interval, dream doesn't reset timeout.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
+ }
+ }
+
+ @Test
fun hubTimeout_userActivityTriggered_resetsTimeout() =
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index de659cf..f517cec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -81,12 +81,12 @@
}
@Test
- fun movement_initializedToZero() =
+ fun movement_initializedToDefaultValues() =
testScope.runTest {
val movement by collectLastValue(underTest.movement(burnInParameters))
assertThat(movement?.translationY).isEqualTo(0)
assertThat(movement?.translationX).isEqualTo(0)
- assertThat(movement?.scale).isEqualTo(0f)
+ assertThat(movement?.scale).isEqualTo(1f)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
index 64125f1..af96780 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
@@ -99,6 +99,31 @@
values.forEach { assertThat(it.value).isIn(Range.closed(-100f, 0f)) }
}
+ @Test
+ fun lockscreenTranslationX_resetsAfterCancellation() =
+ testScope.runTest {
+ configurationRepository.setDimensionPixelSize(
+ R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
+ 100
+ )
+ val values by collectValues(underTest.keyguardTranslationX)
+ assertThat(values).isEmpty()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0.3f),
+ step(0.5f),
+ step(0.9f, TransitionState.CANCELED)
+ ),
+ testScope,
+ )
+
+ assertThat(values).hasSize(4)
+ values.forEach { assertThat(it.value).isIn(Range.closed(-100f, 0f)) }
+ assertThat(values.last().value).isEqualTo(0f)
+ }
+
private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 869b2bf..6e15c51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -86,6 +86,13 @@
}
@Test
+ fun defaultBurnInScaleEqualsOne() =
+ testScope.runTest {
+ val burnInScale by collectLastValue(underTest.scale)
+ assertThat(burnInScale!!.scale).isEqualTo(1f)
+ }
+
+ @Test
fun burnInLayerVisibility() =
testScope.runTest {
val burnInLayerVisibility by collectLastValue(underTest.burnInLayerVisibility)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
index 449e8bf..1ed7f5d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.volume.panel.component.spatial.domain
+import android.media.AudioDeviceAttributes
+import android.media.AudioDeviceInfo
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.testing.TestableLooper.RunWithLooper
@@ -49,26 +51,27 @@
class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
private val kosmos = testKosmos()
- private val cachedBluetoothDevice: CachedBluetoothDevice = mock {
- whenever(address).thenReturn("test_address")
- }
- private val bluetoothMediaDevice: BluetoothMediaDevice = mock {
- whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
- }
-
private lateinit var underTest: SpatialAudioAvailabilityCriteria
@Before
fun setup() {
with(kosmos) {
- mediaControllerRepository.setActiveLocalMediaController(
- mediaController.apply {
- whenever(packageName).thenReturn("test.pkg")
- whenever(sessionToken).thenReturn(MediaSession.Token(0, mock {}))
- whenever(playbackState).thenReturn(PlaybackState.Builder().build())
+ val cachedBluetoothDevice: CachedBluetoothDevice = mock {
+ whenever(address).thenReturn("test_address")
+ }
+ localMediaRepository.updateCurrentConnectedDevice(
+ mock<BluetoothMediaDevice> {
+ whenever(name).thenReturn("test_device")
+ whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
}
)
+ whenever(mediaController.packageName).thenReturn("test.pkg")
+ whenever(mediaController.sessionToken).thenReturn(MediaSession.Token(0, mock {}))
+ whenever(mediaController.playbackState).thenReturn(PlaybackState.Builder().build())
+
+ mediaControllerRepository.setActiveLocalMediaController(mediaController)
+
underTest = SpatialAudioAvailabilityCriteria(spatialAudioComponentInteractor)
}
}
@@ -77,9 +80,8 @@
fun noSpatialAudio_noHeadTracking_unavailable() {
with(kosmos) {
testScope.runTest {
- localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice)
- spatializerRepository.defaultHeadTrackingAvailable = false
- spatializerRepository.defaultSpatialAudioAvailable = false
+ spatializerRepository.setIsSpatialAudioAvailable(headset, false)
+ spatializerRepository.setIsHeadTrackingAvailable(headset, false)
val isAvailable by collectLastValue(underTest.isAvailable())
runCurrent()
@@ -93,9 +95,8 @@
fun spatialAudio_noHeadTracking_available() {
with(kosmos) {
testScope.runTest {
- localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice)
- spatializerRepository.defaultHeadTrackingAvailable = false
- spatializerRepository.defaultSpatialAudioAvailable = true
+ spatializerRepository.setIsSpatialAudioAvailable(headset, true)
+ spatializerRepository.setIsHeadTrackingAvailable(headset, false)
val isAvailable by collectLastValue(underTest.isAvailable())
runCurrent()
@@ -109,9 +110,8 @@
fun spatialAudio_headTracking_available() {
with(kosmos) {
testScope.runTest {
- localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice)
- spatializerRepository.defaultHeadTrackingAvailable = true
- spatializerRepository.defaultSpatialAudioAvailable = true
+ spatializerRepository.setIsSpatialAudioAvailable(headset, true)
+ spatializerRepository.setIsHeadTrackingAvailable(headset, true)
val isAvailable by collectLastValue(underTest.isAvailable())
runCurrent()
@@ -125,8 +125,8 @@
fun spatialAudio_headTracking_noDevice_unavailable() {
with(kosmos) {
testScope.runTest {
- spatializerRepository.defaultHeadTrackingAvailable = true
- spatializerRepository.defaultSpatialAudioAvailable = true
+ localMediaRepository.updateCurrentConnectedDevice(null)
+ spatializerRepository.setIsSpatialAudioAvailable(headset, false)
val isAvailable by collectLastValue(underTest.isAvailable())
runCurrent()
@@ -135,4 +135,13 @@
}
}
}
+
+ private companion object {
+ val headset =
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ "test_address"
+ )
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
index 7c6ab9d..281b03d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
@@ -26,6 +26,7 @@
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.spatializerInteractor
@@ -37,6 +38,7 @@
import com.android.systemui.volume.mediaController
import com.android.systemui.volume.mediaControllerRepository
import com.android.systemui.volume.mediaOutputInteractor
+import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -74,16 +76,6 @@
mediaControllerRepository.setActiveLocalMediaController(mediaController)
- spatializerRepository.setIsSpatialAudioAvailable(
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLE_HEADSET,
- "test_address"
- ),
- true
- )
- spatializerRepository.defaultHeadTrackingAvailable = true
-
underTest =
SpatialAudioComponentInteractor(
mediaOutputInteractor,
@@ -97,6 +89,7 @@
fun setEnabled_changesIsEnabled() {
with(kosmos) {
testScope.runTest {
+ spatializerRepository.setIsSpatialAudioAvailable(headset, true)
val values by collectValues(underTest.isEnabled)
underTest.setEnabled(SpatialAudioEnabledModel.Disabled)
@@ -117,4 +110,92 @@
}
}
}
+
+ @Test
+ fun connectedDeviceSupports_isAvailable_SpatialAudio() {
+ with(kosmos) {
+ testScope.runTest {
+ spatializerRepository.setIsSpatialAudioAvailable(headset, true)
+
+ val isAvailable by collectLastValue(underTest.isAvailable)
+
+ assertThat(isAvailable)
+ .isInstanceOf(SpatialAudioAvailabilityModel.SpatialAudio::class.java)
+ }
+ }
+ }
+
+ @Test
+ fun connectedDeviceSupportsHeadTracking_isAvailable_HeadTracking() {
+ with(kosmos) {
+ testScope.runTest {
+ spatializerRepository.setIsSpatialAudioAvailable(headset, true)
+ spatializerRepository.setIsHeadTrackingAvailable(headset, true)
+
+ val isAvailable by collectLastValue(underTest.isAvailable)
+
+ assertThat(isAvailable)
+ .isInstanceOf(SpatialAudioAvailabilityModel.HeadTracking::class.java)
+ }
+ }
+ }
+
+ @Test
+ fun connectedDeviceDoesntSupport_isAvailable_Unavailable() {
+ with(kosmos) {
+ testScope.runTest {
+ spatializerRepository.setIsSpatialAudioAvailable(headset, false)
+
+ val isAvailable by collectLastValue(underTest.isAvailable)
+
+ assertThat(isAvailable)
+ .isInstanceOf(SpatialAudioAvailabilityModel.Unavailable::class.java)
+ }
+ }
+ }
+
+ @Test
+ fun noConnectedDeviceBuiltinSupports_isAvailable_SpatialAudio() {
+ with(kosmos) {
+ testScope.runTest {
+ localMediaRepository.updateCurrentConnectedDevice(null)
+ spatializerRepository.setIsSpatialAudioAvailable(builtinSpeaker, true)
+
+ val isAvailable by collectLastValue(underTest.isAvailable)
+
+ assertThat(isAvailable)
+ .isInstanceOf(SpatialAudioAvailabilityModel.SpatialAudio::class.java)
+ }
+ }
+ }
+
+ @Test
+ fun noConnectedDeviceBuiltinDoesntSupport_isAvailable_Unavailable() {
+ with(kosmos) {
+ testScope.runTest {
+ localMediaRepository.updateCurrentConnectedDevice(null)
+ spatializerRepository.setIsSpatialAudioAvailable(builtinSpeaker, false)
+
+ val isAvailable by collectLastValue(underTest.isAvailable)
+
+ assertThat(isAvailable)
+ .isInstanceOf(SpatialAudioAvailabilityModel.Unavailable::class.java)
+ }
+ }
+ }
+
+ private companion object {
+ val headset =
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ "test_address"
+ )
+ val builtinSpeaker =
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+ ""
+ )
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index c08b083..69aa909 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -77,7 +77,7 @@
// settings is expanded.
public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
// Winscope tracing is enabled
- public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
+ public static final int SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION = 1 << 12;
// The Assistant gesture should be constrained. It is up to the launcher implementation to
// decide how to constrain it
public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
@@ -148,7 +148,7 @@
SYSUI_STATE_OVERVIEW_DISABLED,
SYSUI_STATE_HOME_DISABLED,
SYSUI_STATE_SEARCH_DISABLED,
- SYSUI_STATE_TRACING_ENABLED,
+ SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION,
SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
SYSUI_STATE_BUBBLES_EXPANDED,
SYSUI_STATE_DIALOG_SHOWING,
@@ -211,8 +211,8 @@
if ((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0) {
str.add("a11y_long_click");
}
- if ((flags & SYSUI_STATE_TRACING_ENABLED) != 0) {
- str.add("tracing");
+ if ((flags & SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION) != 0) {
+ str.add("disable_gesture_split_invocation");
}
if ((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0) {
str.add("asst_gesture_constrain");
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index c0ae4a1..7f9ae5e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -250,6 +250,10 @@
@Override
protected void onViewAttached() {
mStatusArea = mView.findViewById(R.id.keyguard_status_area);
+ if (migrateClocksToBlueprint()) {
+ return;
+ }
+
mStatusArea.addOnLayoutChangeListener(mStatusAreaLayoutChangeListener);
mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
mConfigurationController.addCallback(mConfigurationListener);
@@ -257,6 +261,10 @@
@Override
protected void onViewDetached() {
+ if (migrateClocksToBlueprint()) {
+ return;
+ }
+
mStatusArea.removeOnLayoutChangeListener(mStatusAreaLayoutChangeListener);
mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
mConfigurationController.removeCallback(mConfigurationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index 270fedc..0c9712d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -21,17 +21,10 @@
import android.annotation.SuppressLint;
import android.content.ComponentCallbacks;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.SettingsStringUtil;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
@@ -434,22 +427,6 @@
onPositionChanged();
}
- void gotoEditScreen() {
- if (!Flags.floatingMenuDragToEdit()) {
- return;
- }
- mMenuAnimationController.flingMenuThenSpringToEdge(
- getMenuPosition().x, 100f, 0f);
-
- Intent intent = getIntentForEditScreen();
- PackageManager packageManager = getContext().getPackageManager();
- List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,
- PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY));
- if (!activities.isEmpty()) {
- mContext.startActivity(intent);
- }
- }
-
void incrementTexMetricForAllTargets(String metric) {
if (!Flags.floatingMenuDragToEdit()) {
return;
@@ -464,23 +441,6 @@
Counter.logIncrementWithUid(metric, uid);
}
- Intent getIntentForEditScreen() {
- List<String> targets = new SettingsStringUtil.ColonDelimitedSet.OfStrings(
- mSecureSettings.getStringForUser(
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
- UserHandle.USER_CURRENT)).stream().toList();
-
- Intent intent = new Intent(
- Settings.ACTION_ACCESSIBILITY_SHORTCUT_SETTINGS);
- Bundle args = new Bundle();
- Bundle fragmentArgs = new Bundle();
- fragmentArgs.putStringArray("targets", targets.toArray(new String[0]));
- args.putBundle(":settings:show_fragment_args", fragmentArgs);
- intent.replaceExtras(args);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- return intent;
- }
-
private InstantInsetLayerDrawable getContainerViewInsetLayer() {
return (InstantInsetLayerDrawable) getBackground();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 97e38f4..85bf784 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -36,19 +36,24 @@
import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.app.NotificationManager;
+import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.SettingsStringUtil;
import android.util.ArraySet;
import android.view.MotionEvent;
import android.view.View;
@@ -120,6 +125,7 @@
private final MenuAnimationController mMenuAnimationController;
private final AccessibilityManager mAccessibilityManager;
private final NotificationManager mNotificationManager;
+ private StatusBarManager mStatusBarManager;
private final MenuNotificationFactory mNotificationFactory;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final IAccessibilityFloatingMenu mFloatingMenu;
@@ -246,6 +252,7 @@
mDismissView.getCircle().setId(R.id.action_remove_menu);
mNotificationFactory = new MenuNotificationFactory(context);
mNotificationManager = context.getSystemService(NotificationManager.class);
+ mStatusBarManager = context.getSystemService(StatusBarManager.class);
if (Flags.floatingMenuDragToEdit()) {
mDragToInteractAnimationController = new DragToInteractAnimationController(
@@ -488,7 +495,7 @@
mMenuView.incrementTexMetricForAllTargets(TEX_METRIC_DISMISS);
} else if (id == R.id.action_edit
&& Flags.floatingMenuDragToEdit()) {
- mMenuView.gotoEditScreen();
+ gotoEditScreen();
mMenuView.incrementTexMetricForAllTargets(TEX_METRIC_EDIT);
}
mDismissView.hide();
@@ -497,6 +504,40 @@
id, /* scaleUp= */ false);
}
+ void gotoEditScreen() {
+ if (!Flags.floatingMenuDragToEdit()) {
+ return;
+ }
+ mMenuAnimationController.flingMenuThenSpringToEdge(
+ mMenuView.getMenuPosition().x, 100f, 0f);
+
+ Intent intent = getIntentForEditScreen();
+ PackageManager packageManager = getContext().getPackageManager();
+ List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,
+ PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY));
+ if (!activities.isEmpty()) {
+ mContext.startActivity(intent);
+ mStatusBarManager.collapsePanels();
+ }
+ }
+
+ Intent getIntentForEditScreen() {
+ List<String> targets = new SettingsStringUtil.ColonDelimitedSet.OfStrings(
+ mSecureSettings.getStringForUser(
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ UserHandle.USER_CURRENT)).stream().toList();
+
+ Intent intent = new Intent(
+ Settings.ACTION_ACCESSIBILITY_SHORTCUT_SETTINGS);
+ Bundle args = new Bundle();
+ Bundle fragmentArgs = new Bundle();
+ fragmentArgs.putStringArray("targets", targets.toArray(new String[0]));
+ args.putBundle(":settings:show_fragment_args", fragmentArgs);
+ intent.replaceExtras(args);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return intent;
+ }
+
private CharSequence getMigrationMessage() {
final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 8c87b0c..893887f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -93,6 +93,8 @@
val keyguardAuthenticatedPrimaryAuth: Flow<Int> = repository.keyguardAuthenticatedPrimaryAuth
val keyguardAuthenticatedBiometrics: Flow<Boolean> =
repository.keyguardAuthenticatedBiometrics.filterNotNull()
+ val keyguardAuthenticatedBiometricsHandled: Flow<Unit> =
+ repository.keyguardAuthenticatedBiometrics.filter { it == null }.map {}
val userRequestedBouncerWhenAlreadyAuthenticated: Flow<Int> =
repository.userRequestedBouncerWhenAlreadyAuthenticated.filterNotNull()
val isShowing: StateFlow<Boolean> = repository.primaryBouncerShow
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
index 1c9d1f0..e1fea5f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -23,12 +23,14 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.bouncer.ui.BouncerViewDelegate
import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
/** Models UI state for the lock screen bouncer; handles user input. */
+@ExperimentalCoroutinesApi
class KeyguardBouncerViewModel
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 98c8205..ef686f9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -30,6 +30,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import com.android.systemui.util.settings.SystemSettings
import javax.inject.Inject
@@ -38,7 +39,6 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
@@ -108,23 +108,29 @@
bgScope.launch {
combine(
communalInteractor.desiredScene,
- keyguardInteractor.isDreaming,
// Emit a value on start so the combine starts.
communalInteractor.userActivity.emitOnStart()
- ) { scene, isDreaming, _ ->
+ ) { scene, _ ->
// Time out should run whenever we're dreaming and the hub is open, even if not
// docked.
- scene == CommunalScenes.Communal && isDreaming
+ scene == CommunalScenes.Communal
}
- // collectLatest cancels the previous action block when new values arrive, so any
+ // mapLatest cancels the previous action block when new values arrive, so any
// already running timeout gets cancelled when conditions change or user interaction
// is detected.
- .collectLatest { shouldTimeout ->
+ .mapLatest { shouldTimeout ->
if (!shouldTimeout) {
- return@collectLatest
+ return@mapLatest false
}
+
delay(screenTimeout.milliseconds)
- communalInteractor.onSceneChanged(CommunalScenes.Blank)
+ true
+ }
+ .sample(keyguardInteractor.isDreaming, ::Pair)
+ .collect { (shouldTimeout, isDreaming) ->
+ if (isDreaming && shouldTimeout) {
+ communalInteractor.onSceneChanged(CommunalScenes.Blank)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 0e487d2..9c68c45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -159,6 +159,11 @@
lastAnimator?.cancel()
lastAnimator = info.animator
+ // Cancel any existing manual transitions
+ updateTransitionId?.let { uuid ->
+ updateTransition(uuid, lastStep.value, TransitionState.CANCELED)
+ }
+
info.animator?.let { animator ->
// An animator was provided, so use it to run the transition
animator.setFloatValues(startingValue, 1f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BurnInModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BurnInModel.kt
index 02d1471..0c8ddee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BurnInModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BurnInModel.kt
@@ -20,6 +20,6 @@
data class BurnInModel(
val translationX: Int = 0,
val translationY: Int = 0,
- val scale: Float = 0f,
+ val scale: Float = 1f,
val scaleClockOnly: Boolean = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index d400210..3a2781c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -102,9 +102,16 @@
launch {
viewModel.scrimAlpha.collect {
+ val wasVisible = alternateBouncerViewContainer.visibility == View.VISIBLE
alternateBouncerViewContainer.visibility =
if (it < .1f) View.INVISIBLE else View.VISIBLE
scrim.viewAlpha = it
+ if (
+ wasVisible && alternateBouncerViewContainer.visibility == View.INVISIBLE
+ ) {
+ // view is no longer visible
+ viewModel.hideAlternateBouncer()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index 2526f0a..10a9e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -89,4 +89,8 @@
fun showPrimaryBouncer() {
statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true)
}
+
+ fun hideAlternateBouncer() {
+ statusBarKeyguardViewManager.hideAlternateBouncer(false)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index abf2372..6042117 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -72,7 +72,11 @@
duration = TO_LOCKSCREEN_DURATION,
onStep = { value -> -translatePx + value * translatePx },
interpolator = EMPHASIZED,
- onCancel = { -translatePx.toFloat() },
+ // Move notifications back to their original position since they can be
+ // accessed from the shade, and also keyguard elements in case the animation
+ // is cancelled.
+ onFinish = { 0f },
+ onCancel = { 0f },
name = "GLANCEABLE_HUB->LOCKSCREEN: keyguardTranslationX"
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
index b7f7b06..5cbc1d4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
@@ -71,7 +71,8 @@
duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
onStep = { value -> value * translatePx },
// Move notifications back to their original position since they can be
- // accessed from the shade.
+ // accessed from the shade, and also keyguard elements in case the animation
+ // is cancelled.
onFinish = { 0f },
onCancel = { 0f },
interpolator = EMPHASIZED,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt
index dcae088..1247854 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt
@@ -30,7 +30,7 @@
private val bluetoothAutoOnRepository: BluetoothAutoOnRepository,
) {
- val isEnabled = bluetoothAutoOnRepository.getValue.map { it == ENABLED }.distinctUntilChanged()
+ val isEnabled = bluetoothAutoOnRepository.isAutoOn.map { it == ENABLED }.distinctUntilChanged()
/**
* Checks if the auto on value is present in the repository.
@@ -49,7 +49,7 @@
Log.e(TAG, "Trying to set toggle value while feature not available.")
} else {
val newValue = if (value) ENABLED else DISABLED
- bluetoothAutoOnRepository.setValue(newValue)
+ bluetoothAutoOnRepository.setAutoOn(newValue)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt
index e17b4d3..f97fc38 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt
@@ -16,8 +16,6 @@
package com.android.systemui.qs.tiles.dialog.bluetooth
-import android.os.UserHandle
-import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -27,17 +25,21 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
-/** Repository class responsible for managing the Bluetooth Auto-On feature settings. */
-// TODO(b/316822488): Handle multi-user
+/**
+ * Repository class responsible for managing the Bluetooth Auto-On feature settings for the current
+ * user.
+ */
@SysUISingleton
class BluetoothAutoOnRepository
@Inject
@@ -47,21 +49,24 @@
@Application private val coroutineScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) {
- // Flow representing the auto on setting value
- internal val getValue: Flow<Int> =
- secureSettings
- .observerFlow(UserHandle.USER_SYSTEM, SETTING_NAME)
- .onStart { emit(Unit) }
- .map {
- if (userRepository.getSelectedUserInfo().id != UserHandle.USER_SYSTEM) {
- Log.i(TAG, "Current user is not USER_SYSTEM. Multi-user is not supported")
- return@map UNSET
- }
- secureSettings.getIntForUser(SETTING_NAME, UNSET, UserHandle.USER_SYSTEM)
+
+ // Flow representing the auto on setting value for the current user
+ @OptIn(ExperimentalCoroutinesApi::class)
+ internal val isAutoOn: StateFlow<Int> =
+ userRepository.selectedUserInfo
+ .flatMapLatest { userInfo ->
+ secureSettings
+ .observerFlow(userInfo.id, SETTING_NAME)
+ .onStart { emit(Unit) }
+ .map { secureSettings.getIntForUser(SETTING_NAME, UNSET, userInfo.id) }
}
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
- .shareIn(coroutineScope, SharingStarted.WhileSubscribed(replayExpirationMillis = 0))
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(replayExpirationMillis = 0),
+ UNSET
+ )
/**
* Checks if the auto on setting value is ever set for the current user.
@@ -70,12 +75,11 @@
*/
suspend fun isValuePresent(): Boolean =
withContext(backgroundDispatcher) {
- if (userRepository.getSelectedUserInfo().id != UserHandle.USER_SYSTEM) {
- Log.i(TAG, "Current user is not USER_SYSTEM. Multi-user is not supported")
- false
- } else {
- secureSettings.getIntForUser(SETTING_NAME, UNSET, UserHandle.USER_SYSTEM) != UNSET
- }
+ secureSettings.getIntForUser(
+ SETTING_NAME,
+ UNSET,
+ userRepository.getSelectedUserInfo().id
+ ) != UNSET
}
/**
@@ -83,18 +87,17 @@
*
* @param value The new setting value to be applied.
*/
- suspend fun setValue(value: Int) {
+ suspend fun setAutoOn(value: Int) {
withContext(backgroundDispatcher) {
- if (userRepository.getSelectedUserInfo().id != UserHandle.USER_SYSTEM) {
- Log.i(TAG, "Current user is not USER_SYSTEM. Multi-user is not supported")
- } else {
- secureSettings.putIntForUser(SETTING_NAME, value, UserHandle.USER_SYSTEM)
- }
+ secureSettings.putIntForUser(
+ SETTING_NAME,
+ value,
+ userRepository.getSelectedUserInfo().id
+ )
}
}
companion object {
- private const val TAG = "BluetoothAutoOnRepository"
const val SETTING_NAME = "bluetooth_automatic_turn_on"
const val UNSET = -1
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index d0ff338..9ae049d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -37,7 +37,6 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_TRANSITION;
@@ -115,8 +114,6 @@
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInterface;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -128,6 +125,8 @@
import javax.inject.Inject;
import javax.inject.Provider;
+import dagger.Lazy;
+
/**
* Class to send information from overview to launcher with a binder.
*/
@@ -670,8 +669,7 @@
// Listen for tracing state changes
@Override
public void onTracingStateChanged(boolean enabled) {
- mSysUiState.setFlag(SYSUI_STATE_TRACING_ENABLED, enabled)
- .commitUpdate(mContext.getDisplayId());
+ // TODO(b/286509643) Cleanup callers of this; Unused downstream
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index ff18a11..7313a49 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -28,8 +28,8 @@
import android.widget.Button
import android.widget.PopupMenu
import android.widget.Switch
-import androidx.annotation.AnyThread
import androidx.annotation.MainThread
+import androidx.annotation.WorkerThread
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
@@ -74,6 +74,7 @@
@SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch
private lateinit var issueTypeButton: Button
+ private var hasSelectedIssueType: Boolean = false
@MainThread
override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
@@ -82,15 +83,21 @@
setTitle(context.getString(R.string.qs_record_issue_label))
setIcon(R.drawable.qs_record_issue_icon_off)
setNegativeButton(R.string.cancel) { _, _ -> dismiss() }
- setPositiveButton(R.string.qs_record_issue_start) { _, _ ->
- onStarted.accept(
- IssueRecordingConfig(
- screenRecordSwitch.isChecked,
- true /* TODO: Base this on issueType selected */
- )
- )
- dismiss()
- }
+ setPositiveButton(
+ R.string.qs_record_issue_start,
+ { _, _ ->
+ if (hasSelectedIssueType) {
+ onStarted.accept(
+ IssueRecordingConfig(
+ screenRecordSwitch.isChecked,
+ true /* TODO: Base this on issueType selected */
+ )
+ )
+ dismiss()
+ }
+ },
+ false
+ )
}
}
@@ -104,49 +111,47 @@
screenRecordSwitch = requireViewById(R.id.screenrecord_switch)
screenRecordSwitch.setOnCheckedChangeListener { _, isEnabled ->
- onScreenRecordSwitchClicked(context, isEnabled)
+ if (isEnabled) {
+ bgExecutor.execute { onScreenRecordSwitchClicked() }
+ }
}
issueTypeButton = requireViewById(R.id.issue_type_button)
issueTypeButton.setOnClickListener { onIssueTypeClicked(context) }
}
}
- @AnyThread
- private fun onScreenRecordSwitchClicked(context: Context, isEnabled: Boolean) {
- if (!isEnabled) return
-
- bgExecutor.execute {
- if (
- flags.isEnabled(WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES) &&
- devicePolicyResolver
- .get()
- .isScreenCaptureCompletelyDisabled(UserHandle.of(userTracker.userId))
- ) {
- mainExecutor.execute {
- screenCaptureDisabledDialogDelegate.createDialog().show()
- screenRecordSwitch.isChecked = false
- }
- return@execute
+ @WorkerThread
+ private fun onScreenRecordSwitchClicked() {
+ if (
+ flags.isEnabled(WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES) &&
+ devicePolicyResolver
+ .get()
+ .isScreenCaptureCompletelyDisabled(UserHandle.of(userTracker.userId))
+ ) {
+ mainExecutor.execute {
+ screenCaptureDisabledDialogDelegate.createDialog().show()
+ screenRecordSwitch.isChecked = false
}
+ return
+ }
- mediaProjectionMetricsLogger.notifyProjectionInitiated(
- userTracker.userId,
- SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER
- )
+ mediaProjectionMetricsLogger.notifyProjectionInitiated(
+ userTracker.userId,
+ SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER
+ )
- if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) {
- val prefs =
- userFileManager.getSharedPreferences(
- RecordIssueTile.TILE_SPEC,
- Context.MODE_PRIVATE,
- userTracker.userId
- )
- if (!prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)) {
- mainExecutor.execute {
- ScreenCapturePermissionDialogDelegate(factory, prefs).createDialog().apply {
- setOnCancelListener { screenRecordSwitch.isChecked = false }
- show()
- }
+ if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) {
+ val prefs =
+ userFileManager.getSharedPreferences(
+ RecordIssueTile.TILE_SPEC,
+ Context.MODE_PRIVATE,
+ userTracker.userId
+ )
+ if (!prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)) {
+ mainExecutor.execute {
+ ScreenCapturePermissionDialogDelegate(factory, prefs).createDialog().apply {
+ setOnCancelListener { screenRecordSwitch.isChecked = false }
+ show()
}
}
}
@@ -174,5 +179,6 @@
setForceShowIcon(true)
show()
}
+ hasSelectedIssueType = true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
index 2707ed8..b77748e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
@@ -28,6 +28,7 @@
import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_OCCLUDED
import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_SHOWING
import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_LOCKED_SHADE
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_USER_SETUP_INCOMPLETE
import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_EXPECTED_TO_HUN
import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NOT_IMPORTANT_ENOUGH
import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_FULL_SCREEN_INTENT
@@ -101,6 +102,7 @@
FSI_KEYGUARD_OCCLUDED(true, "keyguard is occluded"),
FSI_LOCKED_SHADE(true, "locked shade"),
FSI_DEVICE_NOT_PROVISIONED(true, "device not provisioned"),
+ FSI_USER_SETUP_INCOMPLETE(true, "user setup incomplete"),
NO_FSI_NO_HUN_OR_KEYGUARD(
false,
"no HUN or keyguard",
@@ -189,6 +191,10 @@
return FSI_DEVICE_NOT_PROVISIONED
}
+ if (!deviceProvisionedController.isCurrentUserSetup) {
+ return FSI_USER_SETUP_INCOMPLETE
+ }
+
return NO_FSI_NO_HUN_OR_KEYGUARD
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index b0155f1..c084482 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -104,7 +104,11 @@
/**
* The device is not provisioned, launch FSI.
*/
- FSI_NOT_PROVISIONED(true);
+ FSI_NOT_PROVISIONED(true),
+ /**
+ * The current user has not completed setup, launch FSI.
+ */
+ FSI_USER_SETUP_INCOMPLETE(true);
public final boolean shouldLaunch;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index dc9eeb3..a655c72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -362,6 +362,12 @@
suppressedByDND);
}
+ // The current user hasn't completed setup, launch FSI.
+ if (!mDeviceProvisionedController.isCurrentUserSetup()) {
+ return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_USER_SETUP_INCOMPLETE,
+ suppressedByDND);
+ }
+
// Detect the case determined by b/231322873 to launch FSI while device is in use,
// as blocked by the correct implementation, and report the event.
return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 77e9425..9479762 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -49,6 +49,7 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.SystemClock;
import android.os.Trace;
import android.provider.Settings;
import android.util.AttributeSet;
@@ -208,6 +209,9 @@
private float mQsExpansionFraction;
private final int mSplitShadeMinContentHeight;
private String mLastUpdateSidePaddingDumpString;
+ private long mLastUpdateSidePaddingElapsedRealtime;
+ private String mLastInitViewDumpString;
+ private long mLastInitViewElapsedRealtime;
/**
* The algorithm which calculates the properties for our children
@@ -887,17 +891,34 @@
mOverflingDistance = configuration.getScaledOverflingDistance();
Resources res = context.getResources();
+ final boolean isSmallScreenLandscape = res.getBoolean(R.bool.is_small_screen_landscape);
boolean useSmallLandscapeLockscreenResources = mIsSmallLandscapeLockscreenEnabled
- && res.getBoolean(R.bool.is_small_screen_landscape);
+ && isSmallScreenLandscape;
// TODO (b/293252410) remove condition here when flag is launched
// Instead update the config_skinnyNotifsInLandscape to be false whenever
// is_small_screen_landscape is true. Then, only use the config_skinnyNotifsInLandscape.
+ final boolean configSkinnyNotifsInLandscape = res.getBoolean(
+ R.bool.config_skinnyNotifsInLandscape);
if (useSmallLandscapeLockscreenResources) {
mSkinnyNotifsInLandscape = false;
} else {
- mSkinnyNotifsInLandscape = res.getBoolean(
- R.bool.config_skinnyNotifsInLandscape);
+ mSkinnyNotifsInLandscape = configSkinnyNotifsInLandscape;
}
+
+ mLastInitViewDumpString =
+ "mIsSmallLandscapeLockscreenEnabled=" + mIsSmallLandscapeLockscreenEnabled
+ + " isSmallScreenLandscape=" + isSmallScreenLandscape
+ + " useSmallLandscapeLockscreenResources="
+ + useSmallLandscapeLockscreenResources
+ + " skinnyNotifsInLandscape=" + configSkinnyNotifsInLandscape
+ + " mSkinnyNotifsInLandscape=" + mSkinnyNotifsInLandscape;
+ mLastInitViewElapsedRealtime = SystemClock.elapsedRealtime();
+
+ if (DEBUG_UPDATE_SIDE_PADDING) {
+ Log.v(TAG, "initView @ elapsedRealtime " + mLastInitViewElapsedRealtime + ": "
+ + mLastInitViewDumpString);
+ }
+
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
mStackScrollAlgorithm.initView(context);
mStateAnimator.initView(context);
@@ -925,22 +946,33 @@
mLastUpdateSidePaddingDumpString = "viewWidth=" + viewWidth
+ " skinnyNotifsInLandscape=" + mSkinnyNotifsInLandscape
+ " orientation=" + orientation;
+ mLastUpdateSidePaddingElapsedRealtime = SystemClock.elapsedRealtime();
if (DEBUG_UPDATE_SIDE_PADDING) {
- Log.v(TAG, "updateSidePadding: " + mLastUpdateSidePaddingDumpString);
+ Log.v(TAG,
+ "updateSidePadding @ elapsedRealtime " + mLastUpdateSidePaddingElapsedRealtime
+ + ": " + mLastUpdateSidePaddingDumpString);
}
- if (viewWidth == 0 || !mSkinnyNotifsInLandscape) {
+ if (viewWidth == 0) {
+ Log.e(TAG, "updateSidePadding: viewWidth is zero");
mSidePaddings = mMinimumPaddings;
return;
}
- // Portrait is easy, just use the dimen for paddings
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
mSidePaddings = mMinimumPaddings;
return;
}
+ if (mShouldUseSplitNotificationShade) {
+ if (mSkinnyNotifsInLandscape) {
+ Log.e(TAG, "updateSidePadding: mSkinnyNotifsInLandscape has betrayed us!");
+ }
+ mSidePaddings = mMinimumPaddings;
+ return;
+ }
+
final int innerWidth = viewWidth - mMinimumPaddings * 2;
final int qsTileWidth = (innerWidth - mQsTilePadding * 3) / 4;
mSidePaddings = mMinimumPaddings + qsTileWidth + mQsTilePadding;
@@ -4884,6 +4916,7 @@
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
pw.println("Internal state:");
DumpUtilsKt.withIncreasedIndent(pw, () -> {
println(pw, "pulsing", mPulsing);
@@ -4914,7 +4947,17 @@
println(pw, "minimumPaddings", mMinimumPaddings);
println(pw, "qsTilePadding", mQsTilePadding);
println(pw, "sidePaddings", mSidePaddings);
+ println(pw, "elapsedRealtime", elapsedRealtime);
+ println(pw, "lastInitView", mLastInitViewDumpString);
+ println(pw, "lastInitViewElapsedRealtime", mLastInitViewElapsedRealtime);
+ println(pw, "lastInitViewMillisAgo", elapsedRealtime - mLastInitViewElapsedRealtime);
+ println(pw, "shouldUseSplitNotificationShade", mShouldUseSplitNotificationShade);
println(pw, "lastUpdateSidePadding", mLastUpdateSidePaddingDumpString);
+ println(pw, "lastUpdateSidePaddingElapsedRealtime",
+ mLastUpdateSidePaddingElapsedRealtime);
+ println(pw, "lastUpdateSidePaddingMillisAgo",
+ elapsedRealtime - mLastUpdateSidePaddingElapsedRealtime);
+ println(pw, "isSmallLandscapeLockscreenEnabled", mIsSmallLandscapeLockscreenEnabled);
mNotificationStackSizeCalculator.dump(pw, args);
});
pw.println();
@@ -5482,6 +5525,7 @@
mAmbientState.setUseSplitShade(split);
updateDismissBehavior();
updateUseRoundedRectClipping();
+ requestLayout();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7dd328a..14e934fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -76,6 +76,8 @@
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
import com.android.systemui.keyguard.shared.model.DismissAction;
import com.android.systemui.keyguard.shared.model.KeyguardDone;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.TaskbarDelegate;
@@ -105,6 +107,8 @@
import dagger.Lazy;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -116,6 +120,7 @@
import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
+import kotlinx.coroutines.Job;
/**
* Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
@@ -163,6 +168,9 @@
private final Lazy<ShadeController> mShadeController;
private final Lazy<SceneInteractor> mSceneInteractorLazy;
+ private Job mListenForAlternateBouncerTransitionSteps = null;
+ private Job mListenForKeyguardAuthenticatedBiometricsHandled = null;
+
// Local cache of expansion events, to avoid duplicates
private float mFraction = -1f;
private boolean mTracking = false;
@@ -491,6 +499,26 @@
mDockManager.addListener(mDockEventListener);
mIsDocked = mDockManager.isDocked();
}
+ if (mListenForAlternateBouncerTransitionSteps != null) {
+ mListenForAlternateBouncerTransitionSteps.cancel(null);
+ }
+ mListenForAlternateBouncerTransitionSteps = null;
+ if (mListenForKeyguardAuthenticatedBiometricsHandled != null) {
+ mListenForKeyguardAuthenticatedBiometricsHandled.cancel(null);
+ }
+ mListenForKeyguardAuthenticatedBiometricsHandled = null;
+ if (!DeviceEntryUdfpsRefactor.isEnabled()) {
+ mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow(
+ mKeyguardTransitionInteractor.transitionStepsFromState(
+ KeyguardState.ALTERNATE_BOUNCER),
+ this::consumeFromAlternateBouncerTransitionSteps
+ );
+
+ mListenForKeyguardAuthenticatedBiometricsHandled = mJavaAdapter.alwaysCollectFlow(
+ mPrimaryBouncerInteractor.getKeyguardAuthenticatedBiometricsHandled(),
+ this::consumeKeyguardAuthenticatedBiometricsHandled
+ );
+ }
if (KeyguardWmStateRefactor.isEnabled()) {
// Show the keyguard views whenever we've told WM that the lockscreen is visible.
@@ -514,6 +542,22 @@
}
}
+ @VisibleForTesting
+ void consumeFromAlternateBouncerTransitionSteps(TransitionStep step) {
+ hideAlternateBouncer(false);
+ }
+
+ /**
+ * Required without fix for b/328643370: missing AlternateBouncer (when occluded) => Gone
+ * transition.
+ */
+ @VisibleForTesting
+ void consumeKeyguardAuthenticatedBiometricsHandled(Unit handled) {
+ if (mAlternateBouncerInteractor.isVisibleState()) {
+ hideAlternateBouncer(false);
+ }
+ }
+
private void consumeShowStatusBarKeyguardView(boolean show) {
if (show != mLastShowing) {
if (show) {
@@ -1458,7 +1502,6 @@
mPrimaryBouncerInteractor.notifyKeyguardAuthenticatedBiometrics(strongAuth);
if (mAlternateBouncerInteractor.isVisibleState()) {
- hideAlternateBouncer(false);
executeAfterKeyguardGoneAction();
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
index a32b75a..298ca67 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
@@ -18,8 +18,9 @@
import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
-import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.media.BluetoothMediaDevice
+import com.android.settingslib.media.MediaDevice
+import com.android.settingslib.media.PhoneMediaDevice
import com.android.settingslib.media.domain.interactor.SpatializerInteractor
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
@@ -54,12 +55,9 @@
private val currentAudioDeviceAttributes: StateFlow<AudioDeviceAttributes?> =
mediaOutputInteractor.currentConnectedDevice
.map { mediaDevice ->
- mediaDevice ?: return@map null
- val btDevice: CachedBluetoothDevice =
- (mediaDevice as? BluetoothMediaDevice)?.cachedDevice ?: return@map null
- btDevice.getAudioDeviceAttributes()
+ if (mediaDevice == null) builtinSpeaker else mediaDevice.getAudioDeviceAttributes()
}
- .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), builtinSpeaker)
/**
* Returns spatial audio availability model. It can be:
@@ -137,34 +135,50 @@
changes.emit(Unit)
}
- private suspend fun CachedBluetoothDevice.getAudioDeviceAttributes(): AudioDeviceAttributes? {
- return listOf(
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLE_HEADSET,
- address
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLE_SPEAKER,
- address
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLE_BROADCAST,
- address
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
- address
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_HEARING_AID,
- address
- )
+ private suspend fun MediaDevice.getAudioDeviceAttributes(): AudioDeviceAttributes? {
+ when (this) {
+ is PhoneMediaDevice -> return builtinSpeaker
+ is BluetoothMediaDevice -> {
+ val device = cachedDevice ?: return null
+ return listOf(
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ device.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ device.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_BROADCAST,
+ device.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ device.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_HEARING_AID,
+ device.address,
+ )
+ )
+ .firstOrNull { spatializerInteractor.isSpatialAudioAvailable(it) }
+ }
+ else -> return null
+ }
+ }
+
+ private companion object {
+ val builtinSpeaker =
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+ ""
)
- .firstOrNull { spatializerInteractor.isSpatialAudioAvailable(it) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 7674fe9..e18bc04 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -21,6 +21,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
@@ -260,6 +261,13 @@
splitScreen.goToFullscreenFromSplit();
}
});
+ splitScreen.registerSplitAnimationListener(new SplitScreen.SplitInvocationListener() {
+ @Override
+ public void onSplitAnimationInvoked(boolean animationRunning) {
+ mSysUiState.setFlag(SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION, animationRunning)
+ .commitUpdate(mDisplayTracker.getDefaultDisplayId());
+ }
+ }, mSysUiMainExecutor);
}
@VisibleForTesting
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index e893eb1..11fe862 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -30,12 +30,14 @@
import static org.mockito.Mockito.when;
import android.animation.AnimatorTestRule;
+import android.platform.test.annotations.DisableFlags;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import com.android.app.animation.Interpolators;
+import com.android.systemui.Flags;
import com.android.systemui.animation.ViewHierarchyAnimator;
import com.android.systemui.plugins.clocks.ClockConfig;
import com.android.systemui.plugins.clocks.ClockController;
@@ -80,6 +82,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
public void onLocaleListChangedNotifiesClockSwitchController() {
ArgumentCaptor<ConfigurationListener> configurationListenerArgumentCaptor =
ArgumentCaptor.forClass(ConfigurationListener.class);
@@ -239,6 +242,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
public void statusAreaHeightChange_animatesHeightOutputChange() {
// Init & Capture Layout Listener
mController.onInit();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 976cd5bd..1f7d033 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -90,12 +90,12 @@
mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
mSecureSettings));
mMenuView.setTranslationY(halfScreenHeight);
- doNothing().when(mMenuView).gotoEditScreen();
mMenuViewLayer = spy(new MenuViewLayer(
mContext, stubWindowManager, mAccessibilityManager,
stubMenuViewModel, stubMenuViewAppearance, mMenuView,
mock(IAccessibilityFloatingMenu.class), mSecureSettings));
+ doNothing().when(mMenuViewLayer).gotoEditScreen();
doReturn(mDraggableBounds).when(mMenuView).getMenuDraggableBounds();
mStubListView = new RecyclerView(mContext);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index f76957f..de696f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -35,6 +35,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -49,6 +50,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.Insets;
@@ -136,6 +138,8 @@
private WindowManager mStubWindowManager;
@Mock
private AccessibilityManager mStubAccessibilityManager;
+ @Mock
+ private PackageManager mMockPackageManager;
private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings();
private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
@@ -162,14 +166,15 @@
new MenuView(mSpyContext, mMenuViewModel, menuViewAppearance, mSecureSettings));
// Ensure tests don't actually update metrics.
doNothing().when(mMenuView).incrementTexMetric(any(), anyInt());
- doNothing().when(mMenuView).gotoEditScreen();
mMenuViewLayer = spy(new MenuViewLayer(mSpyContext, mStubWindowManager,
mStubAccessibilityManager, mMenuViewModel, menuViewAppearance, mMenuView,
mFloatingMenu, mSecureSettings));
- mMenuView = (MenuView) mMenuViewLayer.getChildAt(LayerIndex.MENU_VIEW);
mMenuAnimationController = mMenuView.getMenuAnimationController();
+ doNothing().when(mSpyContext).startActivity(any());
+ when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager);
+
mLastAccessibilityButtonTargets =
Settings.Secure.getStringForUser(mSpyContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, UserHandle.USER_CURRENT);
@@ -277,9 +282,31 @@
@Test
@EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
- public void onEditAction_gotoEditScreen_isCalled() {
+ public void onEditAction_startsActivity() {
+ mockActivityQuery(true);
mMenuViewLayer.dispatchAccessibilityAction(R.id.action_edit);
- verify(mMenuView).gotoEditScreen();
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mSpyContext).startActivity(intentCaptor.capture());
+ assertThat(intentCaptor.getValue().getAction()).isEqualTo(
+ mMenuViewLayer.getIntentForEditScreen().getAction());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+ public void onEditAction_noResolve_doesNotStart() {
+ mockActivityQuery(false);
+ mMenuViewLayer.dispatchAccessibilityAction(R.id.action_edit);
+ verify(mSpyContext, never()).startActivity(any());
+ }
+
+ @Test
+ public void getIntentForEditScreen_validate() {
+ Intent intent = mMenuViewLayer.getIntentForEditScreen();
+ String[] targets = intent.getBundleExtra(
+ ":settings:show_fragment_args").getStringArray("targets");
+
+ assertThat(intent.getAction()).isEqualTo(Settings.ACTION_ACCESSIBILITY_SHORTCUT_SETTINGS);
+ assertThat(targets).asList().containsExactlyElementsIn(TestUtils.TEST_BUTTON_TARGETS);
}
@Test
@@ -500,4 +527,15 @@
magnetListener.onReleasedInTarget(
new MagnetizedObject.MagneticTarget(view, 200), mock(MagnetizedObject.class));
}
+
+ private void mockActivityQuery(boolean successfulQuery) {
+ // Query just needs to return a non-empty set to be successful.
+ ArrayList<ResolveInfo> resolveInfos = new ArrayList<>();
+ if (successfulQuery) {
+ resolveInfos.add(new ResolveInfo());
+ }
+ when(mMockPackageManager.queryIntentActivities(
+ any(), any(PackageManager.ResolveInfoFlags.class))).thenReturn(resolveInfos);
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index eced465..f6288b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -22,19 +22,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.UiModeManager;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.platform.test.annotations.EnableFlags;
-import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
@@ -58,8 +52,6 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.util.ArrayList;
-
/** Tests for {@link MenuView}. */
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -79,8 +71,6 @@
private AccessibilityManager mAccessibilityManager;
private SysuiTestableContext mSpyContext;
- @Mock
- private PackageManager mMockPackageManager;
@Before
public void setUp() throws Exception {
@@ -91,7 +81,6 @@
mSpyContext = spy(mContext);
doNothing().when(mSpyContext).startActivity(any());
- when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager);
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
secureSettings);
@@ -178,32 +167,6 @@
assertThat(radiiAnimator.isStarted()).isTrue();
}
- @Test
- public void getIntentForEditScreen_validate() {
- Intent intent = mMenuView.getIntentForEditScreen();
- String[] targets = intent.getBundleExtra(
- ":settings:show_fragment_args").getStringArray("targets");
-
- assertThat(intent.getAction()).isEqualTo(Settings.ACTION_ACCESSIBILITY_SHORTCUT_SETTINGS);
- assertThat(targets).asList().containsExactlyElementsIn(TestUtils.TEST_BUTTON_TARGETS);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
- public void gotoEditScreen_sendsIntent() {
- mockActivityQuery(true);
- mMenuView.gotoEditScreen();
- verify(mSpyContext).startActivity(any());
- }
-
- @Test
- @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
- public void gotoEditScreen_noResolve_doesNotStart() {
- mockActivityQuery(false);
- mMenuView.gotoEditScreen();
- verify(mSpyContext, never()).startActivity(any());
- }
-
private InstantInsetLayerDrawable getMenuViewInsetLayer() {
return (InstantInsetLayerDrawable) mMenuView.getBackground();
}
@@ -226,14 +189,4 @@
mUiModeManager.setNightMode(mNightMode);
Prefs.putString(mContext, Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION, mLastPosition);
}
-
- private void mockActivityQuery(boolean successfulQuery) {
- // Query just needs to return a non-empty set to be successful.
- ArrayList<ResolveInfo> resolveInfos = new ArrayList<>();
- if (successfulQuery) {
- resolveInfos.add(new ResolveInfo());
- }
- when(mMockPackageManager.queryIntentActivities(
- any(), any(PackageManager.ResolveInfoFlags.class))).thenReturn(resolveInfos);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index ad80a06..8700001 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -27,7 +27,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -37,6 +39,7 @@
import java.math.BigDecimal
import java.math.RoundingMode
import java.util.UUID
+import kotlinx.coroutines.flow.dropWhile
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -224,6 +227,101 @@
}
@Test
+ fun startingSecondManualTransitionWillCancelPreviousManualTransition() =
+ TestScope().runTest {
+ // Drop initial steps from OFF which are sent in the constructor
+ val steps = mutableListOf<TransitionStep>()
+ val job =
+ underTest.transitions
+ .dropWhile { step -> step.from == OFF }
+ .onEach { steps.add(it) }
+ .launchIn(this)
+
+ val firstUuid =
+ underTest.startTransition(
+ TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator = null)
+ )
+ runCurrent()
+
+ checkNotNull(firstUuid)
+ underTest.updateTransition(firstUuid, 0.5f, TransitionState.RUNNING)
+ runCurrent()
+
+ val secondUuid =
+ underTest.startTransition(
+ TransitionInfo(OWNER_NAME, AOD, DREAMING, animator = null)
+ )
+ runCurrent()
+
+ checkNotNull(secondUuid)
+ underTest.updateTransition(secondUuid, 0.7f, TransitionState.RUNNING)
+ // Trying to transition the old uuid should be ignored.
+ underTest.updateTransition(firstUuid, 0.6f, TransitionState.RUNNING)
+ runCurrent()
+
+ assertThat(steps)
+ .containsExactly(
+ TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME),
+ TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.RUNNING, OWNER_NAME),
+ TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.CANCELED, OWNER_NAME),
+ TransitionStep(AOD, DREAMING, 0.5f, TransitionState.STARTED, OWNER_NAME),
+ TransitionStep(AOD, DREAMING, 0.7f, TransitionState.RUNNING, OWNER_NAME),
+ )
+ .inOrder()
+
+ job.cancel()
+ }
+
+ @Test
+ fun startingSecondTransitionWillCancelPreviousManualTransition() =
+ TestScope().runTest {
+ // Drop initial steps from OFF which are sent in the constructor
+ val steps = mutableListOf<TransitionStep>()
+ val job =
+ underTest.transitions
+ .dropWhile { step -> step.from == OFF }
+ .onEach { steps.add(it) }
+ .launchIn(this)
+
+ val uuid =
+ underTest.startTransition(
+ TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator = null)
+ )
+ runCurrent()
+
+ checkNotNull(uuid)
+ underTest.updateTransition(uuid, 0.5f, TransitionState.RUNNING)
+ runCurrent()
+
+ // Start new transition to dreaming, should cancel previous one.
+ runner.startTransition(
+ this,
+ TransitionInfo(
+ OWNER_NAME,
+ AOD,
+ DREAMING,
+ getAnimator(),
+ TransitionModeOnCanceled.RESET,
+ ),
+ )
+ runCurrent()
+
+ // Trying to transition the old uuid should be ignored.
+ underTest.updateTransition(uuid, 0.6f, TransitionState.RUNNING)
+ runCurrent()
+
+ assertThat(steps.take(3))
+ .containsExactly(
+ TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME),
+ TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.RUNNING, OWNER_NAME),
+ TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.CANCELED, OWNER_NAME),
+ )
+ .inOrder()
+
+ job.cancel()
+ }
+
+ @Test
fun attemptTomanuallyUpdateTransitionWithInvalidUUIDthrowsException() {
underTest.updateTransition(UUID.randomUUID(), 0f, TransitionState.RUNNING)
assertThat(wtfHandler.failed).isTrue()
@@ -336,6 +434,7 @@
private class WtfHandler : TerribleFailureHandler {
var failed = false
+
override fun onTerribleFailure(tag: String, what: TerribleFailure, system: Boolean) {
failed = true
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index 87391cc..d410dac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -36,6 +37,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.Mockito.verify
@ExperimentalCoroutinesApi
@RunWith(JUnit4::class)
@@ -48,6 +50,20 @@
private val underTest = kosmos.alternateBouncerViewModel
@Test
+ fun showPrimaryBouncer() =
+ testScope.runTest {
+ underTest.showPrimaryBouncer()
+ verify(statusBarKeyguardViewManager).showPrimaryBouncer(any())
+ }
+
+ @Test
+ fun hideAlternateBouncer() =
+ testScope.runTest {
+ underTest.hideAlternateBouncer()
+ verify(statusBarKeyguardViewManager).hideAlternateBouncer(any())
+ }
+
+ @Test
fun transitionToAlternateBouncer_scrimAlphaUpdate() =
testScope.runTest {
val scrimAlphas by collectValues(underTest.scrimAlpha)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt
index 8986d99..cd1452a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt
@@ -68,7 +68,7 @@
fun testGetValue_valueUnset() {
testScope.runTest {
userRepository.setSelectedUserInfo(SYSTEM_USER)
- val actualValue by collectLastValue(bluetoothAutoOnRepository.getValue)
+ val actualValue by collectLastValue(bluetoothAutoOnRepository.isAutoOn)
runCurrent()
@@ -81,7 +81,7 @@
fun testGetValue_valueFalse() {
testScope.runTest {
userRepository.setSelectedUserInfo(SYSTEM_USER)
- val actualValue by collectLastValue(bluetoothAutoOnRepository.getValue)
+ val actualValue by collectLastValue(bluetoothAutoOnRepository.isAutoOn)
secureSettings.putIntForUser(SETTING_NAME, DISABLED, UserHandle.USER_SYSTEM)
runCurrent()
@@ -94,7 +94,7 @@
fun testGetValue_valueTrue() {
testScope.runTest {
userRepository.setSelectedUserInfo(SYSTEM_USER)
- val actualValue by collectLastValue(bluetoothAutoOnRepository.getValue)
+ val actualValue by collectLastValue(bluetoothAutoOnRepository.isAutoOn)
secureSettings.putIntForUser(SETTING_NAME, ENABLED, UserHandle.USER_SYSTEM)
runCurrent()
@@ -104,15 +104,16 @@
}
@Test
- fun testGetValue_valueTrue_secondaryUser_returnUnset() {
+ fun testGetValue_valueTrue_secondaryUser_returnTrue() {
testScope.runTest {
userRepository.setSelectedUserInfo(SECONDARY_USER)
- val actualValue by collectLastValue(bluetoothAutoOnRepository.getValue)
+ val actualValue by collectLastValue(bluetoothAutoOnRepository.isAutoOn)
+ secureSettings.putIntForUser(SETTING_NAME, DISABLED, SYSTEM_USER_ID)
secureSettings.putIntForUser(SETTING_NAME, ENABLED, SECONDARY_USER_ID)
runCurrent()
- assertThat(actualValue).isEqualTo(UNSET)
+ assertThat(actualValue).isEqualTo(ENABLED)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index f89b9cd..66a306e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -53,8 +53,6 @@
import com.android.internal.logging.UiEventLogger.UiEventEnum
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogcatEchoTracker
import com.android.systemui.log.core.LogLevel
@@ -150,7 +148,10 @@
@Before
fun setUp() {
- val user = UserInfo(ActivityManager.getCurrentUser(), "Current user", /* flags = */ 0)
+ val userId = ActivityManager.getCurrentUser()
+ val user = UserInfo(userId, "Current user", /* flags = */ 0)
+
+ deviceProvisionedController.currentUser = userId
userTracker.set(listOf(user), /* currentUserIndex = */ 0)
provider.start()
@@ -823,6 +824,13 @@
}
@Test
+ fun testShouldFsi_userSetupIncomplete() {
+ ensureUserSetupIncompleteFsiState()
+ assertShouldFsi(buildFsiEntry())
+ assertNoEventsLogged()
+ }
+
+ @Test
fun testShouldNotFsi_noHunOrKeyguard() {
ensureNoHunOrKeyguardFsiState()
assertShouldNotFsi(buildFsiEntry())
@@ -888,7 +896,8 @@
var statusBarState: Int? = null,
var keyguardIsShowing: Boolean = false,
var keyguardIsOccluded: Boolean = false,
- var deviceProvisioned: Boolean = true
+ var deviceProvisioned: Boolean = true,
+ var currentUserSetup: Boolean = true
)
protected fun setState(state: State): Unit =
@@ -925,6 +934,7 @@
keyguardStateController.isShowing = keyguardIsShowing
deviceProvisionedController.deviceProvisioned = deviceProvisioned
+ deviceProvisionedController.isCurrentUserSetup = currentUserSetup
}
protected fun ensureState(block: State.() -> Unit) =
@@ -999,6 +1009,18 @@
hunSettingEnabled = false
keyguardIsShowing = false
deviceProvisioned = false
+ currentUserSetup = true
+ run(block)
+ }
+
+ protected fun ensureUserSetupIncompleteFsiState(block: State.() -> Unit = {}) = ensureState {
+ isInteractive = true
+ isDreaming = false
+ statusBarState = SHADE
+ hunSettingEnabled = false
+ keyguardIsShowing = false
+ deviceProvisioned = true
+ currentUserSetup = false
run(block)
}
@@ -1009,6 +1031,7 @@
hunSettingEnabled = false
keyguardIsShowing = false
deviceProvisioned = true
+ currentUserSetup = true
run(block)
}
@@ -1216,7 +1239,7 @@
neb.setImportance(importance)
val channel =
- NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance)
+ NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance)
channel.isImportantConversation = isImportantConversation
neb.setChannel(channel)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 562aa6a..b0b9bec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -82,6 +82,9 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
+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.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.TaskbarDelegate;
import com.android.systemui.plugins.ActivityStarter;
@@ -103,6 +106,8 @@
import com.google.common.truth.Truth;
+import kotlin.Unit;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -1045,4 +1050,35 @@
verify(mCentralSurfaces, never()).hideKeyguard();
verify(mPrimaryBouncerInteractor, never()).show(true);
}
+
+ @Test
+ public void altBouncerNotVisible_keyguardAuthenticatedBiometricsHandled() {
+ clearInvocations(mAlternateBouncerInteractor);
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
+ mStatusBarKeyguardViewManager.consumeKeyguardAuthenticatedBiometricsHandled(Unit.INSTANCE);
+ verify(mAlternateBouncerInteractor, never()).hide();
+ }
+
+ @Test
+ public void altBouncerVisible_keyguardAuthenticatedBiometricsHandled() {
+ clearInvocations(mAlternateBouncerInteractor);
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+ mStatusBarKeyguardViewManager.consumeKeyguardAuthenticatedBiometricsHandled(Unit.INSTANCE);
+ verify(mAlternateBouncerInteractor).hide();
+ }
+
+ @Test
+ public void fromAlternateBouncerTransitionStep() {
+ clearInvocations(mAlternateBouncerInteractor);
+ mStatusBarKeyguardViewManager.consumeFromAlternateBouncerTransitionSteps(
+ new TransitionStep(
+ /* from */ KeyguardState.ALTERNATE_BOUNCER,
+ /* to */ KeyguardState.GONE,
+ /* value */ 1f,
+ TransitionState.FINISHED,
+ "StatusBarKeyguardViewManagerTest"
+ )
+ );
+ verify(mAlternateBouncerInteractor).hide();
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
index aa2c2a2..ebcabf8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
@@ -39,8 +39,16 @@
callbacks.toSet().forEach { it.onUserSwitched() }
}
- fun setUserSetup(userId: Int) {
- usersSetup.add(userId)
+ fun setUserSetup(userId: Int, isSetup: Boolean = true) {
+ if (isSetup) {
+ usersSetup.add(userId)
+ } else {
+ usersSetup.remove(userId)
+ }
callbacks.toSet().forEach { it.onUserSetupChanged() }
}
+
+ fun setCurrentUserSetup(isSetup: Boolean) {
+ setUserSetup(currentUser, isSetup)
+ }
}
diff --git a/ravenwood/api-maintainers.md b/ravenwood/api-maintainers.md
index 4b2f968..c059cab 100644
--- a/ravenwood/api-maintainers.md
+++ b/ravenwood/api-maintainers.md
@@ -4,7 +4,7 @@
To opt-in to supporting an API under Ravenwood, you can use the inline annotations documented below to customize your API behavior when running under Ravenwood. Because these annotations are inline in the relevant platform source code, they serve as valuable reminders to future API maintainers of Ravenwood support expectations.
-> **Note:** to ensure that API teams are well-supported during early Ravenwood onboarding, the Ravenwood team is manually maintaining an allow-list of classes that are able to use Ravenwood annotations. Please reach out to ravenwood@ so we can offer design advice and allow-list your APIs.
+> **Note:** Active development of Ravenwood has been paused as of March 2024. Currently supported APIs will continue working, but the addition of new APIs is not currently being supported. There is an allowlist that restricts where Ravenwood-specific annotations can be used, and that allowlist is not being expanded while development is paused.
These Ravenwood-specific annotations have no bearing on the status of an API being public, `@SystemApi`, `@TestApi`, `@hide`, etc. Ravenwood annotations are an orthogonal concept that are only consumed by the internal `hoststubgen` tool during a post-processing step that generates the Ravenwood runtime environment. Teams that own APIs can continue to refactor opted-in `@hide` implementation details, as long as the test-visible behavior continues passing.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fecf44f..dd2474b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1535,9 +1535,7 @@
// and user switch would not happen at that time.
resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_USER);
- final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
- newUserId, AdditionalSubtypeMapRepository.get(newUserId), DirectBootAwareness.AUTO);
- InputMethodSettingsRepository.put(newUserId, newSettings);
+ final InputMethodSettings newSettings = InputMethodSettingsRepository.get(newUserId);
mSettings = newSettings;
postInputMethodSettingUpdatedLocked(initialUserSwitch /* resetDefaultEnabledIme */);
if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e38b5fd..38ab765 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3397,7 +3397,6 @@
}
maybeMigratePoliciesPostUpgradeToDevicePolicyEngineLocked();
migratePoliciesToPolicyEngineLocked();
-
}
maybeStartSecurityLogMonitorOnActivityManagerReady();
break;
@@ -13180,27 +13179,47 @@
CallerIdentity caller, EnforcingAdmin admin, String key, boolean enabled,
boolean parent) {
synchronized (getLockObject()) {
+
+ int ownerType;
if (isDeviceOwner(caller)) {
- if (UserRestrictionsUtils.isGlobal(OWNER_TYPE_DEVICE_OWNER, key)) {
- setGlobalUserRestrictionInternal(admin, key, enabled);
- } else {
- setLocalUserRestrictionInternal(admin, key, enabled, caller.getUserId());
- }
+ ownerType = OWNER_TYPE_DEVICE_OWNER;
+ } else if (isProfileOwnerOfOrganizationOwnedDevice(caller)) {
+ ownerType = OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
} else if (isProfileOwner(caller)) {
- if (UserRestrictionsUtils.isGlobal(OWNER_TYPE_PROFILE_OWNER, key)
- || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)
- && UserRestrictionsUtils.isGlobal(
- OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE, key))) {
- setGlobalUserRestrictionInternal(admin, key, enabled);
- } else {
- int affectedUserId = parent
- ? getProfileParentId(caller.getUserId()) : caller.getUserId();
- setLocalUserRestrictionInternal(admin, key, enabled, affectedUserId);
- }
+ ownerType = OWNER_TYPE_PROFILE_OWNER;
} else {
throw new IllegalStateException("Non-DO/Non-PO cannot set restriction " + key
+ " while targetSdkVersion is less than UPSIDE_DOWN_CAKE");
}
+ setBackwardCompatibleUserRestrictionLocked(ownerType, admin, caller.getUserId(), key,
+ enabled, parent);
+ }
+ }
+
+ private void setBackwardCompatibleUserRestrictionLocked(
+ int ownerType, EnforcingAdmin admin, int userId, String key, boolean enabled,
+ boolean parent) {
+ if (ownerType == OWNER_TYPE_DEVICE_OWNER) {
+ if (UserRestrictionsUtils.isGlobal(OWNER_TYPE_DEVICE_OWNER, key)) {
+ setGlobalUserRestrictionInternal(admin, key, enabled);
+ } else {
+ setLocalUserRestrictionInternal(admin, key, enabled, userId);
+ }
+ } else if (ownerType == OWNER_TYPE_PROFILE_OWNER
+ || ownerType == OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE) {
+ if (UserRestrictionsUtils.isGlobal(OWNER_TYPE_PROFILE_OWNER, key)
+ || (parent && ownerType == OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE
+ && UserRestrictionsUtils.isGlobal(
+ OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE, key))) {
+ setGlobalUserRestrictionInternal(admin, key, enabled);
+ } else {
+ int affectedUserId = parent
+ ? getProfileParentId(userId) : userId;
+ setLocalUserRestrictionInternal(admin, key, enabled, affectedUserId);
+ }
+ } else {
+ throw new IllegalStateException("Non-DO/Non-PO cannot set restriction " + key
+ + " while targetSdkVersion is less than UPSIDE_DOWN_CAKE");
}
}
@@ -23748,13 +23767,15 @@
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
return mInjector.binderWithCleanCallingIdentity(() -> {
- boolean canForceMigration = forceMigration && !hasNonTestOnlyActiveAdmins();
- if (!canForceMigration && !shouldMigrateV1ToDevicePolicyEngine()) {
- return false;
+ synchronized (getLockObject()) {
+ boolean canForceMigration = forceMigration && !hasNonTestOnlyActiveAdmins();
+ if (!canForceMigration && !shouldMigrateV1ToDevicePolicyEngine()) {
+ return false;
+ }
+ boolean migrated = migrateV1PoliciesToDevicePolicyEngine();
+ migrated &= migratePoliciesPostUpgradeToDevicePolicyEngineLocked();
+ return migrated;
}
- boolean migrated = migrateV1PoliciesToDevicePolicyEngine();
- migrated &= migratePoliciesPostUpgradeToDevicePolicyEngineLocked();
- return migrated;
});
}
@@ -23798,6 +23819,7 @@
try {
migrateScreenCapturePolicyLocked();
migrateLockTaskPolicyLocked();
+ migrateUserRestrictionsLocked();
return true;
} catch (Exception e) {
Slogf.e(LOG_TAG, e, "Error occurred during post upgrade migration to the device "
@@ -24067,6 +24089,42 @@
});
}
+ private void migrateUserRestrictionsLocked() {
+ Binder.withCleanCallingIdentity(() -> {
+ List<UserInfo> users = mUserManager.getUsers();
+ for (UserInfo userInfo : users) {
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(userInfo.id);
+ if (admin == null) continue;
+ ComponentName adminComponent = admin.info.getComponent();
+ int userId = userInfo.id;
+ EnforcingAdmin enforcingAdmin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ adminComponent,
+ userId,
+ admin);
+ int ownerType;
+ if (isDeviceOwner(admin)) {
+ ownerType = OWNER_TYPE_DEVICE_OWNER;
+ } else if (isProfileOwnerOfOrganizationOwnedDevice(adminComponent, userId)) {
+ ownerType = OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
+ } else if (isProfileOwner(adminComponent, userId)) {
+ ownerType = OWNER_TYPE_PROFILE_OWNER;
+ } else {
+ throw new IllegalStateException("Invalid DO/PO state");
+ }
+
+ for (final String restriction : admin.ensureUserRestrictions().keySet()) {
+ setBackwardCompatibleUserRestrictionLocked(ownerType, enforcingAdmin, userId,
+ restriction, /* enabled */ true, /* parent */ false);
+ }
+ for (final String restriction : admin.getParentActiveAdmin()
+ .ensureUserRestrictions().keySet()) {
+ setBackwardCompatibleUserRestrictionLocked(ownerType, enforcingAdmin, userId,
+ restriction, /* enabled */ true, /* parent */ true);
+ }
+ }
+ });
+ }
+
private List<PackageInfo> getInstalledPackagesOnUser(int userId) {
return mInjector.binderWithCleanCallingIdentity(() ->
mContext.getPackageManager().getInstalledPackagesAsUser(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 18f0311..48fc407 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -106,6 +106,7 @@
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
+import android.test.mock.MockContentResolver;
import android.util.SparseArray;
import android.view.ContentRecordingSession;
import android.view.Display;
@@ -123,6 +124,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -204,6 +207,8 @@
@Rule
public SetFlagsRule mSetFlagsRule =
new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+ @Rule
+ public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
private Context mContext;
@@ -376,6 +381,8 @@
when(display.getBrightnessInfo()).thenReturn(mock(BrightnessInfo.class));
mContext = spy(new ContextWrapper(
ApplicationProvider.getApplicationContext().createDisplayContext(display)));
+ final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
+ when(mContext.getContentResolver()).thenReturn(resolver);
mResources = Mockito.spy(mContext.getResources());
mPowerHandler = new Handler(Looper.getMainLooper());
manageDisplaysPermission(/* granted= */ false);
diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
index 443de8e..7cdab94 100644
--- a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
index c51da05..377288d 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
index ab23401..68b1473 100644
--- a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
Binary files differ