Merge "Ktfmt on MultiPointerDraggable MultiPointerDraggableTest" into main
diff --git a/Android.bp b/Android.bp
index f8907f3..258440f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -98,6 +98,7 @@
":android.frameworks.location.altitude-V2-java-source",
":android.hardware.biometrics.common-V4-java-source",
":android.hardware.biometrics.fingerprint-V5-java-source",
+ ":android.hardware.biometrics.fingerprint.virtualhal-java-source",
":android.hardware.biometrics.face-V4-java-source",
":android.hardware.gnss-V2-java-source",
":android.hardware.graphics.common-V3-java-source",
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ede4a89..fb425a9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -18156,9 +18156,17 @@
field public static final int DISPLAY_IME_POLICY_LOCAL = 0; // 0x0
}
+ @FlaggedApi("android.companion.virtualdevice.flags.status_bar_and_insets") public static class WindowManager.InsetsParams {
+ ctor public WindowManager.InsetsParams(int);
+ method @Nullable public android.graphics.Insets getInsetsSize();
+ method public int getType();
+ method @NonNull public android.view.WindowManager.InsetsParams setInsetsSize(@Nullable android.graphics.Insets);
+ }
+
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
method public final long getUserActivityTimeout();
method public boolean isSystemApplicationOverlay();
+ method @FlaggedApi("android.companion.virtualdevice.flags.status_bar_and_insets") public void setInsetsParams(@NonNull java.util.List<android.view.WindowManager.InsetsParams>);
method @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public void setSystemApplicationOverlay(boolean);
method public final void setUserActivityTimeout(long);
field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
diff --git a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
index 926cc9a..085e0a4 100644
--- a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
+++ b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
@@ -39,6 +39,8 @@
public static final String STATIC_PROPERTY_ENABLED_BY_DEFAULT = "enabledByDefault";
public static final String APP_FUNCTION_STATIC_NAMESPACE = "app_functions";
+ public static final String PROPERTY_FUNCTION_ID = "functionId";
+ public static final String PROPERTY_PACKAGE_NAME = "packageName";
// These are constants that has to be kept the same with {@code
// com.android.server.appsearch.appsindexer.appsearchtypes.AppSearchHelper}.
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 6f1327c..e9fa3e1 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -121,7 +121,6 @@
description: "Make relevant PowerManager APIs display aware by default"
bug: "365042486"
is_fixed_read_only: true
- is_exported: true
}
flag {
@@ -131,3 +130,10 @@
bug: "350007866"
is_exported: true
}
+
+flag {
+ namespace: "virtual_devices"
+ name: "camera_timestamp_from_surface"
+ description: "Pass the surface timestamp to the capture result"
+ bug: "351341245"
+}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java
index 43c0da9..48c5887 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java
@@ -23,12 +23,14 @@
import android.content.Context;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.biometrics.fingerprint.virtualhal.IVirtualHal;
import android.os.Binder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.HashMap;
@@ -162,6 +164,43 @@
dest.writeMap(mSensorPropsMap);
}
+
+ /**
+ * Remap fqName of VHAL because the `virtual` instance is registered
+ * with IVirtulalHal now (IFingerprint previously)
+ * @param fqName fqName to be translated
+ * @return real fqName
+ */
+ public static String remapFqName(String fqName) {
+ if (!fqName.contains(IFingerprint.DESCRIPTOR + "/virtual")) {
+ return fqName; //no remap needed for real hardware HAL
+ } else {
+ //new Vhal instance name
+ return fqName.replace("IFingerprint", "virtualhal.IVirtualHal");
+ }
+ }
+
+ /**
+ * @param fqName aidl interface instance name
+ * @return aidl interface
+ */
+ public static IFingerprint getIFingerprint(String fqName) {
+ if (fqName.contains("virtual")) {
+ String fqNameMapped = remapFqName(fqName);
+ Slog.i(TAG, "getIFingerprint fqName is mapped: " + fqName + "->" + fqNameMapped);
+ try {
+ IVirtualHal vhal = IVirtualHal.Stub.asInterface(
+ Binder.allowBlocking(ServiceManager.waitForService(fqNameMapped)));
+ return vhal.getFingerprintHal();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in vhal.getFingerprintHal() call" + fqNameMapped);
+ }
+ }
+
+ return IFingerprint.Stub.asInterface(
+ Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+ }
+
/**
* Returns fingerprint sensor props for the HAL {@param instance}.
*/
@@ -176,8 +215,7 @@
try {
final String fqName = IFingerprint.DESCRIPTOR + "/" + instance;
- final IFingerprint fp = IFingerprint.Stub.asInterface(Binder.allowBlocking(
- ServiceManager.waitForDeclaredService(fqName)));
+ final IFingerprint fp = getIFingerprint(fqName);
if (fp != null) {
props = fp.getSensorProps();
} else {
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 0ed1ab6..4c9a02c 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -109,6 +109,7 @@
private static final String TAG = "SystemClock";
private static volatile IAlarmManager sIAlarmManager;
+ private static volatile ITimeDetectorService sITimeDetectorService;
/**
* Since {@code nanoTime()} is arbitrary, anchor our Ravenwood clocks against it.
@@ -188,6 +189,14 @@
return sIAlarmManager;
}
+ private static ITimeDetectorService getITimeDetectorService() {
+ if (sITimeDetectorService == null) {
+ sITimeDetectorService = ITimeDetectorService.Stub
+ .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
+ }
+ return sITimeDetectorService;
+ }
+
/**
* Returns milliseconds since boot, not counting time spent in deep sleep.
*
@@ -314,15 +323,6 @@
}
/**
- * @see #currentNetworkTimeMillis(ITimeDetectorService)
- * @hide
- */
- public static long currentNetworkTimeMillis() {
- return currentNetworkTimeMillis(ITimeDetectorService.Stub
- .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE)));
- }
-
- /**
* Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
* using a remote network source outside the device.
* <p>
@@ -346,29 +346,29 @@
* @throws DateTimeException when no network time can be provided.
* @hide
*/
- public static long currentNetworkTimeMillis(
- ITimeDetectorService timeDetectorService) {
- if (timeDetectorService != null) {
- UnixEpochTime time;
- try {
- time = timeDetectorService.latestNetworkTime();
- } catch (ParcelableException e) {
- e.maybeRethrow(DateTimeException.class);
- throw new RuntimeException(e);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- if (time == null) {
- // This is not expected.
- throw new DateTimeException("Network based time is not available.");
- }
- long currentMillis = elapsedRealtime();
- long deltaMs = currentMillis - time.getElapsedRealtimeMillis();
- return time.getUnixEpochTimeMillis() + deltaMs;
- } else {
+ public static long currentNetworkTimeMillis() {
+ ITimeDetectorService timeDetectorService = getITimeDetectorService();
+ if (timeDetectorService == null) {
throw new RuntimeException(new DeadSystemException());
}
+
+ UnixEpochTime time;
+ try {
+ time = timeDetectorService.latestNetworkTime();
+ } catch (ParcelableException e) {
+ e.maybeRethrow(DateTimeException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (time == null) {
+ // This is not expected.
+ throw new DateTimeException("Network based time is not available.");
+ }
+
+ long currentMillis = elapsedRealtime();
+ long deltaMs = currentMillis - time.getElapsedRealtimeMillis();
+ return time.getUnixEpochTimeMillis() + deltaMs;
}
/**
@@ -396,14 +396,9 @@
*/
public static @NonNull Clock currentNetworkTimeClock() {
return new SimpleClock(ZoneOffset.UTC) {
- private ITimeDetectorService mSvc;
@Override
public long millis() {
- if (mSvc == null) {
- mSvc = ITimeDetectorService.Stub
- .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
- }
- return SystemClock.currentNetworkTimeMillis(mSvc);
+ return SystemClock.currentNetworkTimeMillis();
}
};
}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
index 901bf56..c2ad508 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/text/ClientFlags.java
@@ -28,13 +28,6 @@
*/
public class ClientFlags {
/**
- * @see Flags#noBreakNoHyphenationSpan()
- */
- public static boolean noBreakNoHyphenationSpan() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN);
- }
-
- /**
* @see Flags#fixMisalignedContextMenu()
*/
public static boolean fixMisalignedContextMenu() {
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index a78aa7e..076721f 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -55,7 +55,6 @@
* List of text flags to be transferred to the application process.
*/
public static final String[] TEXT_ACONFIGS_FLAGS = {
- Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN,
Flags.FLAG_FIX_MISALIGNED_CONTEXT_MENU,
};
@@ -65,7 +64,6 @@
* The order must be the same to the TEXT_ACONFIG_FLAGS.
*/
public static final boolean[] TEXT_ACONFIG_DEFAULT_VALUE = {
- Flags.noBreakNoHyphenationSpan(),
Flags.fixMisalignedContextMenu(),
};
diff --git a/core/java/android/text/flags/24Q3.aconfig b/core/java/android/text/flags/24Q3.aconfig
index 6524ed3..7035fc8 100644
--- a/core/java/android/text/flags/24Q3.aconfig
+++ b/core/java/android/text/flags/24Q3.aconfig
@@ -34,3 +34,21 @@
description: "Feature flag that preserve the line height of the TextView and EditText even if the the locale is different from Latin"
bug: "303326708"
}
+
+flag {
+ name: "new_fonts_fallback_xml"
+ is_exported: true
+ namespace: "text"
+ description: "Feature flag for deprecating fonts.xml. By setting true for this feature flag, the new font configuration XML, /system/etc/font_fallback.xml is used. The new XML has a new syntax and flexibility of variable font declarations, but it is not compatible with the apps that reads fonts.xml. So, fonts.xml is maintained as a subset of the font_fallback.xml"
+ # Make read only, as it could be used before the Settings provider is initialized.
+ is_fixed_read_only: true
+ bug: "281769620"
+}
+
+flag {
+ name: "no_break_no_hyphenation_span"
+ is_exported: true
+ namespace: "text"
+ description: "A feature flag that adding new spans that prevents line breaking and hyphenation."
+ bug: "283193586"
+}
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index f8a98da..3c61f4f 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -2,24 +2,6 @@
container: "system"
flag {
- name: "new_fonts_fallback_xml"
- is_exported: true
- namespace: "text"
- description: "Feature flag for deprecating fonts.xml. By setting true for this feature flag, the new font configuration XML, /system/etc/font_fallback.xml is used. The new XML has a new syntax and flexibility of variable font declarations, but it is not compatible with the apps that reads fonts.xml. So, fonts.xml is maintained as a subset of the font_fallback.xml"
- # Make read only, as it could be used before the Settings provider is initialized.
- is_fixed_read_only: true
- bug: "281769620"
-}
-
-flag {
- name: "no_break_no_hyphenation_span"
- is_exported: true
- namespace: "text"
- description: "A feature flag that adding new spans that prevents line breaking and hyphenation."
- bug: "283193586"
-}
-
-flag {
name: "use_optimized_boottime_font_loading"
namespace: "text"
description: "Feature flag ensuring that font is loaded once and asynchronously."
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 67a207e..4fe11e6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -107,6 +107,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -2371,7 +2372,7 @@
public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
/**
- * Window type: the drag-and-drop pseudowindow. There is only one
+ * Window type: the drag-and-drop pseudowindow. There is only one
* drag layer (at most), and it is placed on top of all other windows.
* In multiuser systems shows only on the owning user's window.
* @hide
@@ -2381,7 +2382,7 @@
/**
* Window type: panel that slides out from over the status bar
* In multiuser systems shows on all users' windows. These windows
- * are displayed on top of the stauts bar and any {@link #TYPE_STATUS_BAR_PANEL}
+ * are displayed on top of the status bar and any {@link #TYPE_STATUS_BAR_PANEL}
* windows.
* @hide
*/
@@ -4649,6 +4650,30 @@
public InsetsFrameProvider[] providedInsets;
/**
+ * Sets the insets to be provided by the window.
+ *
+ * @param insetsParams The parameters for the insets to be provided by the window.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_STATUS_BAR_AND_INSETS)
+ @SystemApi
+ public void setInsetsParams(@NonNull List<InsetsParams> insetsParams) {
+ if (insetsParams.isEmpty()) {
+ providedInsets = null;
+ } else {
+ providedInsets = new InsetsFrameProvider[insetsParams.size()];
+ for (int i = 0; i < insetsParams.size(); ++i) {
+ final InsetsParams params = insetsParams.get(i);
+ providedInsets[i] =
+ new InsetsFrameProvider(/* owner= */ this, /* index= */ i,
+ params.getType())
+ .setInsetsSize(params.getInsetsSize());
+ }
+ }
+ }
+
+ /**
* Specifies which {@link InsetsType}s should be forcibly shown. The types shown by this
* method won't affect the app's layout. This field only takes effects if the caller has
* {@link android.Manifest.permission#STATUS_BAR_SERVICE} or the caller has the same uid as
@@ -6117,6 +6142,55 @@
}
/**
+ * Specifies the parameters of the insets provided by a window.
+ *
+ * @see WindowManager.LayoutParams#setInsetsParams(List)
+ * @see android.graphics.Insets
+ *
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_STATUS_BAR_AND_INSETS)
+ @SystemApi
+ public static class InsetsParams {
+
+ private final @InsetsType int mType;
+ private @Nullable Insets mInsets;
+
+ /**
+ * Creates an instance of InsetsParams.
+ *
+ * @param type the type of insets to provide, e.g. {@link WindowInsets.Type#statusBars()}.
+ * @see WindowInsets.Type
+ */
+ public InsetsParams(@InsetsType int type) {
+ mType = type;
+ }
+
+ /**
+ * Sets the size of the provided insets. If {@code null}, then the provided insets will
+ * have the same size as the window frame.
+ */
+ public @NonNull InsetsParams setInsetsSize(@Nullable Insets insets) {
+ mInsets = insets;
+ return this;
+ }
+
+ /**
+ * Returns the type of provided insets.
+ */
+ public @InsetsType int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the size of the provided insets.
+ */
+ public @Nullable Insets getInsetsSize() {
+ return mInsets;
+ }
+ }
+
+ /**
* Holds the WM lock for the specified amount of milliseconds.
* Intended for use by the tests that need to imitate lock contention.
* The token should be obtained by
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 1da2af4..b18dbbc 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -15,12 +15,14 @@
*/
package android.webkit;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
+import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.UserHandle;
import android.os.UserManager;
import java.util.ArrayList;
@@ -31,30 +33,31 @@
* @hide
*/
public class UserPackage {
- private final UserInfo mUserInfo;
+ private final UserHandle mUser;
private final PackageInfo mPackageInfo;
public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.TIRAMISU;
- public UserPackage(UserInfo user, PackageInfo packageInfo) {
- this.mUserInfo = user;
- this.mPackageInfo = packageInfo;
+ public UserPackage(@NonNull UserHandle user, @Nullable PackageInfo packageInfo) {
+ mUser = user;
+ mPackageInfo = packageInfo;
}
/**
* Returns a list of (User,PackageInfo) pairs corresponding to the PackageInfos for all
* device users for the package named {@param packageName}.
*/
- public static List<UserPackage> getPackageInfosAllUsers(Context context,
- String packageName, int packageFlags) {
- List<UserInfo> users = getAllUsers(context);
+ public static @NonNull List<UserPackage> getPackageInfosAllUsers(@NonNull Context context,
+ @NonNull String packageName, int packageFlags) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ List<UserHandle> users = userManager.getUserHandles(false);
List<UserPackage> userPackages = new ArrayList<UserPackage>(users.size());
- for (UserInfo user : users) {
+ for (UserHandle user : users) {
+ PackageManager pm = context.createContextAsUser(user, 0).getPackageManager();
PackageInfo packageInfo = null;
try {
- packageInfo = context.getPackageManager().getPackageInfoAsUser(
- packageName, packageFlags, user.id);
- } catch (NameNotFoundException e) {
+ packageInfo = pm.getPackageInfo(packageName, packageFlags);
+ } catch (PackageManager.NameNotFoundException e) {
}
userPackages.add(new UserPackage(user, packageInfo));
}
@@ -88,18 +91,11 @@
return packageInfo.applicationInfo.targetSdkVersion >= MINIMUM_SUPPORTED_SDK;
}
- public UserInfo getUserInfo() {
- return mUserInfo;
+ public UserHandle getUser() {
+ return mUser;
}
public PackageInfo getPackageInfo() {
return mPackageInfo;
}
-
-
- private static List<UserInfo> getAllUsers(Context context) {
- UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- return userManager.getUsers();
- }
-
}
diff --git a/core/java/com/android/internal/jank/flags.aconfig b/core/java/com/android/internal/jank/flags.aconfig
index b6b8bc5..82f50ae 100644
--- a/core/java/com/android/internal/jank/flags.aconfig
+++ b/core/java/com/android/internal/jank/flags.aconfig
@@ -3,7 +3,7 @@
flag {
name: "use_sf_frame_duration"
- namespace: "android_platform_window_surfaces"
+ namespace: "window_surfaces"
description: "Whether to get the frame duration from SurfaceFlinger, or HWUI"
bug: "354763298"
is_fixed_read_only: true
diff --git a/graphics/java/android/graphics/OWNERS b/graphics/java/android/graphics/OWNERS
index 9fa8f1b..ef8d26c 100644
--- a/graphics/java/android/graphics/OWNERS
+++ b/graphics/java/android/graphics/OWNERS
@@ -11,3 +11,4 @@
per-file FontFamily.java = file:fonts/OWNERS
per-file FontListParser.java = file:fonts/OWNERS
per-file Typeface.java = file:fonts/OWNERS
+per-file Paint.java = file:fonts/OWNERS
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index f727f5b..0e25c34 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -306,13 +306,7 @@
long lastModifiedDate,
int configVersion
) {
- final String fontsXml;
- if (com.android.text.flags.Flags.newFontsFallbackXml()) {
- fontsXml = FONTS_XML;
- } else {
- fontsXml = LEGACY_FONTS_XML;
- }
- return getSystemFontConfigInternal(fontsXml, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR,
+ return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR,
updatableFontMap, lastModifiedDate, configVersion);
}
@@ -337,13 +331,7 @@
* @hide
*/
public static @NonNull FontConfig getSystemPreinstalledFontConfig() {
- final String fontsXml;
- if (com.android.text.flags.Flags.newFontsFallbackXml()) {
- fontsXml = FONTS_XML;
- } else {
- fontsXml = LEGACY_FONTS_XML;
- }
- return getSystemFontConfigInternal(fontsXml, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null,
+ return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null,
0, 0);
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
index 424d4bf..b5d63bd 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
@@ -49,6 +49,7 @@
SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
DISABLE_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false),
+ SCALED_RESIZING(Flags::enableWindowingScaledResizing, false),
ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true),
EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index 1e6fa28..2033902 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -24,6 +24,7 @@
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__CHILD_TASK_ENTER_PIP;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_REQUEST;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
@@ -45,6 +46,7 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_REQUEST;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -211,6 +213,8 @@
return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
case EXIT_REASON_DESKTOP_MODE:
return SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
+ case EXIT_REASON_FULLSCREEN_REQUEST:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_REQUEST;
case EXIT_REASON_UNKNOWN:
// Fall through
default:
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 dad0d4e..1b143eb 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
@@ -1175,6 +1175,7 @@
mSplitTransitions.startDismissTransition(wct, this, mLastActiveStage, reason);
setSplitsVisible(false);
mBreakOnNextWake = false;
+ logExit(reason);
}
void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
@@ -1265,6 +1266,7 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(stage, wct);
mSplitTransitions.startDismissTransition(wct, this, stage, exitReason);
+ logExit(exitReason);
}
/**
@@ -1361,6 +1363,7 @@
mMainStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId));
mSideStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId));
});
+ logExit(exitReason);
}
/**
@@ -1579,7 +1582,7 @@
if (stage == STAGE_TYPE_MAIN) {
mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
mSplitLayout.isLeftRightSplit());
- } else {
+ } else if (stage == STAGE_TYPE_SIDE) {
mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLeftRightSplit());
}
@@ -2275,6 +2278,7 @@
if (isOpening && inFullscreen) {
// One task is opening into fullscreen mode, remove the corresponding split record.
mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
+ logExit(EXIT_REASON_FULLSCREEN_REQUEST);
}
if (isSplitActive()) {
@@ -2402,6 +2406,7 @@
if (triggerTask != null) {
mRecentTasks.ifPresent(
recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
+ logExit(EXIT_REASON_CHILD_TASK_ENTER_PIP);
}
@StageType int topStage = STAGE_TYPE_UNDEFINED;
if (isSplitScreenVisible()) {
@@ -2743,7 +2748,10 @@
final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN :
(sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED);
pendingEnter.cancel(
- (cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct));
+ (cancelWct, cancelT) -> {
+ prepareExitSplitScreen(dismissTop, cancelWct);
+ logExit(EXIT_REASON_UNKNOWN);
+ });
Log.w(TAG, splitFailureMessage("startPendingEnterAnimation",
"launched 2 tasks in split, but didn't receive "
+ "2 tasks in transition. Possibly one of them failed to launch"));
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 5955915..e618245 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -1420,20 +1420,18 @@
Mutex AssetManager::SharedZip::gLock;
DefaultKeyedVector<String8, wp<AssetManager::SharedZip> > AssetManager::SharedZip::gOpen;
-AssetManager::SharedZip::SharedZip(const String8& path, ModDate modWhen)
- : mPath(path),
- mZipFile(NULL),
- mModWhen(modWhen),
- mResourceTableAsset(NULL),
- mResourceTable(NULL) {
- if (kIsDebug) {
- ALOGI("Creating SharedZip %p %s\n", this, mPath.c_str());
- }
- ALOGV("+++ opening zip '%s'\n", mPath.c_str());
- mZipFile = ZipFileRO::open(mPath.c_str());
- if (mZipFile == NULL) {
- ALOGD("failed to open Zip archive '%s'\n", mPath.c_str());
- }
+AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
+ : mPath(path), mZipFile(NULL), mModWhen(modWhen),
+ mResourceTableAsset(NULL), mResourceTable(NULL)
+{
+ if (kIsDebug) {
+ ALOGI("Creating SharedZip %p %s\n", this, mPath.c_str());
+ }
+ ALOGV("+++ opening zip '%s'\n", mPath.c_str());
+ mZipFile = ZipFileRO::open(mPath.c_str());
+ if (mZipFile == NULL) {
+ ALOGD("failed to open Zip archive '%s'\n", mPath.c_str());
+ }
}
AssetManager::SharedZip::SharedZip(int fd, const String8& path)
@@ -1455,7 +1453,7 @@
bool createIfNotPresent)
{
AutoMutex _l(gLock);
- auto modWhen = getFileModDate(path.c_str());
+ time_t modWhen = getFileModDate(path.c_str());
sp<SharedZip> zip = gOpen.valueFor(path).promote();
if (zip != NULL && zip->mModWhen == modWhen) {
return zip;
@@ -1522,8 +1520,8 @@
bool AssetManager::SharedZip::isUpToDate()
{
- auto modWhen = getFileModDate(mPath.c_str());
- return mModWhen == modWhen;
+ time_t modWhen = getFileModDate(mPath.c_str());
+ return mModWhen == modWhen;
}
void AssetManager::SharedZip::addOverlay(const asset_path& ap)
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index 376c881..ce0985b 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -280,21 +280,21 @@
~SharedZip();
private:
- SharedZip(const String8& path, ModDate modWhen);
- SharedZip(int fd, const String8& path);
- SharedZip(); // <-- not implemented
+ SharedZip(const String8& path, time_t modWhen);
+ SharedZip(int fd, const String8& path);
+ SharedZip(); // <-- not implemented
- String8 mPath;
- ZipFileRO* mZipFile;
- ModDate mModWhen;
+ String8 mPath;
+ ZipFileRO* mZipFile;
+ time_t mModWhen;
- Asset* mResourceTableAsset;
- ResTable* mResourceTable;
+ Asset* mResourceTableAsset;
+ ResTable* mResourceTable;
- Vector<asset_path> mOverlays;
+ Vector<asset_path> mOverlays;
- static Mutex gLock;
- static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
+ static Mutex gLock;
+ static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
};
/*
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index 98f1aa8..64b1f0c 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -25,9 +25,8 @@
#include "android-base/macros.h"
#include "android-base/unique_fd.h"
#include "androidfw/ConfigDescription.h"
-#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
-#include "androidfw/misc.h"
+#include "androidfw/ResourceTypes.h"
#include "utils/ByteOrder.h"
namespace android {
@@ -203,7 +202,7 @@
android::base::unique_fd idmap_fd_;
std::string_view overlay_apk_path_;
std::string_view target_apk_path_;
- ModDate idmap_last_mod_time_;
+ time_t idmap_last_mod_time_;
private:
DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
index 09ae40c..077609d 100644
--- a/libs/androidfw/include/androidfw/misc.h
+++ b/libs/androidfw/include/androidfw/misc.h
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#pragma once
-#include <time.h>
+#include <sys/types.h>
//
// Handy utility functions and portability code.
//
+#ifndef _LIBS_ANDROID_FW_MISC_H
+#define _LIBS_ANDROID_FW_MISC_H
namespace android {
@@ -40,35 +41,15 @@
} FileType;
/* get the file's type; follows symlinks */
FileType getFileType(const char* fileName);
-
-// MinGW doesn't support nanosecond resolution in stat() modification time, and given
-// that it only matters on the device it's ok to keep it at the second level there.
-#ifdef _WIN32
-using ModDate = time_t;
-inline constexpr ModDate kInvalidModDate = ModDate(-1);
-inline constexpr unsigned long long kModDateResolutionNs = 1ull * 1000 * 1000 * 1000;
-inline time_t toTimeT(ModDate m) {
- return m;
-}
-#else
-using ModDate = timespec;
-inline constexpr ModDate kInvalidModDate = {-1, -1};
-inline constexpr unsigned long long kModDateResolutionNs = 1;
-inline time_t toTimeT(ModDate m) {
- return m.tv_sec;
-}
-#endif
-
-/* get the file's modification date; returns kInvalidModDate w/errno set on failure */
-ModDate getFileModDate(const char* fileName);
+/* get the file's modification date; returns -1 w/errno set on failure */
+time_t getFileModDate(const char* fileName);
/* same, but also returns -1 if the file has already been deleted */
-ModDate getFileModDate(int fd);
+time_t getFileModDate(int fd);
// Check if |path| or |fd| resides on a readonly filesystem.
bool isReadonlyFilesystem(const char* path);
bool isReadonlyFilesystem(int fd);
-} // namespace android
+}; // namespace android
-// Whoever uses getFileModDate() will need this as well
-bool operator==(const timespec& l, const timespec& r);
+#endif // _LIBS_ANDROID_FW_MISC_H
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 9bdaf18a..93dcaf5 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -28,13 +28,11 @@
#include <sys/vfs.h>
#endif // __linux__
+#include <cstring>
+#include <cstdio>
#include <errno.h>
#include <sys/stat.h>
-#include <cstdio>
-#include <cstring>
-#include <tuple>
-
namespace android {
/*
@@ -75,32 +73,27 @@
}
}
-static ModDate getModDate(const struct stat& st) {
-#ifdef _WIN32
- return st.st_mtime;
-#else
- return st.st_mtim;
-#endif
+/*
+ * Get a file's modification date.
+ */
+time_t getFileModDate(const char* fileName) {
+ struct stat sb;
+ if (stat(fileName, &sb) < 0) {
+ return (time_t)-1;
+ }
+ return sb.st_mtime;
}
-ModDate getFileModDate(const char* fileName) {
- struct stat sb;
- if (stat(fileName, &sb) < 0) {
- return kInvalidModDate;
- }
- return getModDate(sb);
-}
-
-ModDate getFileModDate(int fd) {
- struct stat sb;
- if (fstat(fd, &sb) < 0) {
- return kInvalidModDate;
- }
- if (sb.st_nlink <= 0) {
- errno = ENOENT;
- return kInvalidModDate;
- }
- return getModDate(sb);
+time_t getFileModDate(int fd) {
+ struct stat sb;
+ if (fstat(fd, &sb) < 0) {
+ return (time_t)-1;
+ }
+ if (sb.st_nlink <= 0) {
+ errno = ENOENT;
+ return (time_t)-1;
+ }
+ return sb.st_mtime;
}
#ifndef __linux__
@@ -131,8 +124,4 @@
}
#endif // __linux__
-} // namespace android
-
-bool operator==(const timespec& l, const timespec& r) {
- return std::tie(l.tv_sec, l.tv_nsec) == std::tie(r.tv_sec, l.tv_nsec);
-}
+}; // namespace android
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index cb2e56f..60aa7d8 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#include <chrono>
-#include <thread>
-
#include "android-base/file.h"
#include "androidfw/ApkAssets.h"
#include "androidfw/AssetManager2.h"
@@ -30,7 +27,6 @@
#include "data/overlayable/R.h"
#include "data/system/R.h"
-using namespace std::chrono_literals;
using ::testing::NotNull;
namespace overlay = com::android::overlay;
@@ -222,13 +218,10 @@
unlink(temp_file.path);
ASSERT_FALSE(apk_assets->IsUpToDate());
-
- const auto sleep_duration =
- std::chrono::nanoseconds(std::max(kModDateResolutionNs, 1'000'000ull));
- std::this_thread::sleep_for(sleep_duration);
+ sleep(2);
base::WriteStringToFile("hello", temp_file.path);
- std::this_thread::sleep_for(sleep_duration);
+ sleep(2);
ASSERT_FALSE(apk_assets->IsUpToDate());
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 3409c29..defbc11 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,8 +16,6 @@
package com.android.externalstorage;
-import static java.util.regex.Pattern.CASE_INSENSITIVE;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
@@ -61,12 +59,15 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
-import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* Presents content of the shared (a.k.a. "external") storage.
@@ -89,12 +90,9 @@
private static final Uri BASE_URI =
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();
- /**
- * Regex for detecting {@code /Android/data/}, {@code /Android/obb/} and
- * {@code /Android/sandbox/} along with all their subdirectories and content.
- */
- private static final Pattern PATTERN_RESTRICTED_ANDROID_SUBTREES =
- Pattern.compile("^Android/(?:data|obb|sandbox)(?:/.+)?", CASE_INSENSITIVE);
+ private static final String PRIMARY_EMULATED_STORAGE_PATH = "/storage/emulated/";
+
+ private static final String STORAGE_PATH = "/storage/";
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
@@ -309,11 +307,70 @@
return false;
}
- final String path = getPathFromDocId(documentId);
- return PATTERN_RESTRICTED_ANDROID_SUBTREES.matcher(path).matches();
+ try {
+ final RootInfo root = getRootFromDocId(documentId);
+ final String canonicalPath = getPathFromDocId(documentId);
+ return isRestrictedPath(root.rootId, canonicalPath);
+ } catch (Exception e) {
+ return true;
+ }
}
/**
+ * Based on the given root id and path, we restrict path access if file is Android/data or
+ * Android/obb or Android/sandbox or one of their subdirectories.
+ *
+ * @param canonicalPath of the file
+ * @return true if path is restricted
+ */
+ private boolean isRestrictedPath(String rootId, String canonicalPath) {
+ if (rootId == null || canonicalPath == null) {
+ return true;
+ }
+
+ final String rootPath;
+ if (rootId.equalsIgnoreCase(ROOT_ID_PRIMARY_EMULATED)) {
+ // Creates "/storage/emulated/<user-id>"
+ rootPath = PRIMARY_EMULATED_STORAGE_PATH + UserHandle.myUserId();
+ } else {
+ // Creates "/storage/<volume-uuid>"
+ rootPath = STORAGE_PATH + rootId;
+ }
+ List<java.nio.file.Path> restrictedPathList = Arrays.asList(
+ Paths.get(rootPath, "Android", "data"),
+ Paths.get(rootPath, "Android", "obb"),
+ Paths.get(rootPath, "Android", "sandbox"));
+ // We need to identify restricted parent paths which actually exist on the device
+ List<java.nio.file.Path> validRestrictedPathsToCheck = restrictedPathList.stream().filter(
+ Files::exists).collect(Collectors.toList());
+
+ boolean isRestricted = false;
+ java.nio.file.Path filePathToCheck = Paths.get(rootPath, canonicalPath);
+ try {
+ while (filePathToCheck != null) {
+ for (java.nio.file.Path restrictedPath : validRestrictedPathsToCheck) {
+ if (Files.isSameFile(restrictedPath, filePathToCheck)) {
+ isRestricted = true;
+ Log.v(TAG, "Restricting access for path: " + filePathToCheck);
+ break;
+ }
+ }
+ if (isRestricted) {
+ break;
+ }
+
+ filePathToCheck = filePathToCheck.getParent();
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Error in checking file equality check.", e);
+ isRestricted = true;
+ }
+
+ return isRestricted;
+ }
+
+
+ /**
* Check that the directory is the root of storage or blocked file from tree.
* <p>
* Note, that this is different from hidden documents: blocked documents <b>WILL</b> appear
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index 3e2d832..d3c345d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -98,7 +98,7 @@
*/
suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean
- suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode)
+ suspend fun setRingerModeInternal(audioStream: AudioStream, mode: RingerMode)
/** Gets audio device category. */
@AudioDeviceCategory suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int
@@ -248,8 +248,8 @@
}
}
- override suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) {
- withContext(backgroundCoroutineContext) { audioManager.ringerMode = mode.value }
+ override suspend fun setRingerModeInternal(audioStream: AudioStream, mode: RingerMode) {
+ withContext(backgroundCoroutineContext) { audioManager.ringerModeInternal = mode.value }
}
@AudioDeviceCategory
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
index 08863b5..dca890d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
@@ -68,7 +68,7 @@
if (audioStream.value == AudioManager.STREAM_RING) {
val mode =
if (isMuted) AudioManager.RINGER_MODE_VIBRATE else AudioManager.RINGER_MODE_NORMAL
- audioRepository.setRingerMode(audioStream, RingerMode(mode))
+ audioRepository.setRingerModeInternal(audioStream, RingerMode(mode))
}
val mutedChanged = audioRepository.setMuted(audioStream, isMuted)
if (mutedChanged && !isMuted) {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
index 52e6391..8a3b1dfb 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
@@ -74,6 +74,8 @@
private lateinit var underTest: AudioRepository
+ private var ringerModeInternal: RingerMode = RingerMode(AudioManager.RINGER_MODE_NORMAL)
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -82,7 +84,7 @@
`when`(audioManager.communicationDevice).thenReturn(communicationDevice)
`when`(audioManager.getStreamMinVolume(anyInt())).thenReturn(MIN_VOLUME)
`when`(audioManager.getStreamMaxVolume(anyInt())).thenReturn(MAX_VOLUME)
- `when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
+ `when`(audioManager.ringerModeInternal).then { ringerModeInternal.value }
`when`(audioManager.setStreamVolume(anyInt(), anyInt(), anyInt())).then {
val streamType = it.arguments[0] as Int
volumeByStream[it.arguments[0] as Int] = it.arguments[1] as Int
@@ -103,6 +105,10 @@
`when`(audioManager.isStreamMute(anyInt())).thenAnswer {
isMuteByStream.getOrDefault(it.arguments[0] as Int, false)
}
+ `when`(audioManager.setRingerModeInternal(anyInt())).then {
+ ringerModeInternal = RingerMode(it.arguments[0] as Int)
+ Unit
+ }
underTest =
AudioRepositoryImpl(
@@ -137,7 +143,7 @@
underTest.ringerMode.onEach { modes.add(it) }.launchIn(backgroundScope)
runCurrent()
- `when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
+ ringerModeInternal = RingerMode(AudioManager.RINGER_MODE_SILENT)
triggerEvent(AudioManagerEvent.InternalRingerModeChanged)
runCurrent()
@@ -150,6 +156,19 @@
}
@Test
+ fun changingRingerMode_changesRingerModeInternal() {
+ testScope.runTest {
+ underTest.setRingerModeInternal(
+ AudioStream(AudioManager.STREAM_SYSTEM),
+ RingerMode(AudioManager.RINGER_MODE_SILENT),
+ )
+ runCurrent()
+
+ assertThat(ringerModeInternal).isEqualTo(RingerMode(AudioManager.RINGER_MODE_SILENT))
+ }
+ }
+
+ @Test
fun communicationDeviceChanges_repositoryEmits() {
testScope.runTest {
var device: AudioDeviceInfo? = null
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index ba885f7..1f98cd8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -65,7 +65,6 @@
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformOutlinedButton
import com.android.compose.animation.Easings
@@ -355,7 +354,10 @@
fun Content(modifier: Modifier) {
// Wrap PIN entry in a Box so it is visible to accessibility (even if empty).
- Box(modifier = modifier.fillMaxWidth().wrapContentHeight()) {
+ Box(
+ modifier = modifier.fillMaxWidth().wrapContentHeight(),
+ contentAlignment = Alignment.Center,
+ ) {
Row(
modifier
.heightIn(min = shapeAnimations.shapeSize)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt
index e1f73e3..4e8121f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt
@@ -17,9 +17,12 @@
package com.android.systemui.common.ui.compose
+import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.shared.model.Text.Companion.loadText
/** Returns the loaded [String] or `null` if there isn't one. */
@Composable
@@ -29,3 +32,7 @@
is Text.Resource -> stringResource(res)
}
}
+
+fun Text.toAnnotatedString(context: Context): AnnotatedString? {
+ return loadText(context)?.let { AnnotatedString(it) }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
index cd0c58f..c4fc132 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
@@ -19,12 +19,15 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.contextualeducation.GestureType.ALL_APPS
import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.education.data.repository.contextualEducationRepository
import com.android.systemui.education.data.repository.fakeEduClock
+import com.android.systemui.keyboard.data.repository.keyboardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
+import com.android.systemui.touchpad.data.repository.touchpadRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -36,22 +39,62 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val underTest = kosmos.keyboardTouchpadEduStatsInteractor
+ private val keyboardRepository = kosmos.keyboardRepository
+ private val touchpadRepository = kosmos.touchpadRepository
+ private val repository = kosmos.contextualEducationRepository
@Test
- fun dataUpdatedOnIncrementSignalCount() =
+ fun dataUpdatedOnIncrementSignalCountWhenTouchpadConnected() =
testScope.runTest {
- val model by
- collectLastValue(kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK))
+ touchpadRepository.setIsAnyTouchpadConnected(true)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
val originalValue = model!!.signalCount
underTest.incrementSignalCount(BACK)
+
assertThat(model?.signalCount).isEqualTo(originalValue + 1)
}
@Test
+ fun dataUnchangedOnIncrementSignalCountWhenTouchpadDisconnected() =
+ testScope.runTest {
+ touchpadRepository.setIsAnyTouchpadConnected(false)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
+ val originalValue = model!!.signalCount
+ underTest.incrementSignalCount(BACK)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
+ fun dataUpdatedOnIncrementSignalCountWhenKeyboardConnected() =
+ testScope.runTest {
+ keyboardRepository.setIsAnyKeyboardConnected(true)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS))
+ val originalValue = model!!.signalCount
+ underTest.incrementSignalCount(ALL_APPS)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue + 1)
+ }
+
+ @Test
+ fun dataUnchangedOnIncrementSignalCountWhenKeyboardDisconnected() =
+ testScope.runTest {
+ keyboardRepository.setIsAnyKeyboardConnected(false)
+
+ val model by collectLastValue(repository.readGestureEduModelFlow(ALL_APPS))
+ val originalValue = model!!.signalCount
+ underTest.incrementSignalCount(ALL_APPS)
+
+ assertThat(model?.signalCount).isEqualTo(originalValue)
+ }
+
+ @Test
fun dataAddedOnUpdateShortcutTriggerTime() =
testScope.runTest {
- val model by
- collectLastValue(kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK))
+ val model by collectLastValue(repository.readGestureEduModelFlow(BACK))
assertThat(model?.lastShortcutTriggeredTime).isNull()
underTest.updateShortcutTriggerTime(BACK)
assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt
index 1e5599b..93ba265 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt
@@ -19,7 +19,6 @@
import android.content.ComponentName
import android.content.packageManager
import android.content.pm.PackageManager
-import android.content.pm.ServiceInfo
import android.content.pm.UserInfo
import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -35,6 +34,7 @@
import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository
import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
@@ -100,6 +100,7 @@
Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)),
Text.Loaded(tileService1),
Text.Loaded(appName1),
+ TileCategory.PROVIDED_BY_APP,
)
val expectedData2 =
EditTileData(
@@ -107,6 +108,7 @@
Icon.Loaded(drawable2, ContentDescription.Loaded(tileService2)),
Text.Loaded(tileService2),
Text.Loaded(appName2),
+ TileCategory.PROVIDED_BY_APP,
)
assertThat(editTileDataList).containsExactly(expectedData1, expectedData2)
@@ -144,6 +146,7 @@
Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)),
Text.Loaded(tileService1),
Text.Loaded(appName1),
+ TileCategory.PROVIDED_BY_APP,
)
val editTileDataList = underTest.getCustomTileData()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
index deefbf5..053a59a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
@@ -132,6 +133,7 @@
icon = Icon.Loaded(icon, ContentDescription.Loaded(tileName)),
label = Text.Loaded(tileName),
appName = Text.Loaded(appName),
+ category = TileCategory.PROVIDED_BY_APP,
)
assertThat(editTiles.customTiles).hasSize(1)
@@ -181,7 +183,8 @@
tileSpec = this,
icon = Icon.Resource(android.R.drawable.star_on, ContentDescription.Loaded(spec)),
label = Text.Loaded(spec),
- appName = null
+ appName = null,
+ category = TileCategory.UNKNOWN,
)
}
@@ -192,6 +195,7 @@
Icon.Resource(uiConfig.iconRes, ContentDescription.Resource(uiConfig.labelRes)),
label = Text.Resource(uiConfig.labelRes),
appName = null,
+ category = category,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
index 7f01fad..484a8ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
@@ -16,11 +16,11 @@
package com.android.systemui.qs.panels.ui.compose
+import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.shared.model.Text
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.model.GridCell
@@ -28,6 +28,7 @@
import com.android.systemui.qs.panels.ui.model.TileGridCell
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -141,10 +142,11 @@
EditTileViewModel(
tileSpec = TileSpec.create(tileSpec),
icon = Icon.Resource(0, null),
- label = Text.Loaded("unused"),
+ label = AnnotatedString("unused"),
appName = null,
isCurrent = true,
availableEditActions = emptySet(),
+ category = TileCategory.UNKNOWN,
),
width,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
index 601779f..583db72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.ui.compose.toAnnotatedString
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
@@ -42,6 +43,7 @@
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
@@ -190,7 +192,7 @@
.forEach {
val data = getEditTileData(it.tileSpec)
- assertThat(it.label).isEqualTo(data.label)
+ assertThat(it.label).isEqualTo(data.label.toAnnotatedString(context))
assertThat(it.icon).isEqualTo(data.icon)
assertThat(it.appName).isNull()
}
@@ -224,15 +226,19 @@
// service1
val model1 = tiles!!.first { it.tileSpec == TileSpec.create(component1) }
- assertThat(model1.label).isEqualTo(Text.Loaded(tileService1))
- assertThat(model1.appName).isEqualTo(Text.Loaded(appName1))
+ assertThat(model1.label)
+ .isEqualTo(Text.Loaded(tileService1).toAnnotatedString(context))
+ assertThat(model1.appName)
+ .isEqualTo(Text.Loaded(appName1).toAnnotatedString(context))
assertThat(model1.icon)
.isEqualTo(Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)))
// service2
val model2 = tiles!!.first { it.tileSpec == TileSpec.create(component2) }
- assertThat(model2.label).isEqualTo(Text.Loaded(tileService2))
- assertThat(model2.appName).isEqualTo(Text.Loaded(appName2))
+ assertThat(model2.label)
+ .isEqualTo(Text.Loaded(tileService2).toAnnotatedString(context))
+ assertThat(model2.appName)
+ .isEqualTo(Text.Loaded(appName2).toAnnotatedString(context))
assertThat(model2.icon)
.isEqualTo(Icon.Loaded(drawable2, ContentDescription.Loaded(tileService2)))
}
@@ -559,7 +565,8 @@
tileSpec = this,
icon = Icon.Resource(R.drawable.star_on, ContentDescription.Loaded(spec)),
label = Text.Loaded(spec),
- appName = null
+ appName = null,
+ category = TileCategory.UNKNOWN,
)
}
@@ -570,6 +577,7 @@
Icon.Resource(uiConfig.iconRes, ContentDescription.Resource(uiConfig.labelRes)),
label = Text.Resource(uiConfig.labelRes),
appName = null,
+ category = category,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/model/GroupAndSortCategoryAndNameTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/model/GroupAndSortCategoryAndNameTest.kt
new file mode 100644
index 0000000..7f90e3b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/model/GroupAndSortCategoryAndNameTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.shared.model
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GroupAndSortCategoryAndNameTest : SysuiTestCase() {
+
+ private val elements =
+ listOf(
+ CategoryAndName(TileCategory.DISPLAY, "B"),
+ CategoryAndName(TileCategory.PRIVACY, "A"),
+ CategoryAndName(TileCategory.DISPLAY, "C"),
+ CategoryAndName(TileCategory.UTILITIES, "B"),
+ CategoryAndName(TileCategory.CONNECTIVITY, "A"),
+ CategoryAndName(TileCategory.PROVIDED_BY_APP, "B"),
+ CategoryAndName(TileCategory.CONNECTIVITY, "C"),
+ CategoryAndName(TileCategory.ACCESSIBILITY, "A")
+ )
+
+ @Test
+ fun allElementsInResult() {
+ val grouped = groupAndSort(elements)
+ val allValues = grouped.values.reduce { acc, el -> acc + el }
+ assertThat(allValues).containsExactlyElementsIn(elements)
+ }
+
+ @Test
+ fun groupedByCategory() {
+ val grouped = groupAndSort(elements)
+ grouped.forEach { tileCategory, categoryAndNames ->
+ categoryAndNames.forEach { element ->
+ assertThat(element.category).isEqualTo(tileCategory)
+ }
+ }
+ }
+
+ @Test
+ fun sortedAlphabeticallyInEachCategory() {
+ val grouped = groupAndSort(elements)
+ grouped.values.forEach { elements ->
+ assertThat(elements.map(CategoryAndName::name)).isInOrder()
+ }
+ }
+
+ @Test
+ fun categoriesSortedInNaturalOrder() {
+ val grouped = groupAndSort(elements)
+ assertThat(grouped.keys).isInOrder()
+ }
+
+ @Test
+ fun missingCategoriesAreNotInResult() {
+ val grouped = groupAndSort(elements.filterNot { it.category == TileCategory.CONNECTIVITY })
+ assertThat(grouped.keys).doesNotContain(TileCategory.CONNECTIVITY)
+ }
+
+ companion object {
+ private fun CategoryAndName(category: TileCategory, name: String): CategoryAndName {
+ return object : CategoryAndName {
+ override val category = category
+ override val name = name
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
index aaad0fc..5a45060 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
@@ -184,7 +184,7 @@
)
val networkModel =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
)
@@ -219,7 +219,7 @@
)
val networkModel =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.NONE,
@@ -398,7 +398,7 @@
collectLastValue(
underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
)
- val networkModel = WifiNetworkModel.Inactive
+ val networkModel = WifiNetworkModel.Inactive()
connectivityRepository.setWifiConnected(validated = false)
wifiRepository.setIsWifiDefault(true)
@@ -416,7 +416,7 @@
underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
)
- val networkModel = WifiNetworkModel.Inactive
+ val networkModel = WifiNetworkModel.Inactive()
connectivityRepository.setWifiConnected(validated = false)
wifiRepository.setIsWifiDefault(true)
@@ -543,7 +543,7 @@
private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) {
val networkModel =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
hotspotDeviceType = hotspot,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
index 2444229..fa6d8bf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.kosmos.testCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
@@ -43,7 +44,8 @@
QSTileConfig(
TileSpec.create(RecordIssueModule.TILE_SPEC),
uiConfig,
- kosmos.qsEventLogger.getNewInstanceId()
+ kosmos.qsEventLogger.getNewInstanceId(),
+ TileCategory.UTILITIES,
)
private val resources = kosmos.mainResources
private val theme = resources.newTheme()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index b9ca8fc..c0a1592 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -78,7 +78,7 @@
@Test
fun ssid_inactiveNetwork_outputsNull() =
testScope.runTest {
- wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive())
var latest: String? = "default"
val job = underTest.ssid.onEach { latest = it }.launchIn(this)
@@ -93,7 +93,7 @@
fun ssid_carrierMergedNetwork_outputsNull() =
testScope.runTest {
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(subscriptionId = 2, level = 1)
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = 2, level = 1)
)
var latest: String? = "default"
@@ -109,7 +109,7 @@
fun ssid_unknownSsid_outputsNull() =
testScope.runTest {
wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = 1,
ssid = WifiManager.UNKNOWN_SSID,
)
@@ -128,7 +128,7 @@
fun ssid_validSsid_outputsSsid() =
testScope.runTest {
wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = 1,
ssid = "MyAwesomeWifiNetwork",
)
@@ -189,7 +189,7 @@
fun wifiNetwork_matchesRepoWifiNetwork() =
testScope.runTest {
val wifiNetwork =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
isValidated = true,
level = 3,
ssid = "AB",
@@ -263,7 +263,7 @@
val latest by collectLastValue(underTest.areNetworksAvailable)
wifiRepository.wifiScanResults.value = emptyList()
- wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive())
assertThat(latest).isFalse()
}
@@ -280,7 +280,7 @@
WifiScanEntry(ssid = "ssid 3"),
)
- wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive())
assertThat(latest).isTrue()
}
@@ -298,7 +298,7 @@
)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
ssid = "ssid 2",
level = 2,
)
@@ -318,7 +318,7 @@
)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
ssid = "ssid 2",
level = 2,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 72a45b9..141e304 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -115,7 +115,7 @@
val latestKeyguard by collectLastValue(keyguard.wifiIcon)
val latestQs by collectLastValue(qs.wifiIcon)
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(isValidated = true, level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(isValidated = true, level = 1))
assertThat(latestHome).isInstanceOf(WifiIcon.Visible::class.java)
assertThat(latestHome).isEqualTo(latestKeyguard)
@@ -129,7 +129,7 @@
// Even WHEN the network has a valid hotspot type
wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
isValidated = true,
level = 1,
hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.LAPTOP,
@@ -191,7 +191,7 @@
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(ssid = null, level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(ssid = null, level = 1))
val activityIn by collectLastValue(underTest.isActivityInViewVisible)
val activityOut by collectLastValue(underTest.isActivityOutViewVisible)
@@ -214,7 +214,7 @@
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
createAndSetViewModel()
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(ssid = null, level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(ssid = null, level = 1))
val activityIn by collectLastValue(underTest.isActivityInViewVisible)
val activityOut by collectLastValue(underTest.isActivityOutViewVisible)
@@ -463,6 +463,6 @@
}
companion object {
- private val ACTIVE_VALID_WIFI_NETWORK = WifiNetworkModel.Active(ssid = "AB", level = 1)
+ private val ACTIVE_VALID_WIFI_NETWORK = WifiNetworkModel.Active.of(ssid = "AB", level = 1)
}
}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1fb1dad..589d9dd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3812,4 +3812,33 @@
<!-- Toast message for notifying users to use regular brightness bar to lower the brightness. [CHAR LIMIT=NONE] -->
<string name="accessibility_deprecate_extra_dim_dialog_toast">
Extra dim shortcut removed. To lower your brightness, use the regular brightness bar.</string>
+
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to connectivity, e.g. Internet. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_connectivity">
+ Connectivity
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to accessibility functions, e.g. Hearing devices. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_accessibility">
+ Accessibility
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to general utilities, e.g. Flashlight. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_utilities">
+ Utilities
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to privacy, e.g. Mic access. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_privacy">
+ Privacy
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are provided by an app, e.g. Calculator. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_providedByApps">
+ Provided by apps
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to the display, e.g. Dark theme. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_display">
+ Display
+ </string>
+ <!-- Label for category in QS Edit mode list of tiles, for tiles with an unknown category. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_category_unknown">
+ Unknown
+ </string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index bb80396..cd9efaf 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -19,6 +19,7 @@
import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.ColorCorrectionTile
import com.android.systemui.qs.tiles.ColorInversionTile
@@ -179,6 +180,7 @@
labelRes = R.string.quick_settings_color_correction_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.ACCESSIBILITY,
)
/** Inject ColorCorrectionTile into tileViewModelMap in QSModule */
@@ -210,6 +212,7 @@
labelRes = R.string.quick_settings_inversion_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.ACCESSIBILITY,
)
/** Inject ColorInversionTile into tileViewModelMap in QSModule */
@@ -241,6 +244,7 @@
labelRes = R.string.quick_settings_font_scaling_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
/** Inject FontScaling Tile into tileViewModelMap in QSModule */
@@ -272,6 +276,7 @@
labelRes = com.android.internal.R.string.reduce_bright_colors_feature_name,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
@Provides
@@ -286,6 +291,7 @@
labelRes = R.string.quick_settings_hearing_devices_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.ACCESSIBILITY,
)
/**
@@ -322,6 +328,7 @@
labelRes = R.string.quick_settings_onehanded_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.ACCESSIBILITY,
)
/** Inject One Handed Mode Tile into tileViewModelMap in QSModule. */
@@ -355,6 +362,7 @@
labelRes = R.string.quick_settings_night_display_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
index 8a9a322..831bc1d 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
@@ -2,6 +2,7 @@
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.BatterySaverTile
import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
@@ -33,7 +34,7 @@
@IntoMap
@StringKey(BATTERY_SAVER_TILE_SPEC)
fun provideBatterySaverAvailabilityInteractor(
- impl: BatterySaverTileDataInteractor
+ impl: BatterySaverTileDataInteractor
): QSTileAvailabilityInteractor
companion object {
@@ -51,6 +52,7 @@
labelRes = R.string.battery_detail_switch_title,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
)
/** Inject BatterySaverTile into tileViewModelMap in QSModule */
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index db7ffc1..037b6fa 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -48,6 +48,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.DeviceControlsTile
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -86,15 +87,16 @@
@IntoMap
@StringKey(DEVICE_CONTROLS_SPEC)
fun provideDeviceControlsTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
- QSTileConfig(
- tileSpec = TileSpec.create(DEVICE_CONTROLS_SPEC),
- uiConfig =
- QSTileUIConfig.Resource(
- iconRes = com.android.systemui.res.R.drawable.controls_icon,
- labelRes = com.android.systemui.res.R.string.quick_controls_title
- ),
- instanceId = uiEventLogger.getNewInstanceId(),
- )
+ QSTileConfig(
+ tileSpec = TileSpec.create(DEVICE_CONTROLS_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = com.android.systemui.res.R.drawable.controls_icon,
+ labelRes = com.android.systemui.res.R.string.quick_controls_title
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
+ )
}
@Binds
@@ -115,12 +117,12 @@
@Binds
abstract fun provideSettingsManager(
- manager: ControlsSettingsRepositoryImpl
+ manager: ControlsSettingsRepositoryImpl
): ControlsSettingsRepository
@Binds
abstract fun provideDialogManager(
- manager: ControlsSettingsDialogManagerImpl
+ manager: ControlsSettingsDialogManagerImpl
): ControlsSettingsDialogManager
@Binds
@@ -141,8 +143,7 @@
repository: SelectedComponentRepositoryImpl
): SelectedComponentRepository
- @BindsOptionalOf
- abstract fun optionalPersistenceWrapper(): ControlsFavoritePersistenceWrapper
+ @BindsOptionalOf abstract fun optionalPersistenceWrapper(): ControlsFavoritePersistenceWrapper
@BindsOptionalOf
abstract fun provideControlsTileResourceConfiguration(): ControlsTileResourceConfiguration
@@ -157,23 +158,17 @@
@Binds
@IntoMap
@ClassKey(ControlsFavoritingActivity::class)
- abstract fun provideControlsFavoritingActivity(
- activity: ControlsFavoritingActivity
- ): Activity
+ abstract fun provideControlsFavoritingActivity(activity: ControlsFavoritingActivity): Activity
@Binds
@IntoMap
@ClassKey(ControlsEditingActivity::class)
- abstract fun provideControlsEditingActivity(
- activity: ControlsEditingActivity
- ): Activity
+ abstract fun provideControlsEditingActivity(activity: ControlsEditingActivity): Activity
@Binds
@IntoMap
@ClassKey(ControlsRequestDialog::class)
- abstract fun provideControlsRequestDialog(
- activity: ControlsRequestDialog
- ): Activity
+ abstract fun provideControlsRequestDialog(activity: ControlsRequestDialog): Activity
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index f6ac7a5..a45ad15 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -38,6 +38,7 @@
import com.android.systemui.dreams.homecontrols.HomeControlsDreamService;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.pipeline.shared.TileSpec;
+import com.android.systemui.qs.shared.model.TileCategory;
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig;
import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy;
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig;
@@ -196,6 +197,7 @@
R.drawable.ic_qs_screen_saver,
R.string.quick_settings_screensaver_label),
uiEventLogger.getNewInstanceId(),
+ TileCategory.UTILITIES,
tileSpec.getSpec(),
QSTilePolicy.NoRestrictions.INSTANCE
);
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
index 3223433..eb4eee2 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
@@ -16,11 +16,17 @@
package com.android.systemui.education.domain.interactor
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.ALL_APPS
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
/**
@@ -39,12 +45,17 @@
@Inject
constructor(
@Background private val backgroundScope: CoroutineScope,
- private val contextualEducationInteractor: ContextualEducationInteractor
+ private val contextualEducationInteractor: ContextualEducationInteractor,
+ private val inputDeviceRepository: UserInputDeviceRepository,
) : KeyboardTouchpadEduStatsInteractor {
override fun incrementSignalCount(gestureType: GestureType) {
- // Todo: check if keyboard/touchpad is connected before update
- backgroundScope.launch { contextualEducationInteractor.incrementSignalCount(gestureType) }
+ backgroundScope.launch {
+ val targetDevice = getTargetDevice(gestureType)
+ if (isTargetDeviceConnected(targetDevice)) {
+ contextualEducationInteractor.incrementSignalCount(gestureType)
+ }
+ }
}
override fun updateShortcutTriggerTime(gestureType: GestureType) {
@@ -52,4 +63,24 @@
contextualEducationInteractor.updateShortcutTriggerTime(gestureType)
}
}
+
+ private suspend fun isTargetDeviceConnected(deviceType: DeviceType): Boolean {
+ if (deviceType == KEYBOARD) {
+ return inputDeviceRepository.isAnyKeyboardConnectedForUser.first().isConnected
+ } else if (deviceType == TOUCHPAD) {
+ return inputDeviceRepository.isAnyTouchpadConnectedForUser.first().isConnected
+ }
+ return false
+ }
+
+ /**
+ * Keyboard shortcut education would be provided for All Apps. Touchpad gesture education would
+ * be provided for the rest of the gesture types (i.e. Home, Overview, Back). This method maps
+ * gesture to its target education device.
+ */
+ private fun getTargetDevice(gestureType: GestureType) =
+ when (gestureType) {
+ ALL_APPS -> KEYBOARD
+ else -> TOUCHPAD
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
index 7ecacdc..092a25a 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
@@ -16,9 +16,17 @@
package com.android.systemui.inputdevice.tutorial
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
import com.android.systemui.CoreStartable
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity
import com.android.systemui.shared.Flags.newTouchpadGesturesTutorial
import dagger.Lazy
import javax.inject.Inject
@@ -27,11 +35,35 @@
@SysUISingleton
class KeyboardTouchpadTutorialCoreStartable
@Inject
-constructor(private val tutorialNotificationCoordinator: Lazy<TutorialNotificationCoordinator>) :
- CoreStartable {
+constructor(
+ private val tutorialNotificationCoordinator: Lazy<TutorialNotificationCoordinator>,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ @Application private val applicationContext: Context,
+) : CoreStartable {
override fun start() {
if (newTouchpadGesturesTutorial()) {
tutorialNotificationCoordinator.get().start()
+ registerTutorialBroadcastReceiver()
}
}
+
+ private fun registerTutorialBroadcastReceiver() {
+ broadcastDispatcher.registerReceiver(
+ receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ applicationContext.startActivityAsUser(
+ Intent(
+ applicationContext,
+ KeyboardTouchpadTutorialActivity::class.java
+ ),
+ UserHandle.SYSTEM
+ )
+ }
+ },
+ filter = IntentFilter("com.android.systemui.action.KEYBOARD_TOUCHPAD_TUTORIAL"),
+ flags = Context.RECEIVER_EXPORTED,
+ user = UserHandle.ALL,
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 63f3d52..dcca12f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -82,6 +82,7 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.CornerRadius
@@ -92,8 +93,12 @@
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -824,9 +829,18 @@
// from the ViewModel.
var queryInternal by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
+ val focusManager = LocalFocusManager.current
LaunchedEffect(Unit) { focusRequester.requestFocus() }
SearchBar(
- modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
+ modifier =
+ Modifier.fillMaxWidth().focusRequester(focusRequester).onKeyEvent {
+ if (it.key == Key.DirectionDown) {
+ focusManager.moveFocus(FocusDirection.Down)
+ return@onKeyEvent true
+ } else {
+ return@onKeyEvent false
+ }
+ },
colors = SearchBarDefaults.colors(containerColor = MaterialTheme.colorScheme.surfaceBright),
query = queryInternal,
active = false,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 362e016c..df0f10a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.google.android.msdl.domain.MSDLPlayer
import dagger.Lazy
@@ -112,6 +113,7 @@
private val clockInteractor: KeyguardClockInteractor,
private val keyguardViewMediator: KeyguardViewMediator,
private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
@Main private val mainDispatcher: CoroutineDispatcher,
private val msdlPlayer: MSDLPlayer,
) : CoreStartable {
@@ -220,6 +222,7 @@
vibratorHelper,
falsingManager,
keyguardViewMediator,
+ statusBarKeyguardViewManager,
mainDispatcher,
msdlPlayer,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
index f2d39da..ecfabc3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
@@ -18,13 +18,12 @@
import android.annotation.SuppressLint
import android.graphics.PointF
+import android.view.InputDevice
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewPropertyAnimator
-import androidx.core.animation.CycleInterpolator
-import androidx.core.animation.ObjectAnimator
-import com.android.systemui.res.R
+import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.view.rawDistanceFrom
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
@@ -71,11 +70,10 @@
// Moving too far while performing a long-press gesture cancels that
// gesture.
if (
- event
- .rawDistanceFrom(
- downDisplayCoords.x,
- downDisplayCoords.y,
- ) > ViewConfiguration.getTouchSlop()
+ event.rawDistanceFrom(
+ downDisplayCoords.x,
+ downDisplayCoords.y,
+ ) > ViewConfiguration.getTouchSlop()
) {
cancel()
}
@@ -151,10 +149,14 @@
event: MotionEvent,
pointerIndex: Int = 0,
): Boolean {
- return when (event.getToolType(pointerIndex)) {
- MotionEvent.TOOL_TYPE_STYLUS -> true
- MotionEvent.TOOL_TYPE_MOUSE -> true
- else -> false
+ return if (Flags.nonTouchscreenDevicesBypassFalsing()) {
+ event.device?.supportsSource(InputDevice.SOURCE_TOUCHSCREEN) == false
+ } else {
+ when (event.getToolType(pointerIndex)) {
+ MotionEvent.TOOL_TYPE_STYLUS -> true
+ MotionEvent.TOOL_TYPE_MOUSE -> true
+ else -> false
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 5bb7b64..ed82159 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -24,6 +24,8 @@
import android.graphics.Rect
import android.util.Log
import android.view.HapticFeedbackConstants
+import android.view.InputDevice
+import android.view.MotionEvent
import android.view.View
import android.view.View.OnLayoutChangeListener
import android.view.View.VISIBLE
@@ -41,6 +43,7 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.keyguard.AuthInteractionProperties
+import com.android.systemui.Flags
import com.android.systemui.Flags.msdlFeedback
import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.Icon
@@ -73,6 +76,7 @@
import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.temporarydisplay.ViewPriority
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
@@ -115,6 +119,7 @@
vibratorHelper: VibratorHelper?,
falsingManager: FalsingManager?,
keyguardViewMediator: KeyguardViewMediator?,
+ statusBarKeyguardViewManager: StatusBarKeyguardViewManager?,
mainImmediateDispatcher: CoroutineDispatcher,
msdlPlayer: MSDLPlayer?,
): DisposableHandle {
@@ -124,12 +129,30 @@
if (KeyguardBottomAreaRefactor.isEnabled) {
disposables +=
view.onTouchListener { _, event ->
+ var consumed = false
if (falsingManager?.isFalseTap(FalsingManager.LOW_PENALTY) == false) {
+ // signifies a primary button click down has reached keyguardrootview
+ // we need to return true here otherwise an ACTION_UP will never arrive
+ if (Flags.nonTouchscreenDevicesBypassFalsing()) {
+ if (
+ event.action == MotionEvent.ACTION_DOWN &&
+ event.buttonState == MotionEvent.BUTTON_PRIMARY &&
+ !event.isTouchscreenSource()
+ ) {
+ consumed = true
+ } else if (
+ event.action == MotionEvent.ACTION_UP &&
+ !event.isTouchscreenSource()
+ ) {
+ statusBarKeyguardViewManager?.showBouncer(true)
+ consumed = true
+ }
+ }
viewModel.setRootViewLastTapPosition(
Point(event.x.toInt(), event.y.toInt())
)
}
- false
+ consumed
}
}
@@ -637,6 +660,10 @@
}
}
+ private fun MotionEvent.isTouchscreenSource(): Boolean {
+ return device?.supportsSource(InputDevice.SOURCE_TOUCHSCREEN) == true
+ }
+
private fun ViewPropertyAnimator.animateInIconTranslation(): ViewPropertyAnimator =
setInterpolator(Interpolators.DECELERATE_QUINT).translationY(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index f581a2e..0b8f741 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -417,6 +417,7 @@
null, // device entry haptics not required for preview mode
null, // falsing manager not required for preview mode
null, // keyguard view mediator is not required for preview mode
+ null, // primary bouncer interactor is not required for preview mode
mainDispatcher,
null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
index 4ad437c..84aae65 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
@@ -70,6 +70,7 @@
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager.Companion.isMediaNotification
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.MediaLogger
import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
import com.android.systemui.media.controls.shared.model.MediaAction
@@ -84,7 +85,7 @@
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.media.controls.util.SmallHash
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
@@ -186,7 +187,6 @@
private val mediaDeviceManager: MediaDeviceManager,
mediaDataCombineLatest: MediaDataCombineLatest,
private val mediaDataFilter: LegacyMediaDataFilterImpl,
- private val activityStarter: ActivityStarter,
private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
private var useMediaResumption: Boolean,
private val useQsMediaPlayer: Boolean,
@@ -197,6 +197,7 @@
private val smartspaceManager: SmartspaceManager?,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val mediaDataLoader: dagger.Lazy<MediaDataLoader>,
+ private val mediaLogger: MediaLogger,
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener, MediaDataManager {
companion object {
@@ -273,7 +274,6 @@
mediaDeviceManager: MediaDeviceManager,
mediaDataCombineLatest: MediaDataCombineLatest,
mediaDataFilter: LegacyMediaDataFilterImpl,
- activityStarter: ActivityStarter,
smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
clock: SystemClock,
tunerService: TunerService,
@@ -282,6 +282,7 @@
smartspaceManager: SmartspaceManager?,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
mediaDataLoader: dagger.Lazy<MediaDataLoader>,
+ mediaLogger: MediaLogger,
) : this(
context,
// Loading bitmap for UMO background can take longer time, so it cannot run on the default
@@ -301,7 +302,6 @@
mediaDeviceManager,
mediaDataCombineLatest,
mediaDataFilter,
- activityStarter,
smartspaceMediaDataProvider,
Utils.useMediaResumption(context),
Utils.useQsMediaPlayer(context),
@@ -312,6 +312,7 @@
smartspaceManager,
keyguardUpdateMonitor,
mediaDataLoader,
+ mediaLogger,
)
private val appChangeReceiver =
@@ -564,6 +565,42 @@
val resumeAction: Runnable? = currentEntry?.resumeAction
val hasCheckedForResume = currentEntry?.hasCheckedForResume == true
val active = currentEntry?.active ?: true
+ val mediaController = mediaControllerFactory.create(result.token!!)
+
+ val mediaData =
+ MediaData(
+ userId = sbn.normalizedUserId,
+ initialized = true,
+ app = result.appName,
+ appIcon = result.appIcon,
+ artist = result.artist,
+ song = result.song,
+ artwork = result.artworkIcon,
+ actions = result.actionIcons,
+ actionsToShowInCompact = result.actionsToShowInCompact,
+ semanticActions = result.semanticActions,
+ packageName = sbn.packageName,
+ token = result.token,
+ clickIntent = result.clickIntent,
+ device = result.device,
+ active = active,
+ resumeAction = resumeAction,
+ playbackLocation = result.playbackLocation,
+ notificationKey = key,
+ hasCheckedForResume = hasCheckedForResume,
+ isPlaying = result.isPlaying,
+ isClearable = !sbn.isOngoing,
+ lastActive = lastActive,
+ createdTimestampMillis = createdTimestampMillis,
+ instanceId = instanceId,
+ appUid = result.appUid,
+ isExplicit = result.isExplicit,
+ )
+
+ if (isSameMediaData(context, mediaController, mediaData, currentEntry)) {
+ mediaLogger.logDuplicateMediaNotification(key)
+ return@withContext
+ }
// We need to log the correct media added.
if (isNewlyActiveEntry) {
@@ -583,40 +620,7 @@
)
}
- withContext(mainDispatcher) {
- onMediaDataLoaded(
- key,
- oldKey,
- MediaData(
- userId = sbn.normalizedUserId,
- initialized = true,
- app = result.appName,
- appIcon = result.appIcon,
- artist = result.artist,
- song = result.song,
- artwork = result.artworkIcon,
- actions = result.actionIcons,
- actionsToShowInCompact = result.actionsToShowInCompact,
- semanticActions = result.semanticActions,
- packageName = sbn.packageName,
- token = result.token,
- clickIntent = result.clickIntent,
- device = result.device,
- active = active,
- resumeAction = resumeAction,
- playbackLocation = result.playbackLocation,
- notificationKey = key,
- hasCheckedForResume = hasCheckedForResume,
- isPlaying = result.isPlaying,
- isClearable = !sbn.isOngoing,
- lastActive = lastActive,
- createdTimestampMillis = createdTimestampMillis,
- instanceId = instanceId,
- appUid = result.appUid,
- isExplicit = result.isExplicit,
- )
- )
- }
+ withContext(mainDispatcher) { onMediaDataLoaded(key, oldKey, mediaData) }
}
/** Add a listener for changes in this class */
@@ -1100,6 +1104,47 @@
val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
val appUid = appInfo?.uid ?: Process.INVALID_UID
+ val lastActive = systemClock.elapsedRealtime()
+ val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
+ val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
+ val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
+ val active = mediaEntries[key]?.active ?: true
+ var mediaData =
+ MediaData(
+ sbn.normalizedUserId,
+ true,
+ appName,
+ smallIcon,
+ artist,
+ song,
+ artWorkIcon,
+ actionIcons,
+ actionsToShowCollapsed,
+ semanticActions,
+ sbn.packageName,
+ token,
+ notif.contentIntent,
+ device,
+ active,
+ resumeAction = resumeAction,
+ playbackLocation = playbackLocation,
+ notificationKey = key,
+ hasCheckedForResume = hasCheckedForResume,
+ isPlaying = isPlaying,
+ isClearable = !sbn.isOngoing,
+ lastActive = lastActive,
+ createdTimestampMillis = createdTimestampMillis,
+ instanceId = instanceId,
+ appUid = appUid,
+ isExplicit = isExplicit,
+ smartspaceId = SmallHash.hash(appUid + systemClock.currentTimeMillis().toInt()),
+ )
+
+ if (isSameMediaData(context, mediaController, mediaData, currentEntry)) {
+ mediaLogger.logDuplicateMediaNotification(key)
+ return
+ }
+
if (isNewlyActiveEntry) {
logSingleVsMultipleMediaAdded(appUid, sbn.packageName, instanceId)
logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation)
@@ -1107,44 +1152,17 @@
logger.logPlaybackLocationChange(appUid, sbn.packageName, instanceId, playbackLocation)
}
- val lastActive = systemClock.elapsedRealtime()
- val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
foregroundExecutor.execute {
- val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
- val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
- val active = mediaEntries[key]?.active ?: true
- onMediaDataLoaded(
- key,
- oldKey,
- MediaData(
- sbn.normalizedUserId,
- true,
- appName,
- smallIcon,
- artist,
- song,
- artWorkIcon,
- actionIcons,
- actionsToShowCollapsed,
- semanticActions,
- sbn.packageName,
- token,
- notif.contentIntent,
- device,
- active,
- resumeAction = resumeAction,
- playbackLocation = playbackLocation,
- notificationKey = key,
- hasCheckedForResume = hasCheckedForResume,
- isPlaying = isPlaying,
- isClearable = !sbn.isOngoing,
- lastActive = lastActive,
- createdTimestampMillis = createdTimestampMillis,
- instanceId = instanceId,
- appUid = appUid,
- isExplicit = isExplicit,
+ val oldResumeAction: Runnable? = mediaEntries[key]?.resumeAction
+ val oldHasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
+ val oldActive = mediaEntries[key]?.active ?: true
+ mediaData =
+ mediaData.copy(
+ resumeAction = oldResumeAction,
+ hasCheckedForResume = oldHasCheckedForResume,
+ active = oldActive
)
- )
+ onMediaDataLoaded(key, oldKey, mediaData)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
index e4047e5..9ee59d1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
@@ -80,6 +80,7 @@
field?.disconnect()
field = value
}
+
private var currentUserId: Int = context.userId
@VisibleForTesting
@@ -89,7 +90,7 @@
if (Intent.ACTION_USER_UNLOCKED == intent.action) {
val userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
if (userId == currentUserId) {
- loadMediaResumptionControls()
+ backgroundExecutor.execute { loadMediaResumptionControls() }
}
}
}
@@ -254,15 +255,15 @@
if (data.resumeAction == null && !data.hasCheckedForResume && isEligibleForResume) {
// TODO also check for a media button receiver intended for restarting (b/154127084)
// Set null action to prevent additional attempts to connect
- mediaDataManager.setResumeAction(key, null)
- Log.d(TAG, "Checking for service component for " + data.packageName)
- val pm = context.packageManager
- val serviceIntent = Intent(MediaBrowserService.SERVICE_INTERFACE)
- val resumeInfo = pm.queryIntentServicesAsUser(serviceIntent, 0, currentUserId)
+ backgroundExecutor.execute {
+ mediaDataManager.setResumeAction(key, null)
+ Log.d(TAG, "Checking for service component for " + data.packageName)
+ val pm = context.packageManager
+ val serviceIntent = Intent(MediaBrowserService.SERVICE_INTERFACE)
+ val resumeInfo = pm.queryIntentServicesAsUser(serviceIntent, 0, currentUserId)
- val inf = resumeInfo?.filter { it.serviceInfo.packageName == data.packageName }
- if (inf != null && inf.size > 0) {
- backgroundExecutor.execute {
+ val inf = resumeInfo?.filter { it.serviceInfo.packageName == data.packageName }
+ if (inf != null && inf.size > 0) {
tryUpdateResumptionList(key, inf!!.get(0).componentInfo.componentName)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index cef1e69..2a9fe83 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -32,6 +32,7 @@
import androidx.core.view.GestureDetectorCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
+import com.android.systemui.Flags
import com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.FalsingManager
@@ -102,9 +103,11 @@
}
_progress.postValue(value)
}
+
private val _progress = MutableLiveData<Progress>().apply { postValue(_data) }
val progress: LiveData<Progress>
get() = _progress
+
private var controller: MediaController? = null
set(value) {
if (field?.sessionToken != value?.sessionToken) {
@@ -113,6 +116,7 @@
field = value
}
}
+
private var playbackState: PlaybackState? = null
private var callback =
object : MediaController.Callback() {
@@ -128,6 +132,15 @@
override fun onSessionDestroyed() {
clearController()
}
+
+ override fun onMetadataChanged(metadata: MediaMetadata?) {
+ if (!Flags.mediaControlsPostsOptimization()) return
+
+ val (enabled, duration) = getEnabledStateAndDuration(metadata)
+ if (_data.duration != duration) {
+ _data = _data.copy(enabled = enabled, duration = duration)
+ }
+ }
}
private var cancel: Runnable? = null
@@ -233,22 +246,13 @@
fun updateController(mediaController: MediaController?) {
controller = mediaController
playbackState = controller?.playbackState
- val mediaMetadata = controller?.metadata
+ val (enabled, duration) = getEnabledStateAndDuration(controller?.metadata)
val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L
val position = playbackState?.position?.toInt()
- val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
val playing =
NotificationMediaManager.isPlayingState(
playbackState?.state ?: PlaybackState.STATE_NONE
)
- val enabled =
- if (
- playbackState == null ||
- playbackState?.getState() == PlaybackState.STATE_NONE ||
- (duration <= 0)
- )
- false
- else true
_data = Progress(enabled, seekAvailable, playing, scrubbing, position, duration, listening)
checkIfPollingNeeded()
}
@@ -368,6 +372,16 @@
}
}
+ /** returns a pair of whether seekbar is enabled and the duration of media. */
+ private fun getEnabledStateAndDuration(metadata: MediaMetadata?): Pair<Boolean, Int> {
+ val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
+ val enabled =
+ !(playbackState == null ||
+ playbackState?.state == PlaybackState.STATE_NONE ||
+ (duration <= 0))
+ return Pair(enabled, duration)
+ }
+
/**
* This method specifies if user made a bad seekbar grab or not. If the vertical distance from
* first touch on seekbar is more than the horizontal distance, this means that the seekbar grab
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 18c6f53..4251b81 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -307,7 +307,7 @@
private void setUpDialog(AlertDialog dialog) {
SystemUIDialog.registerDismissListener(dialog);
- SystemUIDialog.applyFlags(dialog);
+ SystemUIDialog.applyFlags(dialog, /* showWhenLocked= */ false);
SystemUIDialog.setDialogSize(dialog);
dialog.setOnCancelListener(this::onDialogDismissedOrCancelled);
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
index 3a8fe71..ef1f834 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
@@ -19,6 +19,7 @@
import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.QRCodeScannerTile
import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
@@ -51,7 +52,7 @@
@IntoMap
@StringKey(QR_CODE_SCANNER_TILE_SPEC)
fun provideQrCodeScannerAvailabilityInteractor(
- impl: QRCodeScannerTileDataInteractor
+ impl: QRCodeScannerTileDataInteractor
): QSTileAvailabilityInteractor
companion object {
@@ -69,6 +70,7 @@
labelRes = R.string.qr_code_scanner_title,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
)
/** Inject QR Code Scanner Tile into tileViewModelMap in QSModule. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt
index 28c1fbf..2f054b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt
@@ -24,10 +24,10 @@
import com.android.systemui.qs.panels.shared.model.EditTileData
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
@SysUISingleton
@@ -60,6 +60,7 @@
Icon.Loaded(icon, ContentDescription.Loaded(label.toString())),
Text.Loaded(label.toString()),
Text.Loaded(appName.toString()),
+ TileCategory.PROVIDED_BY_APP,
)
} else {
null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
index 3b29422..a2cee3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
@@ -24,6 +24,7 @@
import com.android.systemui.qs.panels.data.repository.StockTilesRepository
import com.android.systemui.qs.panels.domain.model.EditTilesModel
import com.android.systemui.qs.panels.shared.model.EditTileData
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import javax.inject.Inject
@@ -53,6 +54,7 @@
),
Text.Resource(config.uiConfig.labelRes),
null,
+ category = config.category,
)
} else {
EditTileData(
@@ -62,7 +64,8 @@
ContentDescription.Loaded(it.spec)
),
Text.Loaded(it.spec),
- null
+ null,
+ category = TileCategory.UNKNOWN,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt
index 8b70bb9..b153ef7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt
@@ -19,12 +19,14 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
data class EditTileData(
val tileSpec: TileSpec,
val icon: Icon,
val label: Text,
val appName: Text?,
+ val category: TileCategory,
) {
init {
check(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index 24af09d..93037d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -34,6 +34,7 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.LocalOverscrollConfiguration
+import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable
@@ -95,6 +96,8 @@
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.util.fastMap
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
import com.android.compose.modifiers.background
@@ -115,6 +118,7 @@
import com.android.systemui.qs.panels.ui.viewmodel.toUiState
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.groupAndSort
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.res.R
import java.util.function.Supplier
@@ -472,31 +476,39 @@
onClick: (TileSpec) -> Unit,
dragAndDropState: DragAndDropState,
) {
- // Available tiles aren't visible during drag and drop, so the row isn't needed
- val (otherTilesStock, otherTilesCustom) =
- tiles.map { TileGridCell(it, 0) }.partition { it.tile.appName == null }
val availableTileHeight = tileHeight(true)
val availableGridHeight = gridHeight(tiles.size, availableTileHeight, columns, tilePadding)
+ // Available tiles aren't visible during drag and drop, so the row isn't needed
+ val groupedTiles =
+ remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
+ groupAndSort(tiles.fastMap { TileGridCell(it, 0) })
+ }
+ val labelColors = TileDefaults.inactiveTileColors()
// Available tiles
TileLazyGrid(
modifier = Modifier.height(availableGridHeight).testTag(AVAILABLE_TILES_GRID_TEST_TAG),
columns = GridCells.Fixed(columns)
) {
- editTiles(
- otherTilesStock,
- ClickAction.ADD,
- onClick,
- dragAndDropState = dragAndDropState,
- showLabels = true,
- )
- editTiles(
- otherTilesCustom,
- ClickAction.ADD,
- onClick,
- dragAndDropState = dragAndDropState,
- showLabels = true,
- )
+ groupedTiles.forEach { category, tiles ->
+ stickyHeader {
+ Text(
+ text = category.label.load() ?: "",
+ fontSize = 20.sp,
+ color = labelColors.label,
+ modifier =
+ Modifier.background(Color.Black)
+ .padding(start = 16.dp, bottom = 8.dp, top = 8.dp)
+ )
+ }
+ editTiles(
+ tiles,
+ ClickAction.ADD,
+ onClick,
+ dragAndDropState = dragAndDropState,
+ showLabels = true,
+ )
+ }
}
}
@@ -618,7 +630,7 @@
showLabels: Boolean,
modifier: Modifier = Modifier,
) {
- val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec
+ val label = tileViewModel.label.text
val colors = TileDefaults.inactiveTileColors()
TileContainer(
@@ -638,7 +650,7 @@
} else {
LargeTileContent(
label = label,
- secondaryLabel = tileViewModel.appName?.load(),
+ secondaryLabel = tileViewModel.appName?.text,
icon = tileViewModel.icon,
colors = colors,
iconShape = RoundedCornerShape(TileDefaults.InactiveCornerRadius),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
index 8ca8de7..08ee856 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
@@ -21,6 +21,7 @@
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.splitInRowsSequence
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.shared.model.CategoryAndName
/** Represents an item from a grid associated with a row and a span */
interface GridCell {
@@ -38,7 +39,7 @@
override val row: Int,
override val width: Int,
override val span: GridItemSpan = GridItemSpan(width)
-) : GridCell, SizedTile<EditTileViewModel> {
+) : GridCell, SizedTile<EditTileViewModel>, CategoryAndName by tile {
val key: String = "${tile.tileSpec.spec}-$row"
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index 42715be..4a8aa83e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -16,6 +16,9 @@
package com.android.systemui.qs.panels.ui.viewmodel
+import android.content.Context
+import androidx.compose.ui.util.fastMap
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.qs.panels.domain.interactor.EditTilesListInteractor
@@ -27,6 +30,7 @@
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.MinimumTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.CoroutineScope
@@ -35,6 +39,7 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
@@ -49,6 +54,8 @@
private val currentTilesInteractor: CurrentTilesInteractor,
private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
private val minTilesInteractor: MinimumTilesInteractor,
+ private val configurationInteractor: ConfigurationInteractor,
+ @Application private val applicationContext: Context,
@Named("Default") private val defaultGridLayout: GridLayout,
@Application private val applicationScope: CoroutineScope,
gridLayoutTypeInteractor: GridLayoutTypeInteractor,
@@ -99,38 +106,45 @@
.map { it.tileSpec }
.minus(currentTilesInteractor.currentTilesSpecs.toSet())
)
- currentTilesInteractor.currentTiles.map { tiles ->
- val currentSpecs = tiles.map { it.spec }
- val canRemoveTiles = currentSpecs.size > minimumTiles
- val allTiles = editTilesData.stockTiles + editTilesData.customTiles
- val allTilesMap = allTiles.associate { it.tileSpec to it }
- val currentTiles = currentSpecs.map { allTilesMap.get(it) }.filterNotNull()
- val nonCurrentTiles = allTiles.filter { it.tileSpec !in currentSpecs }
+ currentTilesInteractor.currentTiles
+ .map { tiles ->
+ val currentSpecs = tiles.map { it.spec }
+ val canRemoveTiles = currentSpecs.size > minimumTiles
+ val allTiles = editTilesData.stockTiles + editTilesData.customTiles
+ val allTilesMap = allTiles.associate { it.tileSpec to it }
+ val currentTiles = currentSpecs.map { allTilesMap.get(it) }.filterNotNull()
+ val nonCurrentTiles = allTiles.filter { it.tileSpec !in currentSpecs }
- (currentTiles + nonCurrentTiles)
- .filterNot { it.tileSpec in unavailable }
- .map {
- val current = it.tileSpec in currentSpecs
- val availableActions = buildSet {
- if (current) {
- add(AvailableEditActions.MOVE)
- if (canRemoveTiles) {
- add(AvailableEditActions.REMOVE)
+ (currentTiles + nonCurrentTiles)
+ .filterNot { it.tileSpec in unavailable }
+ .map {
+ val current = it.tileSpec in currentSpecs
+ val availableActions = buildSet {
+ if (current) {
+ add(AvailableEditActions.MOVE)
+ if (canRemoveTiles) {
+ add(AvailableEditActions.REMOVE)
+ }
+ } else {
+ add(AvailableEditActions.ADD)
}
- } else {
- add(AvailableEditActions.ADD)
}
+ UnloadedEditTileViewModel(
+ it.tileSpec,
+ it.icon,
+ it.label,
+ it.appName,
+ current,
+ availableActions,
+ it.category,
+ )
}
- EditTileViewModel(
- it.tileSpec,
- it.icon,
- it.label,
- it.appName,
- current,
- availableActions
- )
- }
- }
+ }
+ .combine(configurationInteractor.onAnyConfigurationChange.emitOnStart()) {
+ tiles,
+ _ ->
+ tiles.fastMap { it.load(applicationContext) }
+ }
} else {
emptyFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
index a4c8638..ee12736f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
@@ -16,9 +16,15 @@
package com.android.systemui.qs.panels.ui.viewmodel
+import android.content.Context
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.text.AnnotatedString
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.ui.compose.toAnnotatedString
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.CategoryAndName
+import com.android.systemui.qs.shared.model.TileCategory
/**
* View model for each tile that is available to be added/removed/moved in Edit mode.
@@ -26,14 +32,41 @@
* [isCurrent] indicates whether this tile is part of the current set of tiles that the user sees in
* Quick Settings.
*/
-data class EditTileViewModel(
+data class UnloadedEditTileViewModel(
val tileSpec: TileSpec,
val icon: Icon,
val label: Text,
val appName: Text?,
val isCurrent: Boolean,
val availableEditActions: Set<AvailableEditActions>,
-)
+ val category: TileCategory,
+) {
+ fun load(context: Context): EditTileViewModel {
+ return EditTileViewModel(
+ tileSpec,
+ icon,
+ label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec),
+ appName?.toAnnotatedString(context),
+ isCurrent,
+ availableEditActions,
+ category,
+ )
+ }
+}
+
+@Immutable
+data class EditTileViewModel(
+ val tileSpec: TileSpec,
+ val icon: Icon,
+ val label: AnnotatedString,
+ val appName: AnnotatedString?,
+ val isCurrent: Boolean,
+ val availableEditActions: Set<AvailableEditActions>,
+ override val category: TileCategory,
+) : CategoryAndName {
+ override val name
+ get() = label.text
+}
enum class AvailableEditActions {
ADD,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt b/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt
new file mode 100644
index 0000000..59cb7d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.shared.model
+
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.res.R
+
+/** Categories for tiles. This can be used to sort tiles in edit mode. */
+enum class TileCategory(val label: Text) {
+ CONNECTIVITY(Text.Resource(R.string.qs_edit_mode_category_connectivity)),
+ UTILITIES(Text.Resource(R.string.qs_edit_mode_category_utilities)),
+ DISPLAY(Text.Resource(R.string.qs_edit_mode_category_display)),
+ PRIVACY(Text.Resource(R.string.qs_edit_mode_category_privacy)),
+ ACCESSIBILITY(Text.Resource(R.string.qs_edit_mode_category_accessibility)),
+ PROVIDED_BY_APP(Text.Resource(R.string.qs_edit_mode_category_providedByApps)),
+ UNKNOWN(Text.Resource(R.string.qs_edit_mode_category_unknown)),
+}
+
+interface CategoryAndName {
+ val category: TileCategory
+ val name: String
+}
+
+/**
+ * Groups the elements of the list by [CategoryAndName.category] (with the keys sorted in the
+ * natural order of [TileCategory]), and sorts the elements of each group based on the
+ * [CategoryAndName.name].
+ */
+fun <T : CategoryAndName> groupAndSort(list: List<T>): Map<TileCategory, List<T>> {
+ val groupedByCategory = list.groupBy { it.category }.toSortedMap()
+ return groupedByCategory.mapValues { it.value.sortedBy { it.name } }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
index cdcefdb..3a9cb17 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
@@ -21,6 +21,7 @@
import androidx.annotation.StringRes
import com.android.internal.logging.InstanceId
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
data class QSTileConfig
@JvmOverloads
@@ -28,6 +29,7 @@
val tileSpec: TileSpec,
val uiConfig: QSTileUIConfig,
val instanceId: InstanceId,
+ val category: TileCategory,
val metricsSpec: String = tileSpec.spec,
val policy: QSTilePolicy = QSTilePolicy.NoRestrictions,
val autoRemoveOnUnavailable: Boolean = true,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
index 0609e79..4dbddd9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import javax.inject.Inject
interface QSTileConfigProvider {
@@ -73,6 +74,7 @@
spec,
QSTileUIConfig.Empty,
qsEventLogger.getNewInstanceId(),
+ category = TileCategory.PROVIDED_BY_APP,
)
is TileSpec.Invalid ->
throw IllegalArgumentException("TileSpec.Invalid doesn't support configs")
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
index 907b92c..c092c2f 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
@@ -19,6 +19,7 @@
import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.RecordIssueTile
import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
@@ -61,6 +62,7 @@
labelRes = R.string.qs_record_issue_label
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
)
/** Inject FlashlightTile into tileViewModelMap in QSModule */
diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
index 0589e6c6..c9712fc 100644
--- a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
@@ -19,6 +19,7 @@
import com.android.systemui.camera.CameraRotationModule
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileDataInteractor
@@ -42,8 +43,9 @@
@IntoMap
@StringKey(ROTATION_TILE_SPEC)
fun provideRotationAvailabilityInteractor(
- impl: RotationLockTileDataInteractor
+ impl: RotationLockTileDataInteractor
): QSTileAvailabilityInteractor
+
companion object {
private const val ROTATION_TILE_SPEC = "rotation"
@@ -60,6 +62,7 @@
labelRes = R.string.quick_settings_rotation_unlocked_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
/** Inject Rotation tile into tileViewModelMap in QSModule */
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
index 738b184..e477efe 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
@@ -25,6 +25,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
@@ -34,6 +35,7 @@
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
@@ -45,6 +47,7 @@
import kotlinx.coroutines.launch
/** Business logic about the visibility of various parts of the window root view. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class WindowRootViewVisibilityInteractor
@Inject
@@ -80,9 +83,9 @@
is ObservableTransitionState.Idle ->
flowOf(
state.currentScene == Scenes.Shade ||
- state.currentScene == Scenes.NotificationsShade ||
- state.currentScene == Scenes.QuickSettingsShade ||
- state.currentScene == Scenes.Lockscreen
+ state.currentScene == Scenes.Lockscreen ||
+ Overlays.NotificationsShade in state.currentOverlays ||
+ Overlays.QuickSettingsShade in state.currentOverlays
)
is ObservableTransitionState.Transition ->
if (
@@ -94,12 +97,12 @@
} else {
flowOf(
state.toContent == Scenes.Shade ||
- state.toContent == Scenes.NotificationsShade ||
- state.toContent == Scenes.QuickSettingsShade ||
+ state.toContent == Overlays.NotificationsShade ||
+ state.toContent == Overlays.QuickSettingsShade ||
state.toContent == Scenes.Lockscreen ||
state.fromContent == Scenes.Shade ||
- state.fromContent == Scenes.NotificationsShade ||
- state.fromContent == Scenes.QuickSettingsShade ||
+ state.fromContent == Overlays.NotificationsShade ||
+ state.fromContent == Overlays.QuickSettingsShade ||
state.fromContent == Scenes.Lockscreen
)
}
@@ -115,10 +118,9 @@
* false if the device is asleep.
*/
val isLockscreenOrShadeVisibleAndInteractive: StateFlow<Boolean> =
- combine(
- isLockscreenOrShadeVisible,
- powerInteractor.isAwake,
- ) { isKeyguardAodOrShadeVisible, isAwake ->
+ combine(isLockscreenOrShadeVisible, powerInteractor.isAwake) {
+ isKeyguardAodOrShadeVisible,
+ isAwake ->
isKeyguardAodOrShadeVisible && isAwake
}
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
index d1629c7..e352bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
@@ -19,7 +19,9 @@
package com.android.systemui.scene.domain.startable
import androidx.annotation.VisibleForTesting
+import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.CoreStartable
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -33,6 +35,7 @@
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
import com.android.systemui.statusbar.phone.DozeServiceHost
@@ -74,6 +77,7 @@
deviceEntryInteractor.isDeviceEntered,
occlusionInteractor.invisibleDueToOcclusion,
sceneInteractor.currentScene,
+ sceneInteractor.currentOverlays,
sceneInteractor.transitionState,
keyguardInteractor.isDozing,
keyguardInteractor.isDreaming,
@@ -95,34 +99,29 @@
val isDeviceEntered = flowValues[0] as Boolean
val isOccluded = flowValues[1] as Boolean
val currentScene = flowValues[2] as SceneKey
- val transitionState = flowValues[3] as ObservableTransitionState
- val isDozing = flowValues[4] as Boolean
- val isDreaming = flowValues[5] as Boolean
- val biometricUnlockState = flowValues[6] as BiometricUnlockModel
- val isBrightnessMirrorVisible = flowValues[7] as Boolean
- val isPulsing = flowValues[8] as Boolean
- val hasPendingScreenOffCallback = flowValues[9] as Boolean
+ val currentOverlays = flowValues[3] as Set<OverlayKey>
+ val transitionState = flowValues[4] as ObservableTransitionState
+ val isDozing = flowValues[5] as Boolean
+ val isDreaming = flowValues[6] as Boolean
+ val biometricUnlockState = flowValues[7] as BiometricUnlockModel
+ val isBrightnessMirrorVisible = flowValues[8] as Boolean
+ val isPulsing = flowValues[9] as Boolean
+ val hasPendingScreenOffCallback = flowValues[10] as Boolean
// This is true when the lockscreen scene is either the current scene or somewhere
- // in the
- // navigation back stack of scenes.
+ // in the navigation back stack of scenes.
val isOnKeyguard = !isDeviceEntered
val isCurrentSceneBouncer = currentScene == Scenes.Bouncer
// This is true when moving away from one of the keyguard scenes to the gone scene.
- // It
- // happens only when unlocking or when dismissing a dismissible lockscreen.
+ // It happens only when unlocking or when dismissing a dismissible lockscreen.
val isTransitioningAwayFromKeyguard =
transitionState is ObservableTransitionState.Transition.ChangeScene &&
transitionState.fromScene.isKeyguard() &&
transitionState.toScene == Scenes.Gone
- // This is true when any of the shade scenes is the current scene.
- val isCurrentSceneShade = currentScene.isShade()
- // This is true when moving into one of the shade scenes when a non-shade scene.
- val isTransitioningToShade =
- transitionState is ObservableTransitionState.Transition.ChangeScene &&
- !transitionState.fromScene.isShade() &&
- transitionState.toScene.isShade()
+ // This is true when any of the shade scenes or overlays is the current content.
+ val isCurrentContentShade =
+ currentScene.isShade() || currentOverlays.any { it.isShade() }
// This is true after completing a transition to communal.
val isIdleOnCommunal = transitionState.isIdle(Scenes.Communal)
@@ -137,10 +136,10 @@
if (alternateBouncerInteractor.isVisibleState()) {
// This will cancel the keyguardFadingAway animation if it is running. We need
- // to do
- // this as otherwise it can remain pending and leave keyguard in a weird state.
+ // to do this as otherwise it can remain pending and leave keyguard in a weird
+ // state.
onKeyguardFadedAway(isTransitioningAwayFromKeyguard)
- if (!isTransitioningToShade) {
+ if (!transitionState.isTransitioningToShade()) {
// Safeguard which prevents the scrim from being stuck in the wrong
// state
Model(scrimState = ScrimState.KEYGUARD, unlocking = unlocking)
@@ -163,7 +162,7 @@
)
} else if (isBrightnessMirrorVisible) {
Model(scrimState = ScrimState.BRIGHTNESS_MIRROR, unlocking = unlocking)
- } else if (isCurrentSceneShade && !isDeviceEntered) {
+ } else if (isCurrentContentShade && !isDeviceEntered) {
Model(scrimState = ScrimState.SHADE_LOCKED, unlocking = unlocking)
} else if (isPulsing) {
Model(scrimState = ScrimState.PULSING, unlocking = unlocking)
@@ -171,8 +170,8 @@
Model(scrimState = ScrimState.OFF, unlocking = unlocking)
} else if (isDozing && !unlocking) {
// This will cancel the keyguardFadingAway animation if it is running. We need
- // to do
- // this as otherwise it can remain pending and leave keyguard in a weird state.
+ // to do this as otherwise it can remain pending and leave keyguard in a weird
+ // state.
onKeyguardFadedAway(isTransitioningAwayFromKeyguard)
Model(scrimState = ScrimState.AOD, unlocking = false)
} else if (isIdleOnCommunal) {
@@ -222,15 +221,24 @@
return this == Scenes.Lockscreen || this == Scenes.Bouncer
}
- private fun SceneKey.isShade(): Boolean {
+ private fun ContentKey.isShade(): Boolean {
return this == Scenes.Shade ||
this == Scenes.QuickSettings ||
- this == Scenes.NotificationsShade ||
- this == Scenes.QuickSettingsShade
+ this == Overlays.NotificationsShade ||
+ this == Overlays.QuickSettingsShade
}
- private data class Model(
- val scrimState: ScrimState,
- val unlocking: Boolean,
- )
+ private fun ObservableTransitionState.isTransitioningToShade(): Boolean {
+ return when (this) {
+ is ObservableTransitionState.Idle -> false
+ is ObservableTransitionState.Transition.ChangeScene ->
+ !fromScene.isShade() && toScene.isShade()
+ is ObservableTransitionState.Transition.ReplaceOverlay ->
+ !fromOverlay.isShade() && toOverlay.isShade()
+ is ObservableTransitionState.Transition.ShowOrHideOverlay ->
+ !fromContent.isShade() && toContent.isShade()
+ }
+ }
+
+ private data class Model(val scrimState: ScrimState, val unlocking: Boolean)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
index a830e1b..9a9c576 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
@@ -21,6 +21,7 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.ScreenRecordTile
import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
@@ -74,6 +75,7 @@
labelRes = R.string.quick_settings_screen_record_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
/** Inject ScreenRecord Tile into tileViewModelMap in QSModule */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f88fd7d..862f33bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -63,6 +63,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.annotations.KeepForWeakReference;
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IStatusBar;
@@ -192,10 +193,10 @@
private static final String SHOW_IME_SWITCHER_KEY = "showImeSwitcherKey";
private final Object mLock = new Object();
- private ArrayList<Callbacks> mCallbacks = new ArrayList<>();
- private Handler mHandler = new H(Looper.getMainLooper());
+ private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
+ private final Handler mHandler = new H(Looper.getMainLooper());
/** A map of display id - disable flag pair */
- private SparseArray<Pair<Integer, Integer>> mDisplayDisabled = new SparseArray<>();
+ private final SparseArray<Pair<Integer, Integer>> mDisplayDisabled = new SparseArray<>();
/**
* The last ID of the display where IME window for which we received setImeWindowStatus
* event.
@@ -207,6 +208,21 @@
private final @Nullable DumpHandler mDumpHandler;
private final @Nullable Lazy<PowerInteractor> mPowerInteractor;
+ @KeepForWeakReference
+ private final DisplayTracker.Callback mDisplayTrackerCallback = new DisplayTracker.Callback() {
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ mDisplayDisabled.remove(displayId);
+ }
+ // This callback is registered with {@link #mHandler} that already posts to run on
+ // main thread, so it is safe to dispatch directly.
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ mCallbacks.get(i).onDisplayRemoved(displayId);
+ }
+ }
+ };
+
/**
* These methods are called back on the main thread.
*/
@@ -576,19 +592,8 @@
mDisplayTracker = displayTracker;
mRegistry = registry;
mDumpHandler = dumpHandler;
- mDisplayTracker.addDisplayChangeCallback(new DisplayTracker.Callback() {
- @Override
- public void onDisplayRemoved(int displayId) {
- synchronized (mLock) {
- mDisplayDisabled.remove(displayId);
- }
- // This callback is registered with {@link #mHandler} that already posts to run on
- // main thread, so it is safe to dispatch directly.
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).onDisplayRemoved(displayId);
- }
- }
- }, new HandlerExecutor(mHandler));
+ mDisplayTracker.addDisplayChangeCallback(mDisplayTrackerCallback,
+ new HandlerExecutor(mHandler));
// We always have default display.
setDisabled(mDisplayTracker.getDefaultDisplayId(), DISABLE_NONE, DISABLE2_NONE);
mPowerInteractor = powerInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index 400f8af..dac0102 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -21,6 +21,7 @@
import com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.AirplaneModeTile
import com.android.systemui.qs.tiles.BluetoothTile
@@ -95,21 +96,21 @@
@IntoMap
@StringKey(AIRPLANE_MODE_TILE_SPEC)
fun provideAirplaneModeAvailabilityInteractor(
- impl: AirplaneModeTileDataInteractor
+ impl: AirplaneModeTileDataInteractor
): QSTileAvailabilityInteractor
@Binds
@IntoMap
@StringKey(DATA_SAVER_TILE_SPEC)
fun provideDataSaverAvailabilityInteractor(
- impl: DataSaverTileDataInteractor
+ impl: DataSaverTileDataInteractor
): QSTileAvailabilityInteractor
@Binds
@IntoMap
@StringKey(INTERNET_TILE_SPEC)
fun provideInternetAvailabilityInteractor(
- impl: InternetTileDataInteractor
+ impl: InternetTileDataInteractor
): QSTileAvailabilityInteractor
companion object {
@@ -149,6 +150,7 @@
),
instanceId = uiEventLogger.getNewInstanceId(),
policy = QSTilePolicy.Restricted(listOf(UserManager.DISALLOW_AIRPLANE_MODE)),
+ category = TileCategory.CONNECTIVITY,
)
/** Inject AirplaneModeTile into tileViewModelMap in QSModule */
@@ -180,6 +182,7 @@
labelRes = R.string.data_saver,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
/** Inject DataSaverTile into tileViewModelMap in QSModule */
@@ -211,6 +214,7 @@
labelRes = R.string.quick_settings_internet_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
/** Inject InternetTile into tileViewModelMap in QSModule */
@@ -242,6 +246,7 @@
labelRes = R.string.quick_settings_hotspot_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
@Provides
@@ -256,6 +261,7 @@
labelRes = R.string.quick_settings_cast_title,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
@Provides
@@ -270,6 +276,7 @@
labelRes = R.string.quick_settings_bluetooth_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index c046168..0ad1042a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -480,10 +480,16 @@
}
public static AlertDialog applyFlags(AlertDialog dialog) {
+ return applyFlags(dialog, true);
+ }
+
+ public static AlertDialog applyFlags(AlertDialog dialog, boolean showWhenLocked) {
final Window window = dialog.getWindow();
window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
- window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ if (showWhenLocked) {
+ window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ }
window.getAttributes().setFitInsetsTypes(
window.getAttributes().getFitInsetsTypes() & ~Type.statusBars());
return dialog;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index fc7a672..bc7d376 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -64,9 +64,6 @@
const val COL_NAME_IS_ENABLED = "isEnabled"
/** Column name to use for [isWifiDefault] for table logging. */
const val COL_NAME_IS_DEFAULT = "isDefault"
-
- const val CARRIER_MERGED_INVALID_SUB_ID_REASON =
- "Wifi network was carrier merged but had invalid sub ID"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index 7163e67..f4bb1a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -46,7 +46,7 @@
private val _isWifiDefault = MutableStateFlow(false)
override val isWifiDefault: StateFlow<Boolean> = _isWifiDefault
- private val _wifiNetwork = MutableStateFlow<WifiNetworkModel>(WifiNetworkModel.Inactive)
+ private val _wifiNetwork = MutableStateFlow<WifiNetworkModel>(WifiNetworkModel.Inactive())
override val wifiNetwork: StateFlow<WifiNetworkModel> = _wifiNetwork
private val _secondaryNetworks = MutableStateFlow<List<WifiNetworkModel>>(emptyList())
@@ -82,7 +82,7 @@
_isWifiEnabled.value = false
_isWifiDefault.value = false
_wifiActivity.value = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
- _wifiNetwork.value = WifiNetworkModel.Inactive
+ _wifiNetwork.value = WifiNetworkModel.Inactive()
}
private fun processEnabledWifiState(event: FakeWifiEventModel.Wifi) {
@@ -100,7 +100,7 @@
}
private fun FakeWifiEventModel.Wifi.toWifiNetworkModel(): WifiNetworkModel =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
isValidated = validated ?: true,
level = level ?: 0,
ssid = ssid ?: DEMO_NET_SSID,
@@ -108,7 +108,7 @@
)
private fun FakeWifiEventModel.CarrierMerged.toCarrierMergedModel(): WifiNetworkModel =
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = subscriptionId,
level = level,
numberOfLevels = numberOfLevels,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index b6e73e0..76024cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -19,7 +19,6 @@
import android.annotation.SuppressLint
import android.net.wifi.ScanResult
import android.net.wifi.WifiManager
-import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
@@ -39,18 +38,14 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository.Companion.CARRIER_MERGED_INVALID_SUB_ID_REASON
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository.Companion.COL_NAME_IS_DEFAULT
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository.Companion.COL_NAME_IS_ENABLED
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Inactive.toHotspotDeviceType
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Unavailable.toHotspotDeviceType
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.wifitrackerlib.HotspotNetworkEntry
import com.android.wifitrackerlib.MergedCarrierEntry
import com.android.wifitrackerlib.WifiEntry
-import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX
-import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN
-import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE
import com.android.wifitrackerlib.WifiPickerTracker
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -246,36 +241,28 @@
}
private fun MergedCarrierEntry.convertCarrierMergedToModel(): WifiNetworkModel {
- return if (this.subscriptionId == INVALID_SUBSCRIPTION_ID) {
- WifiNetworkModel.Invalid(CARRIER_MERGED_INVALID_SUB_ID_REASON)
- } else {
- WifiNetworkModel.CarrierMerged(
- subscriptionId = this.subscriptionId,
- level = this.level,
- // WifiManager APIs to calculate the signal level start from 0, so
- // maxSignalLevel + 1 represents the total level buckets count.
- numberOfLevels = wifiManager.maxSignalLevel + 1,
- )
- }
+ // WifiEntry instance values aren't guaranteed to be stable between method calls
+ // because
+ // WifiPickerTracker is continuously updating the same object. Save the level in a
+ // local
+ // variable so that checking the level validity here guarantees that the level will
+ // still be
+ // valid when we create the `WifiNetworkModel.Active` instance later. Otherwise, the
+ // level
+ // could be valid here but become invalid later, and `WifiNetworkModel.Active` will
+ // throw
+ // an exception. See b/362384551.
+
+ return WifiNetworkModel.CarrierMerged.of(
+ subscriptionId = this.subscriptionId,
+ level = this.level,
+ // WifiManager APIs to calculate the signal level start from 0, so
+ // maxSignalLevel + 1 represents the total level buckets count.
+ numberOfLevels = wifiManager.maxSignalLevel + 1,
+ )
}
private fun WifiEntry.convertNormalToModel(): WifiNetworkModel {
- // WifiEntry instance values aren't guaranteed to be stable between method calls because
- // WifiPickerTracker is continuously updating the same object. Save the level in a local
- // variable so that checking the level validity here guarantees that the level will still be
- // valid when we create the `WifiNetworkModel.Active` instance later. Otherwise, the level
- // could be valid here but become invalid later, and `WifiNetworkModel.Active` will throw
- // an exception. See b/362384551.
- val currentLevel = this.level
- if (
- currentLevel == WIFI_LEVEL_UNREACHABLE ||
- currentLevel !in WIFI_LEVEL_MIN..WIFI_LEVEL_MAX
- ) {
- // If our level means the network is unreachable or the level is otherwise invalid, we
- // don't have an active network.
- return WifiNetworkModel.Inactive
- }
-
val hotspotDeviceType =
if (this is HotspotNetworkEntry) {
this.deviceType.toHotspotDeviceType()
@@ -283,9 +270,9 @@
WifiNetworkModel.HotspotDeviceType.NONE
}
- return WifiNetworkModel.Active(
+ return WifiNetworkModel.Active.of(
isValidated = this.hasInternetAccess(),
- level = currentLevel,
+ level = this.level,
ssid = this.title,
hotspotDeviceType = hotspotDeviceType,
)
@@ -421,7 +408,7 @@
companion object {
// Start out with no known wifi network.
- @VisibleForTesting val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
+ @VisibleForTesting val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive()
private const val WIFI_STATE_DEFAULT = WifiManager.WIFI_STATE_DISABLED
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
index 39842fb..3220377 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.wifi.shared.model
+import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.UNKNOWN_SSID
import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo
import android.telephony.SubscriptionManager
@@ -23,8 +24,12 @@
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Active.Companion.isValid
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Active.Companion.of
import com.android.wifitrackerlib.HotspotNetworkEntry.DeviceType
import com.android.wifitrackerlib.WifiEntry
+import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE
/** Provides information about the current wifi network. */
sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
@@ -64,7 +69,7 @@
/** A description of why the wifi information was invalid. */
val invalidReason: String,
) : WifiNetworkModel() {
- override fun toString() = "WifiNetwork.Invalid[$invalidReason]"
+ override fun toString() = "WifiNetwork.Invalid[reason=$invalidReason]"
override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
if (prevVal !is Invalid) {
@@ -73,12 +78,12 @@
}
if (invalidReason != prevVal.invalidReason) {
- row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason")
+ row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE[reason=$invalidReason]")
}
}
override fun logFull(row: TableRowLogger) {
- row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason")
+ row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE[reason=$invalidReason]")
row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
row.logChange(COL_VALIDATED, false)
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
@@ -89,20 +94,25 @@
}
/** A model representing that we have no active wifi network. */
- object Inactive : WifiNetworkModel() {
- override fun toString() = "WifiNetwork.Inactive"
+ data class Inactive(
+ /** An optional description of why the wifi information was inactive. */
+ val inactiveReason: String? = null,
+ ) : WifiNetworkModel() {
+ override fun toString() = "WifiNetwork.Inactive[reason=$inactiveReason]"
override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
- if (prevVal is Inactive) {
+ if (prevVal !is Inactive) {
+ logFull(row)
return
}
- // When changing to Inactive, we need to log diffs to all the fields.
- logFull(row)
+ if (inactiveReason != prevVal.inactiveReason) {
+ row.logChange(COL_NETWORK_TYPE, "$TYPE_INACTIVE[reason=$inactiveReason]")
+ }
}
override fun logFull(row: TableRowLogger) {
- row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
+ row.logChange(COL_NETWORK_TYPE, "$TYPE_INACTIVE[reason=$inactiveReason]")
row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
row.logChange(COL_VALIDATED, false)
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
@@ -117,31 +127,71 @@
* treated as more of a mobile network.
*
* See [android.net.wifi.WifiInfo.isCarrierMerged] for more information.
+ *
+ * IMPORTANT: Do *not* call [copy] on this class. Instead, use the factory [of] methods. [of]
+ * will verify preconditions correctly.
*/
- data class CarrierMerged(
+ data class CarrierMerged
+ private constructor(
/**
* The subscription ID that this connection represents.
*
* Comes from [android.net.wifi.WifiInfo.getSubscriptionId].
*
- * Per that method, this value must not be [INVALID_SUBSCRIPTION_ID] (if it was invalid,
- * then this is *not* a carrier merged network).
+ * Per that method, this value must not be [SubscriptionManager.INVALID_SUBSCRIPTION_ID] (if
+ * it was invalid, then this is *not* a carrier merged network).
*/
val subscriptionId: Int,
- /** The signal level, guaranteed to be 0 <= level <= numberOfLevels. */
+ /** The signal level, required to be 0 <= level <= numberOfLevels. */
val level: Int,
/** The maximum possible level. */
- val numberOfLevels: Int = MobileConnectionRepository.DEFAULT_NUM_LEVELS,
+ val numberOfLevels: Int,
) : WifiNetworkModel() {
- init {
- require(level in MIN_VALID_LEVEL..numberOfLevels) {
- "CarrierMerged: $MIN_VALID_LEVEL <= wifi level <= $numberOfLevels required; " +
+ companion object {
+ /**
+ * Creates a [CarrierMerged] instance, or an [Invalid] instance if any of the arguments
+ * are invalid.
+ */
+ fun of(
+ subscriptionId: Int,
+ level: Int,
+ numberOfLevels: Int = MobileConnectionRepository.DEFAULT_NUM_LEVELS
+ ): WifiNetworkModel {
+ if (!subscriptionId.isSubscriptionIdValid()) {
+ return Invalid(INVALID_SUB_ID_ERROR_STRING)
+ }
+ if (!level.isLevelValid(numberOfLevels)) {
+ return Invalid(getInvalidLevelErrorString(level, numberOfLevels))
+ }
+ return CarrierMerged(subscriptionId, level, numberOfLevels)
+ }
+
+ private fun Int.isLevelValid(maxLevel: Int): Boolean {
+ return this != WIFI_LEVEL_UNREACHABLE && this in MIN_VALID_LEVEL..maxLevel
+ }
+
+ private fun getInvalidLevelErrorString(level: Int, maxLevel: Int): String {
+ return "Wifi network was carrier merged but had invalid level. " +
+ "$MIN_VALID_LEVEL <= wifi level <= $maxLevel required; " +
"level was $level"
}
- require(subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- "subscription ID cannot be invalid"
+
+ private fun Int.isSubscriptionIdValid(): Boolean {
+ return this != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ }
+
+ private const val INVALID_SUB_ID_ERROR_STRING =
+ "Wifi network was carrier merged but had invalid sub ID"
+ }
+
+ init {
+ require(level.isLevelValid(numberOfLevels)) {
+ "${getInvalidLevelErrorString(level, numberOfLevels)}. $DO_NOT_USE_COPY_ERROR"
+ }
+ require(subscriptionId.isSubscriptionIdValid()) {
+ "$INVALID_SUB_ID_ERROR_STRING. $DO_NOT_USE_COPY_ERROR"
}
}
@@ -173,28 +223,64 @@
}
}
- /** Provides information about an active wifi network. */
- data class Active(
+ /**
+ * Provides information about an active wifi network.
+ *
+ * IMPORTANT: Do *not* call [copy] on this class. Instead, use the factory [of] method. [of]
+ * will verify preconditions correctly.
+ */
+ data class Active
+ private constructor(
/** See [android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED]. */
- val isValidated: Boolean = false,
+ val isValidated: Boolean,
- /** The wifi signal level, guaranteed to be 0 <= level <= 4. */
+ /** The wifi signal level, required to be 0 <= level <= 4. */
val level: Int,
/** See [android.net.wifi.WifiInfo.ssid]. */
- val ssid: String? = null,
+ val ssid: String?,
/**
* The type of device providing a hotspot connection, or [HotspotDeviceType.NONE] if this
* isn't a hotspot connection.
*/
- val hotspotDeviceType: HotspotDeviceType = WifiNetworkModel.HotspotDeviceType.NONE,
+ val hotspotDeviceType: HotspotDeviceType,
) : WifiNetworkModel() {
- init {
- require(level in MIN_VALID_LEVEL..MAX_VALID_LEVEL) {
- "Active: $MIN_VALID_LEVEL <= wifi level <= $MAX_VALID_LEVEL required; " +
+ companion object {
+ /**
+ * Creates an [Active] instance, or an [Inactive] instance if any of the arguments are
+ * invalid.
+ */
+ @JvmStatic
+ fun of(
+ isValidated: Boolean = false,
+ level: Int,
+ ssid: String? = null,
+ hotspotDeviceType: HotspotDeviceType = HotspotDeviceType.NONE,
+ ): WifiNetworkModel {
+ if (!level.isValid()) {
+ return Inactive(getInvalidLevelErrorString(level))
+ }
+ return Active(isValidated, level, ssid, hotspotDeviceType)
+ }
+
+ private fun Int.isValid(): Boolean {
+ return this != WIFI_LEVEL_UNREACHABLE && this in MIN_VALID_LEVEL..MAX_VALID_LEVEL
+ }
+
+ private fun getInvalidLevelErrorString(level: Int): String {
+ return "Wifi network was active but had invalid level. " +
+ "$MIN_VALID_LEVEL <= wifi level <= $MAX_VALID_LEVEL required; " +
"level was $level"
}
+
+ @VisibleForTesting internal const val MAX_VALID_LEVEL = WifiEntry.WIFI_LEVEL_MAX
+ }
+
+ init {
+ require(level.isValid()) {
+ "${getInvalidLevelErrorString(level)}. $DO_NOT_USE_COPY_ERROR"
+ }
}
/** Returns true if this network has a valid SSID and false otherwise. */
@@ -231,10 +317,6 @@
row.logChange(COL_SSID, ssid)
row.logChange(COL_HOTSPOT, hotspotDeviceType.name)
}
-
- companion object {
- @VisibleForTesting internal const val MAX_VALID_LEVEL = WifiEntry.WIFI_LEVEL_MAX
- }
}
companion object {
@@ -292,3 +374,7 @@
val LEVEL_DEFAULT: String? = null
val NUM_LEVELS_DEFAULT: String? = null
val SUB_ID_DEFAULT: String? = null
+
+private const val DO_NOT_USE_COPY_ERROR =
+ "This should only be an issue if the caller incorrectly used `copy` to get a new instance. " +
+ "Please use the `of` method instead."
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
index 591d7af..cf9f9f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -23,6 +23,7 @@
import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.AlarmTile
import com.android.systemui.qs.tiles.CameraToggleTile
@@ -157,6 +158,7 @@
labelRes = R.string.quick_settings_flashlight_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
)
/** Inject FlashlightTile into tileViewModelMap in QSModule */
@@ -192,7 +194,8 @@
policy =
QSTilePolicy.Restricted(
listOf(DISALLOW_SHARE_LOCATION, DISALLOW_CONFIG_LOCATION)
- )
+ ),
+ category = TileCategory.PRIVACY,
)
/** Inject LocationTile into tileViewModelMap in QSModule */
@@ -225,6 +228,7 @@
labelRes = R.string.status_bar_alarm,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.UTILITIES,
)
/** Inject AlarmTile into tileViewModelMap in QSModule */
@@ -257,6 +261,7 @@
labelRes = R.string.quick_settings_ui_mode_night_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.DISPLAY,
)
/** Inject uimodenight into tileViewModelMap in QSModule */
@@ -290,6 +295,7 @@
),
instanceId = uiEventLogger.getNewInstanceId(),
autoRemoveOnUnavailable = false,
+ category = TileCategory.PRIVACY,
)
/** Inject work mode into tileViewModelMap in QSModule */
@@ -323,6 +329,7 @@
),
instanceId = uiEventLogger.getNewInstanceId(),
policy = QSTilePolicy.Restricted(listOf(DISALLOW_CAMERA_TOGGLE)),
+ category = TileCategory.PRIVACY,
)
/** Inject camera toggle tile into tileViewModelMap in QSModule */
@@ -365,6 +372,7 @@
),
instanceId = uiEventLogger.getNewInstanceId(),
policy = QSTilePolicy.Restricted(listOf(DISALLOW_MICROPHONE_TOGGLE)),
+ category = TileCategory.PRIVACY,
)
/** Inject microphone toggle tile into tileViewModelMap in QSModule */
@@ -407,6 +415,7 @@
labelRes = R.string.quick_settings_modes_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
} else {
QSTileConfig(
@@ -417,6 +426,7 @@
labelRes = R.string.quick_settings_dnd_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.CONNECTIVITY,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
index ea213cb..dd1c11d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
@@ -25,6 +25,7 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.pipeline.shared.TileSpec;
+import com.android.systemui.qs.shared.model.TileCategory;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.QuickAccessWalletTile;
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig;
@@ -34,8 +35,6 @@
import com.android.systemui.wallet.controller.WalletContextualLocationsService;
import com.android.systemui.wallet.ui.WalletActivity;
-import java.util.concurrent.Executor;
-
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -43,6 +42,8 @@
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;
+import java.util.concurrent.Executor;
+
/**
* Module for injecting classes in Wallet.
*/
@@ -90,6 +91,7 @@
R.string.wallet_title
),
uiEventLogger.getNewInstanceId(),
+ TileCategory.UTILITIES,
tileSpec.getSpec(),
QSTilePolicy.NoRestrictions.INSTANCE
);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
index d85b774..bf13ceb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
@@ -445,7 +445,7 @@
assertFalse(mWifiRepository.isWifiConnectedWithValidSsid());
mWifiRepository.setWifiNetwork(
- new WifiNetworkModel.Active(
+ WifiNetworkModel.Active.Companion.of(
/* isValidated= */ false,
/* level= */ 0,
/* ssid= */ "",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartableTest.kt
new file mode 100644
index 0000000..9da6885
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartableTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial
+
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
+import com.android.systemui.testKosmos
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyboardTouchpadTutorialCoreStartableTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val broadcastDispatcher = kosmos.broadcastDispatcher
+ private val context = mock<Context>()
+ private val underTest =
+ KeyboardTouchpadTutorialCoreStartable(
+ { mock<TutorialNotificationCoordinator>() },
+ broadcastDispatcher,
+ context
+ )
+
+ @Test
+ fun registersBroadcastReceiverStartingActivityAsSystemUser() {
+ underTest.start()
+
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent("com.android.systemui.action.KEYBOARD_TOUCHPAD_TUTORIAL")
+ )
+
+ verify(context).startActivityAsUser(any(), eq(UserHandle.SYSTEM))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index 3c74374..823a23d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -38,6 +38,8 @@
import android.media.session.PlaybackState
import android.net.Uri
import android.os.Bundle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.service.notification.StatusBarNotification
@@ -61,6 +63,8 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.mediaLogger
+import com.android.systemui.media.controls.shared.mockMediaLogger
import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
import com.android.systemui.media.controls.shared.model.MediaData
@@ -69,7 +73,6 @@
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.controls.util.fakeMediaControllerFactory
import com.android.systemui.media.controls.util.mediaFlags
-import com.android.systemui.plugins.activityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.testKosmos
@@ -186,11 +189,10 @@
mSetFlagsRule.setFlagsParameterization(flags)
}
- private val kosmos = testKosmos()
+ private val kosmos = testKosmos().apply { mediaLogger = mockMediaLogger }
private val testDispatcher = kosmos.testDispatcher
private val testScope = kosmos.testScope
private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
- private val activityStarter = kosmos.activityStarter
private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)
@@ -240,7 +242,6 @@
mediaDeviceManager = mediaDeviceManager,
mediaDataCombineLatest = mediaDataCombineLatest,
mediaDataFilter = mediaDataFilter,
- activityStarter = activityStarter,
smartspaceMediaDataProvider = smartspaceMediaDataProvider,
useMediaResumption = true,
useQsMediaPlayer = true,
@@ -251,6 +252,7 @@
smartspaceManager = smartspaceManager,
keyguardUpdateMonitor = keyguardUpdateMonitor,
mediaDataLoader = { kosmos.mediaDataLoader },
+ mediaLogger = kosmos.mediaLogger,
)
verify(tunerService)
.addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
@@ -2404,6 +2406,45 @@
assertThat(mediaDataCaptor.value.artwork).isNull()
}
+ @Test
+ @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_POSTS_OPTIMIZATION)
+ fun postDuplicateNotification_doesNotCallListeners() {
+ addNotificationAndLoad()
+ reset(listener)
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+
+ testScope.assertRunAllReady(foreground = 0, background = 1)
+ verify(listener, never())
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ verify(kosmos.mediaLogger).logDuplicateMediaNotification(eq(KEY))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_POSTS_OPTIMIZATION)
+ fun postDuplicateNotification_callsListeners() {
+ addNotificationAndLoad()
+ reset(listener)
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ testScope.assertRunAllReady(foreground = 1, background = 1)
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(KEY),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ verify(kosmos.mediaLogger, never()).logDuplicateMediaNotification(eq(KEY))
+ }
+
private fun TestScope.assertRunAllReady(foreground: Int = 0, background: Int = 0) {
runCurrent()
if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
index 02d7413..bc29d2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
@@ -138,6 +138,7 @@
whenever(mockContext.packageManager).thenReturn(context.packageManager)
whenever(mockContext.contentResolver).thenReturn(context.contentResolver)
whenever(mockContext.userId).thenReturn(context.userId)
+ whenever(mockContext.resources).thenReturn(context.resources)
whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(false)
executor = FakeExecutor(clock)
@@ -210,7 +211,7 @@
@Test
fun testOnLoad_checksForResume_noService() {
// When media data is loaded that has not been checked yet, and does not have a MBS
- resumeListener.onMediaDataLoaded(KEY, null, data)
+ onMediaDataLoaded(KEY, null, data)
// Then we report back to the manager
verify(mediaDataManager).setResumeAction(KEY, null)
@@ -223,8 +224,7 @@
whenever(resumeBrowser.testConnection()).thenAnswer { callbackCaptor.value.onError() }
// When media data is loaded that has not been checked yet, and does not have a MBS
- resumeListener.onMediaDataLoaded(KEY, null, data)
- executor.runAllReady()
+ onMediaDataLoaded(KEY, null, data)
// Then we report back to the manager
verify(mediaDataManager).setResumeAction(eq(KEY), eq(null))
@@ -234,7 +234,7 @@
fun testOnLoad_localCast_doesNotCheck() {
// When media data is loaded that has not been checked yet, and is a local cast
val dataCast = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
- resumeListener.onMediaDataLoaded(KEY, null, dataCast)
+ onMediaDataLoaded(KEY, null, dataCast, false)
// Then we do not take action
verify(mediaDataManager, never()).setResumeAction(any(), any())
@@ -244,7 +244,7 @@
fun testOnload_remoteCast_doesNotCheck() {
// When media data is loaded that has not been checked yet, and is a remote cast
val dataRcn = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_REMOTE)
- resumeListener.onMediaDataLoaded(KEY, null, dataRcn)
+ onMediaDataLoaded(KEY, null, dataRcn, resume = false)
// Then we do not take action
verify(mediaDataManager, never()).setResumeAction(any(), any())
@@ -257,7 +257,7 @@
// When media data is loaded that has not been checked yet, and is a local cast
val dataCast = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
- resumeListener.onMediaDataLoaded(KEY, null, dataCast)
+ onMediaDataLoaded(KEY, null, dataCast)
// Then we report back to the manager
verify(mediaDataManager).setResumeAction(KEY, null)
@@ -270,7 +270,7 @@
// When media data is loaded that has not been checked yet, and is a remote cast
val dataRcn = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_REMOTE)
- resumeListener.onMediaDataLoaded(KEY, null, dataRcn)
+ onMediaDataLoaded(KEY, null, dataRcn, false)
// Then we do not take action
verify(mediaDataManager, never()).setResumeAction(any(), any())
@@ -288,10 +288,9 @@
// When media data is loaded that has not been checked yet, and does have a MBS
val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+ onMediaDataLoaded(KEY, null, dataCopy)
// Then we test whether the service is valid
- executor.runAllReady()
verify(mediaDataManager).setResumeAction(eq(KEY), eq(null))
verify(resumeBrowser).testConnection()
@@ -307,7 +306,7 @@
fun testOnLoad_doesNotCheckAgain() {
// When a media data is loaded that has been checked already
var dataCopy = data.copy(hasCheckedForResume = true)
- resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+ onMediaDataLoaded(KEY, null, dataCopy, resume = false)
// Then we should not check it again
verify(resumeBrowser, never()).testConnection()
@@ -320,17 +319,15 @@
setUpMbsWithValidResolveInfo()
resumeListener.onMediaDataLoaded(KEY, null, data)
- // We notify the manager to set a null action
- verify(mediaDataManager).setResumeAction(KEY, null)
-
// If we then get another update from the app before the first check completes
assertThat(executor.numPending()).isEqualTo(1)
var dataWithCheck = data.copy(hasCheckedForResume = true)
resumeListener.onMediaDataLoaded(KEY, null, dataWithCheck)
// We do not try to start another check
- assertThat(executor.numPending()).isEqualTo(1)
+ executor.runAllReady()
verify(mediaDataManager).setResumeAction(KEY, null)
+ verify(resumeBrowserFactory, times(1)).create(any(), any(), anyInt())
}
@Test
@@ -363,6 +360,7 @@
resumeListener.userUnlockReceiver.onReceive(context, intent)
// Then we should attempt to find recent media for each saved component
+ executor.runAllReady()
verify(resumeBrowser, times(3)).findRecentMedia()
// Then since the mock service found media, the manager should be informed
@@ -382,10 +380,9 @@
// When media data is loaded that has not been checked yet, and does have a MBS
val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+ onMediaDataLoaded(KEY, null, dataCopy)
// Then we test whether the service is valid and set the resume action
- executor.runAllReady()
verify(mediaDataManager).setResumeAction(eq(KEY), eq(null))
verify(resumeBrowser).testConnection()
verify(mediaDataManager, times(2)).setResumeAction(eq(KEY), capture(actionCaptor))
@@ -455,6 +452,7 @@
resumeListener.userUnlockReceiver.onReceive(mockContext, intent)
// We add its resume controls
+ executor.runAllReady()
verify(resumeBrowser).findRecentMedia()
verify(mediaDataManager)
.addResumptionControls(anyInt(), any(), any(), any(), any(), any(), eq(PACKAGE_NAME))
@@ -527,7 +525,7 @@
// When media data is loaded that has not been checked yet, and does have a MBS
val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+ onMediaDataLoaded(KEY, null, dataCopy)
// Then we store the new lastPlayed time
verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
@@ -546,10 +544,9 @@
fun testOnMediaDataLoaded_newKeyDifferent_oldMediaBrowserDisconnected() {
setUpMbsWithValidResolveInfo()
- resumeListener.onMediaDataLoaded(key = KEY, oldKey = null, data)
- executor.runAllReady()
+ onMediaDataLoaded(key = KEY, oldKey = null, data)
- resumeListener.onMediaDataLoaded(key = "newKey", oldKey = KEY, data)
+ onMediaDataLoaded(key = "newKey", oldKey = KEY, data)
verify(resumeBrowser).disconnect()
}
@@ -561,8 +558,7 @@
// Set up mocks to return with an error
whenever(resumeBrowser.testConnection()).thenAnswer { callbackCaptor.value.onError() }
- resumeListener.onMediaDataLoaded(key = KEY, oldKey = null, data)
- executor.runAllReady()
+ onMediaDataLoaded(key = KEY, oldKey = null, data)
// Ensure we disconnect the browser
verify(resumeBrowser).disconnect()
@@ -579,8 +575,7 @@
callbackCaptor.value.addTrack(description, component, resumeBrowser)
}
- resumeListener.onMediaDataLoaded(key = KEY, oldKey = null, data)
- executor.runAllReady()
+ onMediaDataLoaded(key = KEY, oldKey = null, data)
// Ensure we disconnect the browser
verify(resumeBrowser).disconnect()
@@ -598,8 +593,7 @@
// Load media data that will require us to get the resume action
val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
- executor.runAllReady()
+ onMediaDataLoaded(KEY, null, dataCopy)
verify(mediaDataManager, times(2)).setResumeAction(eq(KEY), capture(actionCaptor))
// Set up our factory to return a new browser so we can verify we disconnected the old one
@@ -634,6 +628,7 @@
// When the first user unlocks and we query their recent media
userCallbackCaptor.value.onUserChanged(firstUserId, context)
resumeListener.userUnlockReceiver.onReceive(context, unlockIntent)
+ executor.runAllReady()
whenever(resumeBrowser.userId).thenReturn(userIdCaptor.value)
verify(resumeBrowser, times(3)).findRecentMedia()
@@ -688,4 +683,16 @@
whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(resolveInfo)
whenever(pm.getApplicationLabel(any())).thenReturn(PACKAGE_NAME)
}
+
+ private fun onMediaDataLoaded(
+ key: String,
+ oldKey: String?,
+ data: MediaData,
+ resume: Boolean = true
+ ) {
+ resumeListener.onMediaDataLoaded(key, oldKey, data)
+ if (resume) {
+ assertThat(executor.runAllReady()).isEqualTo(1)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index d9faa30..70af5e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -31,17 +31,18 @@
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.shared.model.Text
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -199,10 +200,11 @@
android.R.drawable.star_on,
ContentDescription.Loaded(tileSpec)
),
- label = Text.Loaded(tileSpec),
+ label = AnnotatedString(tileSpec),
appName = null,
isCurrent = true,
availableEditActions = emptySet(),
+ category = TileCategory.UNKNOWN,
),
getWidth(tileSpec),
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index e6ec07e..9f84e34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -258,7 +258,7 @@
companion object {
const val WIFI_SSID = "test ssid"
val ACTIVE_WIFI =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
isValidated = true,
level = 4,
ssid = WIFI_SSID,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
index b6e23c1..715e3b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
@@ -85,7 +85,7 @@
underTest.dataConnectionState.onEach { latestConnState = it }.launchIn(this)
val netJob = underTest.resolvedNetworkType.onEach { latestNetType = it }.launchIn(this)
- wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive())
assertThat(latestConnState).isEqualTo(DataConnectionState.Disconnected)
assertThat(latestNetType).isNotEqualTo(ResolvedNetworkType.CarrierMergedNetworkType)
@@ -104,7 +104,7 @@
underTest.dataConnectionState.onEach { latestConnState = it }.launchIn(this)
val netJob = underTest.resolvedNetworkType.onEach { latestNetType = it }.launchIn(this)
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(level = 1))
assertThat(latestConnState).isEqualTo(DataConnectionState.Disconnected)
assertThat(latestNetType).isNotEqualTo(ResolvedNetworkType.CarrierMergedNetworkType)
@@ -123,7 +123,7 @@
wifiRepository.setIsWifiDefault(true)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 3,
)
@@ -143,7 +143,7 @@
wifiRepository.setIsWifiEnabled(true)
wifiRepository.setIsWifiDefault(true)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 3,
)
@@ -180,7 +180,7 @@
val typeJob = underTest.resolvedNetworkType.onEach { latestType = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID + 10,
level = 3,
)
@@ -201,7 +201,7 @@
val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 3,
)
@@ -221,7 +221,7 @@
val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 3,
)
@@ -240,7 +240,7 @@
val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 1,
numberOfLevels = 6,
@@ -303,7 +303,7 @@
whenever(telephonyManager.simOperatorName).thenReturn("New SIM name")
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = SUB_ID,
level = 3,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index a03980a..fd23655 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -488,7 +488,7 @@
// WHEN we set up carrier merged info
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 3,
)
@@ -499,7 +499,7 @@
// WHEN we update the info
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 1,
)
@@ -538,7 +538,7 @@
// WHEN isCarrierMerged is set to true
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 3,
)
@@ -550,7 +550,7 @@
// WHEN the carrier merge network is updated
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 4,
)
@@ -602,7 +602,7 @@
// THEN updates to the carrier merged level aren't logged
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 4,
)
@@ -610,7 +610,7 @@
assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}4")
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
SUB_ID,
level = 3,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index a1cb29b..c0a206a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -574,7 +574,7 @@
val latest by collectLastValue(underTest.isWifiActive)
// WHEN wifi is active
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(level = 1))
// THEN the interactor returns true due to the wifi network being active
assertThat(latest).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index c1abf98..e7e4969 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -375,7 +375,7 @@
repo.isSatelliteProvisioned.value = true
// GIVEN wifi network is active
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(level = 1))
// THEN icon is null because the device is connected to wifi
assertThat(latest).isNull()
@@ -573,7 +573,7 @@
repo.isSatelliteProvisioned.value = true
// GIVEN wifi network is active
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(level = 1))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(level = 1))
// THEN carrier text is null because the device is connected to wifi
assertThat(latest).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
index fed3317..975e2ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -154,7 +154,7 @@
val latest by collectLastValue(underTest.tileModel)
val networkModel =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
)
@@ -183,7 +183,7 @@
val latest by collectLastValue(underTest.tileModel)
val networkModel =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.NONE,
@@ -295,7 +295,7 @@
testScope.runTest {
val latest by collectLastValue(underTest.tileModel)
- val networkModel = WifiNetworkModel.Inactive
+ val networkModel = WifiNetworkModel.Inactive()
connectivityRepository.setWifiConnected(validated = false)
wifiRepository.setIsWifiDefault(true)
@@ -310,7 +310,7 @@
testScope.runTest {
val latest by collectLastValue(underTest.tileModel)
- val networkModel = WifiNetworkModel.Inactive
+ val networkModel = WifiNetworkModel.Inactive()
connectivityRepository.setWifiConnected(validated = false)
wifiRepository.setIsWifiDefault(true)
@@ -390,7 +390,7 @@
private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) {
val networkModel =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = 4,
ssid = "test ssid",
hotspotDeviceType = hotspot,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index 46f34e8..fd4b77d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -295,7 +295,10 @@
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
+ assertThat(latest).isInstanceOf(WifiNetworkModel.Inactive::class.java)
+ val inactiveReason = (latest as WifiNetworkModel.Inactive).inactiveReason
+ assertThat(inactiveReason).contains("level")
+ assertThat(inactiveReason).contains("$WIFI_LEVEL_UNREACHABLE")
}
@Test
@@ -311,7 +314,10 @@
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
+ assertThat(latest).isInstanceOf(WifiNetworkModel.Inactive::class.java)
+ val inactiveReason = (latest as WifiNetworkModel.Inactive).inactiveReason
+ assertThat(inactiveReason).contains("level")
+ assertThat(inactiveReason).contains("${WIFI_LEVEL_MAX + 1}")
}
@Test
@@ -327,7 +333,10 @@
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
+ assertThat(latest).isInstanceOf(WifiNetworkModel.Inactive::class.java)
+ val inactiveReason = (latest as WifiNetworkModel.Inactive).inactiveReason
+ assertThat(inactiveReason).contains("level")
+ assertThat(inactiveReason).contains("${WIFI_LEVEL_MIN - 1}")
}
@Test
@@ -530,6 +539,25 @@
}
@Test
+ fun wifiNetwork_carrierMergedButInvalidLevel_flowHasInvalid() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiNetwork)
+
+ val mergedEntry =
+ mock<MergedCarrierEntry>().apply {
+ whenever(this.isPrimaryNetwork).thenReturn(true)
+ whenever(this.subscriptionId).thenReturn(3)
+ whenever(this.isDefaultNetwork).thenReturn(true)
+ whenever(this.level).thenReturn(WIFI_LEVEL_UNREACHABLE)
+ }
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
+
+ getCallback().onWifiEntriesChanged()
+
+ assertThat(latest).isInstanceOf(WifiNetworkModel.Invalid::class.java)
+ }
+
+ @Test
fun wifiNetwork_notValidated_networkNotValidated() =
testScope.runTest {
val latest by collectLastValue(underTest.wifiNetwork)
@@ -571,7 +599,7 @@
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
getCallback().onWifiEntriesChanged()
- assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
+ assertThat(latest).isEqualTo(WifiNetworkModel.Inactive())
}
@Test
@@ -587,7 +615,7 @@
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
getCallback().onWifiEntriesChanged()
- assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
+ assertThat(latest).isEqualTo(WifiNetworkModel.Inactive())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
index 92860ef..1495519 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Companion.MIN_VALID_LEVEL
+import com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,59 +33,109 @@
@RunWith(AndroidJUnit4::class)
class WifiNetworkModelTest : SysuiTestCase() {
@Test
- fun active_levelsInValidRange_noException() {
+ fun active_levelsInValidRange_createsActive() {
(MIN_VALID_LEVEL..MAX_VALID_LEVEL).forEach { level ->
- WifiNetworkModel.Active(level = level)
- // No assert, just need no crash
+ val result = WifiNetworkModel.Active.of(level = level)
+ assertThat(result).isInstanceOf(WifiNetworkModel.Active::class.java)
}
}
- @Test(expected = IllegalArgumentException::class)
- fun active_levelNegative_exceptionThrown() {
- WifiNetworkModel.Active(level = MIN_VALID_LEVEL - 1)
+ fun active_levelTooLow_returnsInactive() {
+ val result = WifiNetworkModel.Active.of(level = MIN_VALID_LEVEL - 1)
+ assertThat(result).isInstanceOf(WifiNetworkModel.Inactive::class.java)
}
@Test(expected = IllegalArgumentException::class)
- fun active_levelTooHigh_exceptionThrown() {
- WifiNetworkModel.Active(level = MAX_VALID_LEVEL + 1)
+ fun active_levelTooLow_createdByCopy_exceptionThrown() {
+ val starting = WifiNetworkModel.Active.of(level = MIN_VALID_LEVEL)
+
+ (starting as WifiNetworkModel.Active).copy(level = MIN_VALID_LEVEL - 1)
+ }
+
+ fun active_levelTooHigh_returnsInactive() {
+ val result = WifiNetworkModel.Active.of(level = MAX_VALID_LEVEL + 1)
+
+ assertThat(result).isInstanceOf(WifiNetworkModel.Inactive::class.java)
}
@Test(expected = IllegalArgumentException::class)
- fun carrierMerged_invalidSubId_exceptionThrown() {
- WifiNetworkModel.CarrierMerged(INVALID_SUBSCRIPTION_ID, 1)
+ fun active_levelTooHigh_createdByCopy_exceptionThrown() {
+ val starting = WifiNetworkModel.Active.of(level = MAX_VALID_LEVEL)
+
+ (starting as WifiNetworkModel.Active).copy(level = MAX_VALID_LEVEL + 1)
+ }
+
+ fun active_levelUnreachable_returnsInactive() {
+ val result = WifiNetworkModel.Active.of(level = WIFI_LEVEL_UNREACHABLE)
+
+ assertThat(result).isInstanceOf(WifiNetworkModel.Inactive::class.java)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun active_levelUnreachable_createdByCopy_exceptionThrown() {
+ val starting = WifiNetworkModel.Active.of(level = MAX_VALID_LEVEL)
+
+ (starting as WifiNetworkModel.Active).copy(level = WIFI_LEVEL_UNREACHABLE)
+ }
+
+ fun carrierMerged_invalidSubId_returnsInvalid() {
+ val result = WifiNetworkModel.CarrierMerged.of(INVALID_SUBSCRIPTION_ID, level = 1)
+
+ assertThat(result).isInstanceOf(WifiNetworkModel.Invalid::class.java)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun carrierMerged_invalidSubId_createdByCopy_exceptionThrown() {
+ val starting = WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = 1)
+
+ (starting as WifiNetworkModel.CarrierMerged).copy(subscriptionId = INVALID_SUBSCRIPTION_ID)
+ }
+
+ fun carrierMerged_levelUnreachable_returnsInvalid() {
+ val result =
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = WIFI_LEVEL_UNREACHABLE)
+
+ assertThat(result).isInstanceOf(WifiNetworkModel.Invalid::class.java)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun carrierMerged_levelUnreachable_createdByCopy_exceptionThrown() {
+ val starting = WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = 1)
+
+ (starting as WifiNetworkModel.CarrierMerged).copy(level = WIFI_LEVEL_UNREACHABLE)
}
@Test
fun active_hasValidSsid_nullSsid_false() {
val network =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = MAX_VALID_LEVEL,
ssid = null,
)
- assertThat(network.hasValidSsid()).isFalse()
+ assertThat((network as WifiNetworkModel.Active).hasValidSsid()).isFalse()
}
@Test
fun active_hasValidSsid_unknownSsid_false() {
val network =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = MAX_VALID_LEVEL,
ssid = UNKNOWN_SSID,
)
- assertThat(network.hasValidSsid()).isFalse()
+ assertThat((network as WifiNetworkModel.Active).hasValidSsid()).isFalse()
}
@Test
fun active_hasValidSsid_validSsid_true() {
val network =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
level = MAX_VALID_LEVEL,
ssid = "FakeSsid",
)
- assertThat(network.hasValidSsid()).isTrue()
+ assertThat((network as WifiNetworkModel.Active).hasValidSsid()).isTrue()
}
// Non-exhaustive logDiffs test -- just want to make sure the logging logic isn't totally broken
@@ -93,14 +144,15 @@
fun logDiffs_carrierMergedToInactive_resetsAllFields() {
val logger = TestLogger()
val prevVal =
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = 3,
level = 1,
)
- WifiNetworkModel.Inactive.logDiffs(prevVal, logger)
+ WifiNetworkModel.Inactive(inactiveReason = "TestReason").logDiffs(prevVal, logger)
- assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_INACTIVE))
+ assertThat(logger.changes)
+ .contains(Pair(COL_NETWORK_TYPE, "$TYPE_INACTIVE[reason=TestReason]"))
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
@@ -110,12 +162,12 @@
fun logDiffs_inactiveToCarrierMerged_logsAllFields() {
val logger = TestLogger()
val carrierMerged =
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = 3,
level = 2,
)
- carrierMerged.logDiffs(prevVal = WifiNetworkModel.Inactive, logger)
+ carrierMerged.logDiffs(prevVal = WifiNetworkModel.Inactive(), logger)
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED))
assertThat(logger.changes).contains(Pair(COL_SUB_ID, "3"))
@@ -128,14 +180,14 @@
fun logDiffs_inactiveToActive_logsAllActiveFields() {
val logger = TestLogger()
val activeNetwork =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
isValidated = true,
level = 3,
ssid = "Test SSID",
hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.LAPTOP,
)
- activeNetwork.logDiffs(prevVal = WifiNetworkModel.Inactive, logger)
+ activeNetwork.logDiffs(prevVal = WifiNetworkModel.Inactive(), logger)
assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_ACTIVE))
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
@@ -148,11 +200,13 @@
fun logDiffs_activeToInactive_resetsAllActiveFields() {
val logger = TestLogger()
val activeNetwork =
- WifiNetworkModel.Active(isValidated = true, level = 3, ssid = "Test SSID")
+ WifiNetworkModel.Active.of(isValidated = true, level = 3, ssid = "Test SSID")
- WifiNetworkModel.Inactive.logDiffs(prevVal = activeNetwork, logger)
+ WifiNetworkModel.Inactive(inactiveReason = "TestReason")
+ .logDiffs(prevVal = activeNetwork, logger)
- assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_INACTIVE))
+ assertThat(logger.changes)
+ .contains(Pair(COL_NETWORK_TYPE, "$TYPE_INACTIVE[reason=TestReason]"))
assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
@@ -163,14 +217,14 @@
fun logDiffs_carrierMergedToActive_logsAllActiveFields() {
val logger = TestLogger()
val activeNetwork =
- WifiNetworkModel.Active(
+ WifiNetworkModel.Active.of(
isValidated = true,
level = 3,
ssid = "Test SSID",
hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.AUTO,
)
val prevVal =
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = 3,
level = 1,
)
@@ -188,9 +242,9 @@
fun logDiffs_activeToCarrierMerged_logsAllFields() {
val logger = TestLogger()
val activeNetwork =
- WifiNetworkModel.Active(isValidated = true, level = 3, ssid = "Test SSID")
+ WifiNetworkModel.Active.of(isValidated = true, level = 3, ssid = "Test SSID")
val carrierMerged =
- WifiNetworkModel.CarrierMerged(
+ WifiNetworkModel.CarrierMerged.of(
subscriptionId = 3,
level = 2,
)
@@ -208,9 +262,9 @@
fun logDiffs_activeChangesLevel_onlyLevelLogged() {
val logger = TestLogger()
val prevActiveNetwork =
- WifiNetworkModel.Active(isValidated = true, level = 3, ssid = "Test SSID")
+ WifiNetworkModel.Active.of(isValidated = true, level = 3, ssid = "Test SSID")
val newActiveNetwork =
- WifiNetworkModel.Active(isValidated = true, level = 2, ssid = "Test SSID")
+ WifiNetworkModel.Active.of(isValidated = true, level = 2, ssid = "Test SSID")
newActiveNetwork.logDiffs(prevActiveNetwork, logger)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index ff398f9..37c7a48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -195,7 +195,7 @@
@Test
fun isIconVisible_notEnabled_outputsFalse() {
wifiRepository.setIsWifiEnabled(false)
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(isValidated = true, level = 2))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(isValidated = true, level = 2))
val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
@@ -210,7 +210,7 @@
@Test
fun isIconVisible_enabled_outputsTrue() {
wifiRepository.setIsWifiEnabled(true)
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(isValidated = true, level = 2))
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Active.of(isValidated = true, level = 2))
val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index 82acb40..96a0194 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -206,51 +206,51 @@
// Enabled = false => no networks shown
TestCase(
enabled = false,
- network = WifiNetworkModel.CarrierMerged(subscriptionId = 1, level = 1),
+ network = WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = 1),
expected = null,
),
TestCase(
enabled = false,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected = null,
),
TestCase(
enabled = false,
- network = WifiNetworkModel.Active(isValidated = false, level = 1),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 1),
expected = null,
),
TestCase(
enabled = false,
- network = WifiNetworkModel.Active(isValidated = true, level = 3),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 3),
expected = null,
),
// forceHidden = true => no networks shown
TestCase(
forceHidden = true,
- network = WifiNetworkModel.CarrierMerged(subscriptionId = 1, level = 1),
+ network = WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = 1),
expected = null,
),
TestCase(
forceHidden = true,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected = null,
),
TestCase(
enabled = false,
- network = WifiNetworkModel.Active(isValidated = false, level = 2),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 2),
expected = null,
),
TestCase(
forceHidden = true,
- network = WifiNetworkModel.Active(isValidated = true, level = 1),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 1),
expected = null,
),
// alwaysShowIconWhenEnabled = true => all Inactive and Active networks shown
TestCase(
alwaysShowIconWhenEnabled = true,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected =
Expected(
iconResource = WIFI_NO_NETWORK,
@@ -263,7 +263,7 @@
),
TestCase(
alwaysShowIconWhenEnabled = true,
- network = WifiNetworkModel.Active(isValidated = false, level = 4),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 4),
expected =
Expected(
iconResource = WIFI_NO_INTERNET_ICONS[4],
@@ -276,7 +276,7 @@
),
TestCase(
alwaysShowIconWhenEnabled = true,
- network = WifiNetworkModel.Active(isValidated = true, level = 2),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 2),
expected =
Expected(
iconResource = WIFI_FULL_ICONS[2],
@@ -290,7 +290,7 @@
// hasDataCapabilities = false => all Inactive and Active networks shown
TestCase(
hasDataCapabilities = false,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected =
Expected(
iconResource = WIFI_NO_NETWORK,
@@ -303,7 +303,7 @@
),
TestCase(
hasDataCapabilities = false,
- network = WifiNetworkModel.Active(isValidated = false, level = 2),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 2),
expected =
Expected(
iconResource = WIFI_NO_INTERNET_ICONS[2],
@@ -316,7 +316,7 @@
),
TestCase(
hasDataCapabilities = false,
- network = WifiNetworkModel.Active(isValidated = true, level = 0),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 0),
expected =
Expected(
iconResource = WIFI_FULL_ICONS[0],
@@ -330,7 +330,7 @@
// isDefault = true => all Inactive and Active networks shown
TestCase(
isDefault = true,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected =
Expected(
iconResource = WIFI_NO_NETWORK,
@@ -343,7 +343,7 @@
),
TestCase(
isDefault = true,
- network = WifiNetworkModel.Active(isValidated = false, level = 3),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 3),
expected =
Expected(
iconResource = WIFI_NO_INTERNET_ICONS[3],
@@ -356,7 +356,7 @@
),
TestCase(
isDefault = true,
- network = WifiNetworkModel.Active(isValidated = true, level = 1),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 1),
expected =
Expected(
iconResource = WIFI_FULL_ICONS[1],
@@ -372,14 +372,14 @@
enabled = true,
isDefault = true,
forceHidden = false,
- network = WifiNetworkModel.CarrierMerged(subscriptionId = 1, level = 1),
+ network = WifiNetworkModel.CarrierMerged.of(subscriptionId = 1, level = 1),
expected = null,
),
// isDefault = false => no networks shown
TestCase(
isDefault = false,
- network = WifiNetworkModel.Inactive,
+ network = WifiNetworkModel.Inactive(),
expected = null,
),
TestCase(
@@ -389,7 +389,7 @@
),
TestCase(
isDefault = false,
- network = WifiNetworkModel.Active(isValidated = false, level = 3),
+ network = WifiNetworkModel.Active.of(isValidated = false, level = 3),
expected = null,
),
@@ -397,7 +397,7 @@
// because wifi isn't the default connection (b/272509965).
TestCase(
isDefault = false,
- network = WifiNetworkModel.Active(isValidated = true, level = 4),
+ network = WifiNetworkModel.Active.of(isValidated = true, level = 4),
expected = null,
),
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
index 811c653..251a6b1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt
@@ -50,6 +50,13 @@
Kosmos.Fixture {
KeyboardTouchpadEduStatsInteractorImpl(
backgroundScope = testScope.backgroundScope,
- contextualEducationInteractor = contextualEducationInteractor
+ contextualEducationInteractor = contextualEducationInteractor,
+ inputDeviceRepository =
+ UserInputDeviceRepository(
+ testDispatcher,
+ keyboardRepository,
+ touchpadRepository,
+ userRepository
+ )
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
index b03542c..33227a4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.panels.ui.viewmodel
+import android.content.applicationContext
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.qs.panels.domain.interactor.editTilesListInteractor
@@ -33,6 +35,8 @@
currentTilesInteractor,
tilesAvailabilityInteractor,
minimumTilesInteractor,
+ configurationInteractor,
+ applicationContext,
infiniteGridLayout,
applicationCoroutineScope,
gridLayoutTypeInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
index dceb8bf..f66125a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.instanceIdSequenceFake
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
@@ -47,6 +48,7 @@
tileSpec,
QSTileUIConfig.Empty,
instanceIdSequenceFake.newInstanceId(),
+ category = TileCategory.PROVIDED_BY_APP,
)
object : QSTileViewModel {
override val state: StateFlow<QSTileState?> =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
index 2a0ee88..73d9b32 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
@@ -18,6 +18,7 @@
import com.android.internal.logging.InstanceId
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
object QSTileConfigTestBuilder {
@@ -30,12 +31,14 @@
var instanceId: InstanceId = InstanceId.fakeInstanceId(0)
var metricsSpec: String = tileSpec.spec
var policy: QSTilePolicy = QSTilePolicy.NoRestrictions
+ var category: TileCategory = TileCategory.UNKNOWN
fun build() =
QSTileConfig(
tileSpec,
uiConfig,
instanceId,
+ category,
metricsSpec,
policy,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 709be5e..7ca90ea 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -32,7 +32,7 @@
override val isWifiDefault: StateFlow<Boolean> = _isWifiDefault
private val _wifiNetwork: MutableStateFlow<WifiNetworkModel> =
- MutableStateFlow(WifiNetworkModel.Inactive)
+ MutableStateFlow(WifiNetworkModel.Inactive())
override val wifiNetwork: StateFlow<WifiNetworkModel> = _wifiNetwork
override val secondaryNetworks = MutableStateFlow<List<WifiNetworkModel>>(emptyList())
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
index 888351f..ba6ffd7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
@@ -33,11 +33,7 @@
class FakeAudioRepository : AudioRepository {
- private val unMutableStreams =
- setOf(
- AudioManager.STREAM_VOICE_CALL,
- AudioManager.STREAM_ALARM,
- )
+ private val unMutableStreams = setOf(AudioManager.STREAM_VOICE_CALL, AudioManager.STREAM_ALARM)
private val mutableMode = MutableStateFlow(AudioManager.MODE_NORMAL)
override val mode: StateFlow<Int> = mutableMode.asStateFlow()
@@ -126,7 +122,7 @@
lastAudibleVolumes[audioStream] = volume
}
- override suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) {
+ override suspend fun setRingerModeInternal(audioStream: AudioStream, mode: RingerMode) {
mutableRingerMode.value = mode
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
index 0947238..39aa27a 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
@@ -27,6 +27,7 @@
import android.app.appsearch.GetByDocumentIdRequest;
import android.app.appsearch.GetSchemaResponse;
import android.app.appsearch.PutDocumentsRequest;
+import android.app.appsearch.RemoveByDocumentIdRequest;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResults;
import android.app.appsearch.SearchSpec;
@@ -146,6 +147,22 @@
});
}
+ /** Removes documents from the AppSearchSession database. */
+ public AndroidFuture<AppSearchBatchResult<String, Void>> remove(
+ @NonNull RemoveByDocumentIdRequest removeRequest) {
+ return getSessionAsync()
+ .thenCompose(
+ session -> {
+ AndroidFuture<AppSearchBatchResult<String, Void>>
+ settableBatchResultFuture = new AndroidFuture<>();
+ session.remove(
+ removeRequest,
+ mExecutor,
+ new BatchResultCallbackAdapter<>(settableBatchResultFuture));
+ return settableBatchResultFuture;
+ });
+ }
+
/**
* Retrieves documents from the open AppSearchSession that match a given query string and type
* of search provided.
@@ -200,9 +217,7 @@
Objects.requireNonNull(namespace);
GetByDocumentIdRequest request =
- new GetByDocumentIdRequest.Builder(namespace)
- .addIds(documentId)
- .build();
+ new GetByDocumentIdRequest.Builder(namespace).addIds(documentId).build();
return getSessionAsync()
.thenCompose(
session -> {
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
index be5770b..35fa47a 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.WorkerThread;
+import android.app.appsearch.PropertyPath;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchSpec;
import android.util.ArrayMap;
@@ -110,26 +111,29 @@
}
/**
- * This method returns a map of package names to a set of function ids.
+ * This method returns a map of package names to a set of function ids from the AppFunction
+ * metadata.
*
- * @param queryExpression The query expression to use when searching for AppFunction metadata.
- * @param metadataSearchSpec The search spec to use when searching for AppFunction metadata.
- * @return A map of package names to a set of function ids.
- * @throws ExecutionException If the future search results fail to execute.
- * @throws InterruptedException If the future search results are interrupted.
+ * @param schemaType The name space of the AppFunction metadata.
+ * @return A map of package names to a set of function ids from the AppFunction metadata.
*/
@NonNull
@VisibleForTesting
@WorkerThread
ArrayMap<String, ArraySet<String>> getPackageToFunctionIdMap(
- @NonNull String queryExpression,
- @NonNull SearchSpec metadataSearchSpec,
+ @NonNull String schemaType,
@NonNull String propertyFunctionId,
@NonNull String propertyPackageName)
throws ExecutionException, InterruptedException {
ArrayMap<String, ArraySet<String>> packageToFunctionIds = new ArrayMap<>();
+
FutureSearchResults futureSearchResults =
- mFutureAppSearchSession.search(queryExpression, metadataSearchSpec).get();
+ mFutureAppSearchSession
+ .search(
+ "",
+ buildMetadataSearchSpec(
+ schemaType, propertyFunctionId, propertyPackageName))
+ .get();
List<SearchResult> searchResultsList = futureSearchResults.getNextPage().get();
// TODO(b/357551503): This could be expensive if we have more functions
while (!searchResultsList.isEmpty()) {
@@ -146,4 +150,27 @@
}
return packageToFunctionIds;
}
+
+ /**
+ * This method returns a {@link SearchSpec} for searching the AppFunction metadata.
+ *
+ * @param schemaType The schema type of the AppFunction metadata.
+ * @param propertyFunctionId The property name of the function id in the AppFunction metadata.
+ * @param propertyPackageName The property name of the package name in the AppFunction metadata.
+ * @return A {@link SearchSpec} for searching the AppFunction metadata.
+ */
+ @NonNull
+ private static SearchSpec buildMetadataSearchSpec(
+ @NonNull String schemaType,
+ @NonNull String propertyFunctionId,
+ @NonNull String propertyPackageName) {
+ return new SearchSpec.Builder()
+ .addFilterSchemas(schemaType)
+ .addProjectionPaths(
+ schemaType,
+ List.of(
+ new PropertyPath(propertyFunctionId),
+ new PropertyPath(propertyPackageName)))
+ .build();
+ }
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 99c3eca..439bca0 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -453,7 +453,9 @@
proto.write(StorageRequestMessage.FlagOverrideMessage.PACKAGE_NAME, packageName);
proto.write(StorageRequestMessage.FlagOverrideMessage.FLAG_NAME, flagName);
proto.write(StorageRequestMessage.FlagOverrideMessage.FLAG_VALUE, flagValue);
- proto.write(StorageRequestMessage.FlagOverrideMessage.IS_LOCAL, isLocal);
+ proto.write(StorageRequestMessage.FlagOverrideMessage.OVERRIDE_TYPE, isLocal
+ ? StorageRequestMessage.LOCAL_ON_REBOOT
+ : StorageRequestMessage.SERVER_ON_REBOOT);
proto.end(msgToken);
proto.end(msgsToken);
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 2a16872..dba6c33 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -50,7 +50,6 @@
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.biometrics.face.IFace;
-import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -74,6 +73,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemService;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import java.util.ArrayList;
@@ -203,7 +203,7 @@
*/
@VisibleForTesting
public String[] getFingerprintAidlInstances() {
- return ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR);
+ return FingerprintService.getDeclaredInstances();
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 60cfd5a..2f6ba0b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -26,6 +26,7 @@
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.fingerprint.FingerprintSensorConfigurations.getIFingerprint;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -78,6 +79,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
@@ -1015,7 +1017,7 @@
this(context, BiometricContext.getInstance(context),
() -> IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
- () -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR),
+ () -> getDeclaredInstances(),
null /* fingerprintProvider */,
null /* fingerprintProviderFunction */);
}
@@ -1039,8 +1041,7 @@
mFingerprintProvider = fingerprintProvider != null ? fingerprintProvider :
(name) -> {
final String fqName = IFingerprint.DESCRIPTOR + "/" + name;
- final IFingerprint fp = IFingerprint.Stub.asInterface(
- Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+ final IFingerprint fp = getIFingerprint(fqName);
if (fp != null) {
try {
return new FingerprintProvider(getContext(),
@@ -1129,6 +1130,24 @@
publishBinderService(Context.FINGERPRINT_SERVICE, mServiceWrapper);
}
+ /**
+ * Get all fingerprint hal instances declared in manifest
+ * @return instance names
+ */
+ public static String[] getDeclaredInstances() {
+ String[] a = ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR);
+ Slog.i(TAG, "Before:getDeclaredInstances: IFingerprint instance found, a.length="
+ + a.length);
+ if (!ArrayUtils.contains(a, "virtual")) {
+ // Now, the virtual hal is registered with IVirtualHal interface and it is also
+ // moved from vendor to system_ext partition without a device manifest. So
+ // if the old vhal is not declared, add here.
+ a = ArrayUtils.appendElement(String.class, a, "virtual");
+ }
+ Slog.i(TAG, "After:getDeclaredInstances: a.length=" + a.length);
+ return a;
+ }
+
@NonNull
private List<Fingerprint> getEnrolledFingerprintsDeprecated(int userId, String opPackageName) {
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index caa2c1c..fd3d996 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -20,9 +20,9 @@
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.biometrics.fingerprint.AcquiredInfoAndVendorCode;
-import android.hardware.biometrics.fingerprint.EnrollmentProgressStep;
-import android.hardware.biometrics.fingerprint.NextEnrollment;
+import android.hardware.biometrics.fingerprint.virtualhal.AcquiredInfoAndVendorCode;
+import android.hardware.biometrics.fingerprint.virtualhal.EnrollmentProgressStep;
+import android.hardware.biometrics.fingerprint.virtualhal.NextEnrollment;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
@@ -300,4 +300,4 @@
super.getSensorId_enforcePermission();
return mSensorId;
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 9edaa4e..8195efe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -17,6 +17,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
import static android.hardware.fingerprint.FingerprintManager.SENSOR_ID_ANY;
+import static android.hardware.fingerprint.FingerprintSensorConfigurations.getIFingerprint;
+import static android.hardware.fingerprint.FingerprintSensorConfigurations.remapFqName;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,9 +36,9 @@
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.fingerprint.IFingerprint;
-import android.hardware.biometrics.fingerprint.IVirtualHal;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.biometrics.fingerprint.virtualhal.IVirtualHal;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintEnrollOptions;
@@ -291,7 +293,8 @@
if (mTestHalEnabled) {
return true;
}
- return (ServiceManager.checkService(IFingerprint.DESCRIPTOR + "/" + mHalInstanceName)
+ return (ServiceManager.checkService(
+ remapFqName(IFingerprint.DESCRIPTOR + "/" + mHalInstanceName))
!= null);
}
@@ -330,10 +333,7 @@
Slog.d(getTag(), "Daemon was null, reconnecting");
- mDaemon = IFingerprint.Stub.asInterface(
- Binder.allowBlocking(
- ServiceManager.waitForDeclaredService(
- IFingerprint.DESCRIPTOR + "/" + mHalInstanceNameCurrent)));
+ mDaemon = getIFingerprint(IFingerprint.DESCRIPTOR + "/" + mHalInstanceNameCurrent);
if (mDaemon == null) {
Slog.e(getTag(), "Unable to get daemon");
return null;
@@ -905,8 +905,8 @@
void setTestHalEnabled(boolean enabled) {
final boolean changed = enabled != mTestHalEnabled;
mTestHalEnabled = enabled;
- Slog.i(getTag(), "setTestHalEnabled(): useVhalForTesting=" + Flags.useVhalForTesting()
- + "mTestHalEnabled=" + mTestHalEnabled + " changed=" + changed);
+ Slog.i(getTag(), "setTestHalEnabled(): useVhalForTestingFlags=" + Flags.useVhalForTesting()
+ + " mTestHalEnabled=" + mTestHalEnabled + " changed=" + changed);
if (changed && useVhalForTesting()) {
getHalInstance();
}
@@ -999,12 +999,13 @@
*/
public IVirtualHal getVhal() throws RemoteException {
if (mVhal == null && useVhalForTesting()) {
- mVhal = IVirtualHal.Stub.asInterface(mDaemon.asBinder().getExtension());
- if (mVhal == null) {
- Slog.e(getTag(), "Unable to get fingerprint virtualhal interface");
- }
+ mVhal = IVirtualHal.Stub.asInterface(
+ Binder.allowBlocking(
+ ServiceManager.waitForService(
+ IVirtualHal.DESCRIPTOR + "/"
+ + mHalInstanceNameCurrent)));
+ Slog.d(getTag(), "getVhal " + mHalInstanceNameCurrent);
}
-
return mVhal;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index d12d7b2..25d1fe7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static android.hardware.fingerprint.FingerprintSensorConfigurations.remapFqName;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -356,8 +358,8 @@
if (mTestHalEnabled) {
return true;
}
- return (ServiceManager.checkService(IFingerprint.DESCRIPTOR + "/" + halInstance)
- != null);
+ return (ServiceManager.checkService(
+ remapFqName(IFingerprint.DESCRIPTOR + "/" + halInstance)) != null);
}
@NonNull protected BiometricContext getBiometricContext() {
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 8a3e392..1d68ee54 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import android.annotation.Nullable;
import android.hardware.display.BrightnessInfo;
import android.os.Handler;
import android.os.IBinder;
@@ -92,7 +93,7 @@
return mHbmController.getNormalBrightnessMax();
}
- void loadFromConfig(HighBrightnessModeMetadata hbmMetadata, IBinder token,
+ void loadFromConfig(@Nullable HighBrightnessModeMetadata hbmMetadata, IBinder token,
DisplayDeviceInfo info, DisplayDeviceConfig displayDeviceConfig) {
applyChanges(
() -> mNormalBrightnessModeController.resetNbmData(
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ed16b14..9644b1d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2170,9 +2170,7 @@
HighBrightnessModeMetadata hbmMetadata =
mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
- if (hbmMetadata != null) {
- dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
- }
+ dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
}
}
@@ -2291,9 +2289,7 @@
HighBrightnessModeMetadata hbmMetadata =
mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
- if (hbmMetadata != null) {
- dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
- }
+ dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
}
}
@@ -3560,6 +3556,18 @@
DisplayManagerFlags getFlags() {
return new DisplayManagerFlags();
}
+
+ DisplayPowerController getDisplayPowerController(Context context,
+ DisplayPowerController.Injector injector,
+ DisplayManagerInternal.DisplayPowerCallbacks callbacks, Handler handler,
+ SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
+ BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
+ Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
+ boolean bootCompleted, DisplayManagerFlags flags) {
+ return new DisplayPowerController(context, injector, callbacks, handler, sensorManager,
+ blanker, logicalDisplay, brightnessTracker, brightnessSetting,
+ onBrightnessChangeRunnable, hbmMetadata, bootCompleted, flags);
+ }
}
@VisibleForTesting
@@ -3612,7 +3620,7 @@
// with the corresponding displaydevice.
HighBrightnessModeMetadata hbmMetadata =
mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
- displayPowerController = new DisplayPowerController(
+ displayPowerController = mInjector.getDisplayPowerController(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
() -> handleBrightnessChange(display), hbmMetadata, mBootCompleted, mFlags);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 7c591e3..c3faec0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -849,7 +849,8 @@
*
* Make sure DisplayManagerService.mSyncRoot lock is held when this is called
*/
- public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata, int leadDisplayId) {
+ public void onDisplayChanged(@Nullable HighBrightnessModeMetadata hbmMetadata,
+ int leadDisplayId) {
mLeadDisplayId = leadDisplayId;
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
@@ -949,7 +950,7 @@
}
private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info,
- HighBrightnessModeMetadata hbmMetadata) {
+ @Nullable HighBrightnessModeMetadata hbmMetadata) {
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
mScreenBrightnessDozeConfig = BrightnessUtils.clampAbsoluteBrightness(
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index da9eef2..135cab6 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -266,7 +266,7 @@
mSettingsObserver.stopObserving();
}
- void setHighBrightnessModeMetadata(HighBrightnessModeMetadata hbmInfo) {
+ void setHighBrightnessModeMetadata(@Nullable HighBrightnessModeMetadata hbmInfo) {
mHighBrightnessModeMetadata = hbmInfo;
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 1285a61..dfcea9f 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2269,14 +2269,8 @@
// Native callback.
@SuppressWarnings("unused")
private void notifyTouchpadHardwareState(TouchpadHardwareState hardwareStates, int deviceId) {
- Slog.d(TAG, "notifyTouchpadHardwareState: Time: "
- + hardwareStates.getTimestamp() + ", No. Buttons: "
- + hardwareStates.getButtonsDown() + ", No. Fingers: "
- + hardwareStates.getFingerCount() + ", No. Touch: "
- + hardwareStates.getTouchCount() + ", Id: "
- + deviceId);
if (mTouchpadDebugViewController != null) {
- mTouchpadDebugViewController.updateTouchpadHardwareState(hardwareStates);
+ mTouchpadDebugViewController.updateTouchpadHardwareState(hardwareStates, deviceId);
}
}
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugView.java b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
index ba56ad0..7d8ce5f 100644
--- a/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
@@ -22,6 +22,7 @@
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.input.InputManager;
import android.util.Slog;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -65,7 +66,7 @@
mTouchpadId = touchpadId;
mWindowManager =
Objects.requireNonNull(getContext().getSystemService(WindowManager.class));
- init(context);
+ init(context, touchpadId);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
// TODO(b/360137366): Use the hardware properties to initialise layout parameters.
@@ -88,32 +89,40 @@
mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
}
- private void init(Context context) {
+ private void init(Context context, int touchpadId) {
setOrientation(VERTICAL);
setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
- setBackgroundColor(Color.RED);
+ setBackgroundColor(Color.TRANSPARENT);
- // TODO(b/286551975): Replace this content with the touchpad debug view.
- TextView textView1 = new TextView(context);
- textView1.setBackgroundColor(Color.TRANSPARENT);
- textView1.setTextSize(20);
- textView1.setText("Touchpad Debug View 1");
- textView1.setGravity(Gravity.CENTER);
- textView1.setTextColor(Color.WHITE);
- textView1.setLayoutParams(new LayoutParams(1000, 200));
+ TextView nameView = new TextView(context);
+ nameView.setBackgroundColor(Color.RED);
+ nameView.setTextSize(20);
+ nameView.setText(Objects.requireNonNull(Objects.requireNonNull(
+ mContext.getSystemService(InputManager.class))
+ .getInputDevice(touchpadId)).getName());
+ nameView.setGravity(Gravity.CENTER);
+ nameView.setTextColor(Color.WHITE);
+ nameView.setLayoutParams(new LayoutParams(1000, 200));
- TextView textView2 = new TextView(context);
- textView2.setBackgroundColor(Color.TRANSPARENT);
- textView2.setTextSize(20);
- textView2.setText("Touchpad Debug View 2");
- textView2.setGravity(Gravity.CENTER);
- textView2.setTextColor(Color.WHITE);
- textView2.setLayoutParams(new LayoutParams(1000, 200));
+ TouchpadVisualisationView touchpadVisualisationView =
+ new TouchpadVisualisationView(context);
+ touchpadVisualisationView.setBackgroundColor(Color.WHITE);
+ touchpadVisualisationView.setLayoutParams(new LayoutParams(1000, 200));
- addView(textView1);
- addView(textView2);
+ //TODO(b/365562952): Add a display for recognized gesture info here
+ TextView gestureInfoView = new TextView(context);
+ gestureInfoView.setBackgroundColor(Color.GRAY);
+ gestureInfoView.setTextSize(20);
+ gestureInfoView.setText("Touchpad Debug View 3");
+ gestureInfoView.setGravity(Gravity.CENTER);
+ gestureInfoView.setTextColor(Color.BLACK);
+ gestureInfoView.setLayoutParams(new LayoutParams(1000, 200));
+
+ addView(nameView);
+ addView(touchpadVisualisationView);
+ addView(gestureInfoView);
updateScreenDimensions();
}
@@ -204,7 +213,15 @@
return mWindowLayoutParams;
}
- public void updateHardwareState(TouchpadHardwareState touchpadHardwareState) {
+ /**
+ * Notify the view of a change in TouchpadHardwareState and changing the
+ * color of the view based on the status of the button click.
+ */
+ public void updateHardwareState(TouchpadHardwareState touchpadHardwareState, int deviceId) {
+ if (deviceId != mTouchpadId) {
+ return;
+ }
+
if (mLastTouchpadState.getButtonsDown() == 0) {
if (touchpadHardwareState.getButtonsDown() > 0) {
onTouchpadButtonPress();
@@ -219,18 +236,11 @@
private void onTouchpadButtonPress() {
Slog.d("TouchpadDebugView", "You clicked me!");
-
- // Iterate through all child views
- // Temporary demonstration for testing
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).setBackgroundColor(Color.BLUE);
- }
+ getChildAt(0).setBackgroundColor(Color.BLUE);
}
private void onTouchpadButtonRelease() {
Slog.d("TouchpadDebugView", "You released the click");
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).setBackgroundColor(Color.RED);
- }
+ getChildAt(0).setBackgroundColor(Color.RED);
}
}
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
index bc53c49..4d527bd 100644
--- a/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
@@ -68,6 +68,13 @@
@Override
public void onInputDeviceRemoved(int deviceId) {
hideDebugView(deviceId);
+ if (mTouchpadDebugView == null) {
+ final InputManager inputManager = Objects.requireNonNull(
+ mContext.getSystemService(InputManager.class));
+ for (int id : inputManager.getInputDeviceIds()) {
+ onInputDeviceAdded(id);
+ }
+ }
}
@Override
@@ -134,9 +141,13 @@
Slog.d(TAG, "Touchpad debug view removed.");
}
- public void updateTouchpadHardwareState(TouchpadHardwareState touchpadHardwareState) {
+ /**
+ * Notify the TouchpadDebugView with the new TouchpadHardwareState.
+ */
+ public void updateTouchpadHardwareState(TouchpadHardwareState touchpadHardwareState,
+ int deviceId) {
if (mTouchpadDebugView != null) {
- mTouchpadDebugView.updateHardwareState(touchpadHardwareState);
+ mTouchpadDebugView.updateHardwareState(touchpadHardwareState, deviceId);
}
}
}
diff --git a/services/core/java/com/android/server/input/debug/TouchpadVisualisationView.java b/services/core/java/com/android/server/input/debug/TouchpadVisualisationView.java
new file mode 100644
index 0000000..38442bc
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/TouchpadVisualisationView.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input.debug;
+
+import android.content.Context;
+import android.view.View;
+
+public class TouchpadVisualisationView extends View {
+ public TouchpadVisualisationView(Context context) {
+ super(context);
+ }
+}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 6303ecd..a41675a 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -298,12 +298,13 @@
restoreSettings();
- // Wipe all shell overlays on boot, to recover from a potentially broken device
- String shellPkgName = TextUtils.emptyIfNull(
- getContext().getString(android.R.string.config_systemShell));
- mSettings.removeIf(overlayInfo -> overlayInfo.isFabricated
- && shellPkgName.equals(overlayInfo.packageName));
-
+ if (Build.IS_USER) {
+ // Wipe all shell overlays on boot, to recover from a potentially broken device
+ String shellPkgName = TextUtils.emptyIfNull(
+ getContext().getString(android.R.string.config_systemShell));
+ mSettings.removeIf(overlayInfo -> overlayInfo.isFabricated
+ && shellPkgName.equals(overlayInfo.packageName));
+ }
initIfNeeded();
onStartUser(UserHandle.USER_SYSTEM);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 4dcc6e1..78359bd 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2224,15 +2224,21 @@
public ParcelFileDescriptor getWallpaperWithFeature(String callingPkg, String callingFeatureId,
IWallpaperManagerCallback cb, final int which, Bundle outParams, int wallpaperUserId,
boolean getCropped) {
- final boolean hasPrivilege = hasPermission(READ_WALLPAPER_INTERNAL)
- || hasPermission(MANAGE_EXTERNAL_STORAGE);
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ final boolean hasPrivilege = hasPermission(READ_WALLPAPER_INTERNAL);
if (!hasPrivilege) {
- mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
- Binder.getCallingPid(), Binder.getCallingUid(), callingPkg, callingFeatureId);
+ boolean hasManageExternalStorage = hasPermission(MANAGE_EXTERNAL_STORAGE)
+ || hasAppOpPermission(MANAGE_EXTERNAL_STORAGE, callingUid, callingPkg,
+ callingFeatureId, "getWallpaperWithFeature from package: " + callingPkg);
+ if (!hasManageExternalStorage) {
+ mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
+ callingPid, callingUid, callingPkg, callingFeatureId);
+ }
}
- wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
+ wallpaperUserId = ActivityManager.handleIncomingUser(callingPid, callingUid,
+ wallpaperUserId, false, true, "getWallpaper", null);
if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
@@ -2348,6 +2354,22 @@
return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
}
+ private boolean hasAppOpPermission(String permission, int callingUid, String callingPackage,
+ String attributionTag, String message) {
+ final String op = AppOpsManager.permissionToOp(permission);
+ final int opMode = mAppOpsManager.noteOpNoThrow(op, callingUid, callingPackage,
+ attributionTag, message);
+ switch (opMode) {
+ case AppOpsManager.MODE_ALLOWED:
+ case AppOpsManager.MODE_FOREGROUND:
+ return true;
+ case AppOpsManager.MODE_DEFAULT:
+ return hasPermission(permission);
+ default:
+ return false;
+ }
+ }
+
@Override
public WallpaperInfo getWallpaperInfo(int userId) {
return getWallpaperInfoWithFlags(FLAG_SYSTEM, userId);
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt
index a0f1a55..3bc4411 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt
@@ -19,8 +19,10 @@
import android.app.appfunctions.AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_NAMESPACE
import android.app.appfunctions.AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema
import android.app.appfunctions.AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema
+import android.app.appsearch.AppSearchBatchResult
import android.app.appsearch.AppSearchManager
import android.app.appsearch.PutDocumentsRequest
+import android.app.appsearch.RemoveByDocumentIdRequest
import android.app.appsearch.SearchSpec
import android.app.appsearch.SetSchemaRequest
import androidx.test.platform.app.InstrumentationRegistry
@@ -56,7 +58,7 @@
SetSchemaRequest.Builder()
.addSchemas(
createParentAppFunctionRuntimeSchema(),
- createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME)
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME),
)
.build()
@@ -74,7 +76,7 @@
SetSchemaRequest.Builder()
.addSchemas(
createParentAppFunctionRuntimeSchema(),
- createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME)
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME),
)
.build()
val schema = session.setSchema(setSchemaRequest)
@@ -93,6 +95,40 @@
}
@Test
+ fun remove() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSession(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME),
+ )
+ .build()
+ val schema = session.setSchema(setSchemaRequest)
+ assertThat(schema.get()).isNotNull()
+ val appFunctionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_PACKAGE_NAME, TEST_FUNCTION_ID, "").build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder()
+ .addGenericDocuments(appFunctionRuntimeMetadata)
+ .build()
+ val putResult = session.put(putDocumentsRequest)
+ assertThat(putResult.get().isSuccess).isTrue()
+ val removeDocumentRequest =
+ RemoveByDocumentIdRequest.Builder(APP_FUNCTION_RUNTIME_NAMESPACE)
+ .addIds(appFunctionRuntimeMetadata.id)
+ .build()
+
+ val removeResult: AppSearchBatchResult<String, Void> =
+ session.remove(removeDocumentRequest).get()
+
+ assertThat(removeResult).isNotNull()
+ assertThat(removeResult.isSuccess).isTrue()
+ }
+ }
+
+ @Test
fun search() {
val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
FutureAppSearchSession(appSearchManager, testExecutor, searchContext).use { session ->
@@ -100,7 +136,7 @@
SetSchemaRequest.Builder()
.addSchemas(
createParentAppFunctionRuntimeSchema(),
- createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME)
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME),
)
.build()
val schema = session.setSchema(setSchemaRequest)
@@ -132,7 +168,7 @@
SetSchemaRequest.Builder()
.addSchemas(
createParentAppFunctionRuntimeSchema(),
- createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME)
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME),
)
.build()
val schema = session.setSchema(setSchemaRequest)
@@ -144,12 +180,13 @@
.build()
val putResult = session.put(putDocumentsRequest)
- val genricDocument = session
- .getByDocumentId(
- /* documentId= */ "${TEST_PACKAGE_NAME}/${TEST_FUNCTION_ID}",
- APP_FUNCTION_RUNTIME_NAMESPACE
- )
- .get()
+ val genricDocument =
+ session
+ .getByDocumentId(
+ /* documentId= */ "${TEST_PACKAGE_NAME}/${TEST_FUNCTION_ID}",
+ APP_FUNCTION_RUNTIME_NAMESPACE,
+ )
+ .get()
val foundAppFunctionRuntimeMetadata = AppFunctionRuntimeMetadata(genricDocument)
assertThat(foundAppFunctionRuntimeMetadata.functionId).isEqualTo(TEST_FUNCTION_ID)
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
index 1061da2..5769e07 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
@@ -16,12 +16,9 @@
package com.android.server.appfunctions
import android.app.appfunctions.AppFunctionRuntimeMetadata
-import android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID
-import android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_PACKAGE_NAME
import android.app.appsearch.AppSearchManager
import android.app.appsearch.AppSearchManager.SearchContext
import android.app.appsearch.PutDocumentsRequest
-import android.app.appsearch.SearchSpec
import android.app.appsearch.SetSchemaRequest
import android.util.ArrayMap
import android.util.ArraySet
@@ -76,20 +73,11 @@
testExecutor,
FutureAppSearchSession(appSearchManager, testExecutor, searchContext),
)
- val searchSpec: SearchSpec =
- SearchSpec.Builder()
- .addFilterSchemas(
- AppFunctionRuntimeMetadata.RUNTIME_SCHEMA_TYPE,
- AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema(TEST_TARGET_PKG_NAME)
- .schemaType,
- )
- .build()
val packageToFunctionIdMap =
metadataSyncAdapter.getPackageToFunctionIdMap(
- "",
- searchSpec,
- PROPERTY_FUNCTION_ID,
- PROPERTY_PACKAGE_NAME,
+ AppFunctionRuntimeMetadata.RUNTIME_SCHEMA_TYPE,
+ AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID,
+ AppFunctionRuntimeMetadata.PROPERTY_PACKAGE_NAME,
)
assertThat(packageToFunctionIdMap).isNotNull()
@@ -135,21 +123,11 @@
testExecutor,
FutureAppSearchSession(appSearchManager, testExecutor, searchContext),
)
- val searchSpec: SearchSpec =
- SearchSpec.Builder()
- .setResultCountPerPage(1)
- .addFilterSchemas(
- AppFunctionRuntimeMetadata.RUNTIME_SCHEMA_TYPE,
- AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema(TEST_TARGET_PKG_NAME)
- .schemaType,
- )
- .build()
val packageToFunctionIdMap =
metadataSyncAdapter.getPackageToFunctionIdMap(
- "",
- searchSpec,
- PROPERTY_FUNCTION_ID,
- PROPERTY_PACKAGE_NAME,
+ AppFunctionRuntimeMetadata.RUNTIME_SCHEMA_TYPE,
+ AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID,
+ AppFunctionRuntimeMetadata.PROPERTY_PACKAGE_NAME,
)
assertThat(packageToFunctionIdMap).isNotNull()
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 52f1cbd..76c8e686 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -143,6 +143,7 @@
import com.android.server.display.DisplayManagerService.SyncRoot;
import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.layout.Layout;
import com.android.server.display.notifications.DisplayNotificationManager;
import com.android.server.input.InputManagerInternal;
import com.android.server.lights.LightsManager;
@@ -3201,6 +3202,45 @@
argThat(matchesFilter));
}
+ @Test
+ public void testOnDisplayChanged_HbmMetadataNull() {
+ DisplayPowerController dpc = mock(DisplayPowerController.class);
+ DisplayManagerService.Injector injector = new BasicInjector() {
+ @Override
+ DisplayPowerController getDisplayPowerController(Context context,
+ DisplayPowerController.Injector injector,
+ DisplayManagerInternal.DisplayPowerCallbacks callbacks, Handler handler,
+ SensorManager sensorManager, DisplayBlanker blanker,
+ LogicalDisplay logicalDisplay, BrightnessTracker brightnessTracker,
+ BrightnessSetting brightnessSetting, Runnable onBrightnessChangeRunnable,
+ HighBrightnessModeMetadata hbmMetadata, boolean bootCompleted,
+ DisplayManagerFlags flags) {
+ return dpc;
+ }
+ };
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, injector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ registerDefaultDisplays(displayManager);
+ displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+ // Add the FakeDisplayDevice
+ FakeDisplayDevice displayDevice = new FakeDisplayDevice();
+ DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
+ displayDeviceInfo.state = Display.STATE_ON;
+ displayDevice.setDisplayDeviceInfo(displayDeviceInfo);
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
+ initDisplayPowerController(localService);
+
+ // Simulate DisplayDevice change
+ DisplayDeviceInfo displayDeviceInfo2 = new DisplayDeviceInfo();
+ displayDeviceInfo2.copyFrom(displayDeviceInfo);
+ displayDeviceInfo2.state = Display.STATE_DOZE;
+ updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo2);
+
+ verify(dpc).onDisplayChanged(/* hbmMetadata= */ null, Layout.NO_LEAD_DISPLAY);
+ }
+
private void initDisplayPowerController(DisplayManagerInternal localService) {
localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
@Override
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index cbf7935..def3355 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -19,7 +19,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
+import android.os.UserHandle;
import android.webkit.UserPackage;
import android.webkit.WebViewProviderInfo;
@@ -137,16 +137,12 @@
List<UserPackage> ret = new ArrayList();
// Loop over defined users, and find the corresponding package for each user.
for (int userId : mUsers) {
- ret.add(new UserPackage(createUserInfo(userId),
+ ret.add(new UserPackage(UserHandle.of(userId),
userPackages == null ? null : userPackages.get(userId)));
}
return ret;
}
- private static UserInfo createUserInfo(int userId) {
- return new UserInfo(userId, "User nr. " + userId, 0 /* flags */);
- }
-
/**
* Set package for primary user.
*/
diff --git a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
index 0f08be2..2a49eb1 100644
--- a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
+++ b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
@@ -296,33 +296,31 @@
@Test
public void testTouchpadClick() {
- View child;
+ View child = mTouchpadDebugView.getChildAt(0);
mTouchpadDebugView.updateHardwareState(
new TouchpadHardwareState(0, 1 /* buttonsDown */, 0, 0,
- new TouchpadFingerState[0]));
+ new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID);
- for (int i = 0; i < mTouchpadDebugView.getChildCount(); i++) {
- child = mTouchpadDebugView.getChildAt(i);
- assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
- }
+ assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
mTouchpadDebugView.updateHardwareState(
new TouchpadHardwareState(0, 0 /* buttonsDown */, 0, 0,
- new TouchpadFingerState[0]));
+ new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID);
- for (int i = 0; i < mTouchpadDebugView.getChildCount(); i++) {
- child = mTouchpadDebugView.getChildAt(i);
- assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.RED);
- }
+ assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.RED);
mTouchpadDebugView.updateHardwareState(
new TouchpadHardwareState(0, 1 /* buttonsDown */, 0, 0,
- new TouchpadFingerState[0]));
+ new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID);
- for (int i = 0; i < mTouchpadDebugView.getChildCount(); i++) {
- child = mTouchpadDebugView.getChildAt(i);
- assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
- }
+ assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
+
+ // Color should not change because hardware state of a different touchpad
+ mTouchpadDebugView.updateHardwareState(
+ new TouchpadHardwareState(0, 0 /* buttonsDown */, 0, 0,
+ new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID + 1);
+
+ assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
}
}
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 60c4bf5..5e0f87f 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -292,12 +292,13 @@
}
if (!hasData) {
const String8& srcName = file->getSourceFile();
- auto fileModWhen = getFileModDate(srcName.c_str());
- if (fileModWhen == kInvalidModDate) { // file existence tested earlier,
- return false; // not expecting an error here
+ time_t fileModWhen;
+ fileModWhen = getFileModDate(srcName.c_str());
+ if (fileModWhen == (time_t) -1) { // file existence tested earlier,
+ return false; // not expecting an error here
}
-
- if (toTimeT(fileModWhen) > entry->getModWhen()) {
+
+ if (fileModWhen > entry->getModWhen()) {
// mark as deleted so add() will succeed
if (bundle->getVerbose()) {
printf(" (removing old '%s')\n", storageName.c_str());