diff options
227 files changed, 4067 insertions, 1842 deletions
| diff --git a/core/api/current.txt b/core/api/current.txt index 8e4de7a15dd6..46f426b9f8e9 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12494,6 +12494,7 @@ package android.content.pm {      method @FlaggedApi("android.os.allow_private_profile") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public final android.content.pm.LauncherUserInfo getLauncherUserInfo(@NonNull android.os.UserHandle);      method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);      method @FlaggedApi("android.os.allow_private_profile") @NonNull @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<java.lang.String> getPreInstalledSystemPackages(@NonNull android.os.UserHandle); +    method @FlaggedApi("android.os.get_private_space_settings") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.content.IntentSender getPrivateSpaceSettingsIntent();      method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<android.os.UserHandle> getProfiles();      method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);      method @Nullable public android.content.IntentSender getShortcutConfigActivityIntent(@NonNull android.content.pm.LauncherActivityInfo); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index e4a840745ab3..7ae5d1be9773 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -674,7 +674,7 @@ package android.webkit {      method @Nullable public String getCurrentWebViewPackageName();      method @FlaggedApi("android.webkit.update_service_v2") @NonNull public android.webkit.WebViewProviderInfo getDefaultWebViewPackage();      method @Nullable public static android.webkit.WebViewUpdateManager getInstance(); -    method @NonNull public android.webkit.WebViewProviderInfo[] getValidWebViewPackages(); +    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.QUERY_ALL_PACKAGES}) public android.webkit.WebViewProviderInfo[] getValidWebViewPackages();      method @NonNull public android.webkit.WebViewProviderResponse waitForAndGetProvider();    } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index b7de93aaaee3..099dbbc2764f 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2775,12 +2775,6 @@ package android.os.vibrator.persistence {    @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class ParsedVibration {      ctor public ParsedVibration(@NonNull java.util.List<android.os.VibrationEffect>); -    method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator); -  } - -  @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class VibrationXmlParser { -    method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.vibrator.persistence.ParsedVibration parse(@NonNull java.io.InputStream) throws java.io.IOException; -    method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.InputStream) throws java.io.IOException;    }    public static final class VibrationXmlParser.ParseFailedException extends java.io.IOException { diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index ef8501f563dc..aca9bb4d07c1 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -297,6 +297,12 @@ public class TaskInfo {      public boolean isTopActivityTransparent;      /** +     * Whether the top activity has specified style floating. +     * @hide +     */ +    public boolean isTopActivityStyleFloating; + +    /**       * Encapsulate specific App Compat information.       * @hide       */ @@ -429,6 +435,7 @@ public class TaskInfo {                  && parentTaskId == that.parentTaskId                  && Objects.equals(topActivity, that.topActivity)                  && isTopActivityTransparent == that.isTopActivityTransparent +                && isTopActivityStyleFloating == that.isTopActivityStyleFloating                  && appCompatTaskInfo.equalsForTaskOrganizer(that.appCompatTaskInfo);      } @@ -498,6 +505,7 @@ public class TaskInfo {          mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);          displayAreaFeatureId = source.readInt();          isTopActivityTransparent = source.readBoolean(); +        isTopActivityStyleFloating = source.readBoolean();          appCompatTaskInfo = source.readTypedObject(AppCompatTaskInfo.CREATOR);      } @@ -545,6 +553,7 @@ public class TaskInfo {          dest.writeTypedObject(mTopActivityLocusId, flags);          dest.writeInt(displayAreaFeatureId);          dest.writeBoolean(isTopActivityTransparent); +        dest.writeBoolean(isTopActivityStyleFloating);          dest.writeTypedObject(appCompatTaskInfo, flags);      } @@ -582,6 +591,7 @@ public class TaskInfo {                  + " locusId=" + mTopActivityLocusId                  + " displayAreaFeatureId=" + displayAreaFeatureId                  + " isTopActivityTransparent=" + isTopActivityTransparent +                + " isTopActivityStyleFloating=" + isTopActivityStyleFloating                  + " appCompatTaskInfo=" + appCompatTaskInfo                  + "}";      } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ab58f43bf165..8365840b1efb 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2925,10 +2925,12 @@ public abstract class Context {       */      @SuppressWarnings("HiddenAbstractMethod")      @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) -    public abstract void sendOrderedBroadcastAsUserMultiplePermissions(Intent intent, +    public void sendOrderedBroadcastAsUserMultiplePermissions(Intent intent,              UserHandle user, String[] receiverPermissions, int appOp, Bundle options,              BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, -            String initialData, Bundle initialExtras); +            String initialData, Bundle initialExtras) { +        throw new RuntimeException("Not implemented. Must override in a subclass."); +    }      /**       * Version of diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index df27f9d4e457..52c84dc0ac5d 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -935,12 +935,11 @@ public class LauncherApps {       *       * @return {@link IntentSender} object which launches the Private Space Settings Activity, if       * successful, null otherwise. -     * @hide       */      // Alternatively, a system app can access this api for private profile if they've been granted      // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.      @Nullable -    @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) +    @FlaggedApi(Flags.FLAG_GET_PRIVATE_SPACE_SETTINGS)      @RequiresPermission(conditional = true,              anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})      public IntentSender getPrivateSpaceSettingsIntent() { diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 124d07f7bb08..c7d94c66c81e 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -151,6 +151,16 @@ flag {  }  flag { +    name: "fix_get_user_property_cache" +    namespace: "multiuser" +    description: "Cache is not optimised for getUserProperty for values below 0, eg. UserHandler.USER_NULL or UserHandle.USER_ALL" +    bug: "350416200" +    metadata { +        purpose: PURPOSE_BUGFIX +  } +} + +flag {      name: "cache_quiet_mode_state"      namespace: "multiuser"      description: "Optimise quiet mode state retrieval" diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 2ded615580fd..903e91646332 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -699,6 +699,25 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing      }      /** +     * Set whether the HAL should ignore display touches. +     * Only applies to sensors where the HAL is reponsible for handling touches. +     * @hide +     */ +    @RequiresPermission(USE_BIOMETRIC_INTERNAL) +    public void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouch) { +        if (mService == null) { +            Slog.w(TAG, "setIgnoreDisplayTouches: no fingerprint service"); +            return; +        } + +        try { +            mService.setIgnoreDisplayTouches(requestId, sensorId, ignoreTouch); +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +    } + +    /**       * Request fingerprint enrollment. This call warms up the fingerprint hardware       * and starts scanning for fingerprints. Progress will be indicated by callbacks to the       * {@link EnrollmentCallback} object. It terminates when diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java index f701ec3ec367..d84d29292ed5 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java @@ -120,6 +120,14 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna      }      /** +     * Returns if sensor type is ultrasonic Udfps +     * @return true if sensor is ultrasonic Udfps, false otherwise +     */ +    public boolean isUltrasonicUdfps() { +        return sensorType == TYPE_UDFPS_ULTRASONIC; +    } + +    /**       * Returns if sensor type is side-FPS       * @return true if sensor is side-fps, false otherwise       */ diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 742fa570bad7..370f097b6850 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -195,6 +195,9 @@ interface IFingerprintService {      @EnforcePermission("USE_BIOMETRIC_INTERNAL")      void onUdfpsUiEvent(int event, long requestId, int sensorId); +    @EnforcePermission("USE_BIOMETRIC_INTERNAL") +    void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches); +      // Sets the controller for managing the UDFPS overlay.      @EnforcePermission("USE_BIOMETRIC_INTERNAL")      void setUdfpsOverlayController(in IUdfpsOverlayController controller); diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index 6458534efc9d..f026997bcc57 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -216,3 +216,11 @@ flag {      description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead"      bug: "324046728"  } + +flag { +    name: "get_private_space_settings" +    namespace: "profile_experiences" +    description: "Guards a new Private Profile API in LauncherApps" +    bug: "346294653" +    is_exported: true +} diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java index 555a1204c8a7..9c2fb7bccf5e 100644 --- a/core/java/android/os/vibrator/VibrationConfig.java +++ b/core/java/android/os/vibrator/VibrationConfig.java @@ -70,7 +70,7 @@ public class VibrationConfig {      private final boolean mDefaultKeyboardVibrationEnabled; -    private final boolean mHasFixedKeyboardAmplitude; +    private final boolean mKeyboardVibrationSettingsSupported;      /** @hide */      public VibrationConfig(@Nullable Resources resources) { @@ -89,8 +89,8 @@ public class VibrationConfig {                  com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger, false);          mDefaultKeyboardVibrationEnabled = loadBoolean(resources,                  com.android.internal.R.bool.config_defaultKeyboardVibrationEnabled, true); -        mHasFixedKeyboardAmplitude = loadFloat(resources, -                com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude, -1) > 0; +        mKeyboardVibrationSettingsSupported = loadBoolean(resources, +                com.android.internal.R.bool.config_keyboardVibrationSettingsSupported, false);          mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,                  com.android.internal.R.integer.config_defaultAlarmVibrationIntensity); @@ -202,11 +202,11 @@ public class VibrationConfig {      }      /** -     * Whether the device has a fixed amplitude for keyboard. +     * Whether the device support keyboard vibration settings.       * @hide       */ -    public boolean hasFixedKeyboardAmplitude() { -        return mHasFixedKeyboardAmplitude; +    public boolean isKeyboardVibrationSettingsSupported() { +        return mKeyboardVibrationSettingsSupported;      }      /** Get the default vibration intensity for given usage. */ @@ -249,6 +249,7 @@ public class VibrationConfig {                  + ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity                  + ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity                  + ", mDefaultKeyboardVibrationEnabled=" + mDefaultKeyboardVibrationEnabled +                + ", mKeyboardVibrationSettingsSupported=" + mKeyboardVibrationSettingsSupported                  + "}";      } diff --git a/core/java/android/os/vibrator/persistence/ParsedVibration.java b/core/java/android/os/vibrator/persistence/ParsedVibration.java index 74d3a7b799e7..19162699a2ef 100644 --- a/core/java/android/os/vibrator/persistence/ParsedVibration.java +++ b/core/java/android/os/vibrator/persistence/ParsedVibration.java @@ -38,7 +38,6 @@ import java.util.Objects;   *   * @hide   */ -@TestApi // This was used in CTS before the flag was introduced.  @SystemApi  @FlaggedApi(FLAG_VIBRATION_XML_APIS)  public final class ParsedVibration { @@ -62,7 +61,6 @@ public final class ParsedVibration {       *       * @hide       */ -    @TestApi // This was used in CTS before the flag was introduced.      @SystemApi      @FlaggedApi(FLAG_VIBRATION_XML_APIS)      @Nullable diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java index e2312e0c96c4..7d9624c25c12 100644 --- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java +++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java @@ -121,7 +121,6 @@ import java.util.List;   *   * @hide   */ -@TestApi // This was used in CTS before the flag was introduced.  @SystemApi  @FlaggedApi(FLAG_VIBRATION_XML_APIS)  public final class VibrationXmlParser { @@ -195,7 +194,6 @@ public final class VibrationXmlParser {       *       * @hide       */ -    @TestApi // Replacing test APIs used in CTS before the flagged system APIs was introduced.      @SystemApi      @FlaggedApi(FLAG_VIBRATION_XML_APIS)      @NonNull @@ -217,7 +215,6 @@ public final class VibrationXmlParser {       *       * @hide       */ -    @TestApi // Replacing test APIs used in CTS before the flagged system APIs was introduced.      @SystemApi      @FlaggedApi(FLAG_VIBRATION_XML_APIS)      @NonNull diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 94c769293bff..c954cdb270e8 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6178,6 +6178,15 @@ public final class Settings {          public static final String POINTER_FILL_STYLE = "pointer_fill_style";          /** +         * Pointer stroke style, specified by +         * {@link android.view.PointerIcon.PointerIconVectorStyleStroke} constants. +         * +         * @hide +         */ +        @Readable +        public static final String POINTER_STROKE_STYLE = "pointer_stroke_style"; + +        /**           * Whether lock-to-app will be triggered by long-press on recents.           * @hide           */ @@ -6380,6 +6389,7 @@ public final class Settings {              PRIVATE_SETTINGS.add(SIP_ASK_ME_EACH_TIME);              PRIVATE_SETTINGS.add(POINTER_SPEED);              PRIVATE_SETTINGS.add(POINTER_FILL_STYLE); +            PRIVATE_SETTINGS.add(POINTER_STROKE_STYLE);              PRIVATE_SETTINGS.add(POINTER_SCALE);              PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED);              PRIVATE_SETTINGS.add(EGG_MODE); diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index c30212657c57..15351453a151 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -193,6 +193,25 @@ public final class PointerIcon implements Parcelable {      /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_END =              POINTER_ICON_VECTOR_STYLE_FILL_BLUE; +    /** @hide */ +    @IntDef(prefix = {"POINTER_ICON_VECTOR_STYLE_STROKE_"}, value = { +            POINTER_ICON_VECTOR_STYLE_STROKE_WHITE, +            POINTER_ICON_VECTOR_STYLE_STROKE_BLACK, +            POINTER_ICON_VECTOR_STYLE_STROKE_NONE +    }) +    @Retention(RetentionPolicy.SOURCE) +    public @interface PointerIconVectorStyleStroke {} + +    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_STROKE_WHITE = 0; +    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_STROKE_BLACK = 1; +    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_STROKE_NONE = 2; + +    // If adding PointerIconVectorStyleStroke, update END value for {@link SystemSettingsValidators} +    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_STROKE_BEGIN = +            POINTER_ICON_VECTOR_STYLE_STROKE_WHITE; +    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_STROKE_END = +            POINTER_ICON_VECTOR_STYLE_STROKE_NONE; +      /** @hide */ public static final float DEFAULT_POINTER_SCALE = 1f;      /** @hide */ public static final float LARGE_POINTER_SCALE = 2.5f; @@ -712,6 +731,23 @@ public final class PointerIcon implements Parcelable {      }      /** +     * Convert stroke style constant to resource ID. +     * +     * @hide +     */ +    public static int vectorStrokeStyleToResource(@PointerIconVectorStyleStroke int strokeStyle) { +        return switch (strokeStyle) { +            case POINTER_ICON_VECTOR_STYLE_STROKE_BLACK -> +                    com.android.internal.R.style.PointerIconVectorStyleStrokeBlack; +            case POINTER_ICON_VECTOR_STYLE_STROKE_WHITE -> +                    com.android.internal.R.style.PointerIconVectorStyleStrokeWhite; +            case POINTER_ICON_VECTOR_STYLE_STROKE_NONE -> +                    com.android.internal.R.style.PointerIconVectorStyleStrokeNone; +            default -> com.android.internal.R.style.PointerIconVectorStyleStrokeWhite; +        }; +    } + +    /**       * Sets whether drop shadow will draw in the native code.       *       * @hide diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 766e02bf3198..47669427cb9d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13859,11 +13859,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,      })      @ResolvedLayoutDir      public int getLayoutDirection() { -        final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; -        if (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) { -            mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED; -            return LAYOUT_DIRECTION_RESOLVED_DEFAULT; -        }          return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ==                  PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;      } diff --git a/core/java/android/view/ViewTraversalTracingStrings.java b/core/java/android/view/ViewTraversalTracingStrings.java index 7dde87bad26e..e95205b79c56 100644 --- a/core/java/android/view/ViewTraversalTracingStrings.java +++ b/core/java/android/view/ViewTraversalTracingStrings.java @@ -58,6 +58,6 @@ class ViewTraversalTracingStrings {          out.append(" ");          out.append(className);          v.appendId(out); -        return out.substring(0, Math.min(out.length() - 1, Trace.MAX_SECTION_NAME_LEN - 1)); +        return out.substring(0, Math.min(out.length(), Trace.MAX_SECTION_NAME_LEN));      }  } diff --git a/core/java/android/webkit/WebViewUpdateManager.java b/core/java/android/webkit/WebViewUpdateManager.java index 07576a2e89e4..dd48df975906 100644 --- a/core/java/android/webkit/WebViewUpdateManager.java +++ b/core/java/android/webkit/WebViewUpdateManager.java @@ -101,6 +101,8 @@ public final class WebViewUpdateManager {       * enabled for all users.       */      @SuppressLint({"ParcelableList", "ArrayReturn"}) +    @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS, +            android.Manifest.permission.QUERY_ALL_PACKAGES})      public @NonNull WebViewProviderInfo[] getValidWebViewPackages() {          try {              return mService.getValidWebViewPackages(); diff --git a/core/java/android/window/TaskFragmentAnimationParams.java b/core/java/android/window/TaskFragmentAnimationParams.java index 85e96c9c0290..67b22f9eb9c8 100644 --- a/core/java/android/window/TaskFragmentAnimationParams.java +++ b/core/java/android/window/TaskFragmentAnimationParams.java @@ -171,7 +171,7 @@ public final class TaskFragmentAnimationParams implements Parcelable {       */      public boolean hasOverrideAnimation() {          return mOpenAnimationResId != DEFAULT_ANIMATION_RESOURCES_ID -                || mChangeAnimationResId != DEFAULT_ANIMATION_BACKGROUND_COLOR +                || mChangeAnimationResId != DEFAULT_ANIMATION_RESOURCES_ID                  || mCloseAnimationResId != DEFAULT_ANIMATION_RESOURCES_ID;      } diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig index 5b99ff9703e0..cc880e182b2b 100644 --- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig +++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig @@ -113,4 +113,12 @@ flag {      namespace: "large_screen_experiences_app_compat"      description: "Enables sysui animation for user aspect ratio button"      bug: "300357441" +} + +flag { +  name: "app_compat_ui_framework" +  namespace: "large_screen_experiences_app_compat" +  description: "Whether the declarative compat UI framework is enabled" +  bug: "270361630" +  is_fixed_read_only: true  }
\ No newline at end of file diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index d0ab674a0e62..1c7acd4ea9af 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -127,3 +127,10 @@ flag {      description: "Whether to show developer option for enabling desktop windowing mode"      bug: "348193756"  } + +flag { +    name: "enable_desktop_windowing_app_to_web" +    namespace: "lse_desktop_experience" +    description: "Whether to enable the app-to-web feature and show the open in browser button in the header menu" +    bug: "349695493" +} diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto index 6a0ec1dcb3f2..e5ced2521134 100644 --- a/core/proto/android/providers/settings/system.proto +++ b/core/proto/android/providers/settings/system.proto @@ -126,6 +126,7 @@ message SystemSettingsProto {          option (android.msg_privacy).dest = DEST_EXPLICIT;          optional SettingProto pointer_fill_style = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; +        optional SettingProto pointer_stroke_style = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];          optional SettingProto pointer_scale = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];      }      optional Pointer pointer = 37; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index f61b6bf3f861..9f00d5e08bbf 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -843,6 +843,7 @@      <protected-broadcast android:name="android.intent.action.PROFILE_UNAVAILABLE" />      <protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" />      <protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" /> +    <protected-broadcast android:name="com.android.uwb.uwbcountrycode.GEOCODE_RETRY" />      <!-- ====================================================================== -->      <!--                          RUNTIME PERMISSIONS                           --> diff --git a/core/res/res/drawable/pointer_alias_vector.xml b/core/res/res/drawable/pointer_alias_vector.xml index 035a099cf632..60149f1f26ae 100644 --- a/core/res/res/drawable/pointer_alias_vector.xml +++ b/core/res/res/drawable/pointer_alias_vector.xml @@ -22,10 +22,10 @@          android:fillColor="#00FFFFFF"          android:pathData="M14.494 12.779a5.2 5.2 0 0 1-1.771 1.414 5.2 5.2 0 0 1-1.68.489l.81 1.658a1.968 1.968 0 0 0 3.536-1.728zM12.03 8.291l-.81-1.658a1.968 1.968 0 0 0-3.536 1.728l.896 1.833a5.2 5.2 0 0 1 1.77-1.414 5.2 5.2 0 0 1 1.68-.489" />      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="m18.323 13.178-.975-1.995a5.2 5.2 0 0 0-1.704-1.978 5.2 5.2 0 0 0-.517-2.01L14.152 5.2a5.232 5.232 0 0 0-9.401 4.594l.975 1.995a5.2 5.2 0 0 0 1.704 1.978 5.2 5.2 0 0 0 .517 2.01l.975 1.995a5.233 5.233 0 0 0 9.401-4.594m-2.843 6.1a4.23 4.23 0 0 1-5.66-1.944l-.975-1.995a4.2 4.2 0 0 1-.431-1.838l-.001-.276-.234-.146a4.2 4.2 0 0 1-1.555-1.729L5.65 9.355a4.232 4.232 0 1 1 7.604-3.716l.975 1.995c.29.594.428 1.22.431 1.838l.001.276.234.146c.648.405 1.194.99 1.555 1.728l.975 1.995a4.234 4.234 0 0 1-1.945 5.661" />      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="M15.313 12.177a3 3 0 0 0-.416-.633l-.459-.534-.353.609a4.2 4.2 0 0 1-1.801 1.675 4.2 4.2 0 0 1-1.977.429l-.704-.02.213.671q.066.208.164.409l.975 1.995a2.967 2.967 0 1 0 5.332-2.606zm-.827 5.066a1.97 1.97 0 0 1-2.632-.904l-.81-1.658a5.2 5.2 0 0 0 1.68-.489 5.2 5.2 0 0 0 1.771-1.414l.896 1.833a1.97 1.97 0 0 1-.905 2.632m-3.697-7.565a4.2 4.2 0 0 1 1.977-.429l.704.02-.213-.671a3 3 0 0 0-.164-.409l-.975-1.995A2.967 2.967 0 1 0 6.785 8.8l.975 1.995q.172.35.416.633l.459.534.353-.609a4.2 4.2 0 0 1 1.801-1.675m-2.21.516-.895-1.833a1.968 1.968 0 1 1 3.536-1.728l.81 1.658a5.2 5.2 0 0 0-1.68.489 5.2 5.2 0 0 0-1.771 1.414m3.151 1.965a3 3 0 0 0 1.02-.818l.755-.95-1.205.142a2.97 2.97 0 0 0-1.975 1.1l-.755.95 1.205-.142c.324-.039.646-.132.955-.282" />      <path          android:fillColor="?attr/pointerIconVectorFill" diff --git a/core/res/res/drawable/pointer_all_scroll_vector.xml b/core/res/res/drawable/pointer_all_scroll_vector.xml index 45ad98c1b57c..41314ed7e3a4 100644 --- a/core/res/res/drawable/pointer_all_scroll_vector.xml +++ b/core/res/res/drawable/pointer_all_scroll_vector.xml @@ -24,5 +24,5 @@          android:fillColor="?attr/pointerIconVectorFill"/>      <path          android:pathData="M12 4c.36 0 .72.18.93.54l1.75 3.06c.41.71-.1 1.6-.92 1.6h-.82v1.86h1.86v-.84a1.07 1.07 0 0 1 1.6-.92l3.06 1.75c.72.41.72 1.44 0 1.85l-3.06 1.76a1.07 1.07 0 0 1-1.6-.92v-.8h-1.86v1.87h.82c.82 0 1.33.88.92 1.6l-1.75 3.06a1.07 1.07 0 0 1-1.85 0L9.32 16.4c-.4-.7.1-1.6.93-1.6h.81v-1.86H9.2v.8a1.07 1.07 0 0 1-1.6.92L4.54 12.9a1.06 1.06 0 0 1 0-1.85L7.6 9.3a1.07 1.07 0 0 1 1.6.92v.85h1.86V9.2h-.82c-.81 0-1.33-.89-.92-1.6l1.76-3.06c.2-.36.56-.54.92-.54m0-1c-.74 0-1.41.39-1.79 1.04L8.45 7.1c-.18.33-.28.7-.27 1.05h-.05c-.36 0-.71.1-1.03.28l-3.06 1.76a2.05 2.05 0 0 0 0 3.58l3.06 1.75c.32.18.67.28 1.03.28h.05c-.01.38.08.76.28 1.1l1.75 3.07c.38.65 1.05 1.03 1.8 1.03s1.41-.38 1.78-1.03l1.76-3.07c.2-.34.3-.72.28-1.1h.04c.36 0 .71-.1 1.03-.28l3.06-1.75a2.07 2.07 0 0 0 0-3.58L16.9 8.43a2.07 2.07 0 0 0-1.03-.28h-.04c0-.36-.09-.72-.28-1.05L13.8 4.04A2.04 2.04 0 0 0 12 3z" -        android:fillColor="#FFFFFF"/> +        android:fillColor="?attr/pointerIconVectorStroke"/>  </vector> diff --git a/core/res/res/drawable/pointer_arrow_vector.xml b/core/res/res/drawable/pointer_arrow_vector.xml index 2614170f6994..a0bd5b614280 100644 --- a/core/res/res/drawable/pointer_arrow_vector.xml +++ b/core/res/res/drawable/pointer_arrow_vector.xml @@ -24,5 +24,5 @@          android:fillColor="?attr/pointerIconVectorFill"/>      <path          android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27 2.75 2.75 0 0 0-1.55 2.51l.01 11.95a2.78 2.78 0 0 0 2.82 2.8c.77 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92z" -        android:fillColor="#FFFFFF"/> +        android:fillColor="?attr/pointerIconVectorStroke"/>  </vector> diff --git a/core/res/res/drawable/pointer_cell_vector.xml b/core/res/res/drawable/pointer_cell_vector.xml index cead1c4185b4..9ad64e27da63 100644 --- a/core/res/res/drawable/pointer_cell_vector.xml +++ b/core/res/res/drawable/pointer_cell_vector.xml @@ -19,7 +19,7 @@      android:viewportHeight="24"      android:viewportWidth="24">      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="M19 9.667h-4.668V5a2 2 0 0 0-2-2h-.667a2 2 0 0 0-2 2v4.667H5a2 2 0 0 0-2 2v.667a2 2 0 0 0 2 2h4.665V19a2 2 0 0 0 2 2h.667a2 2 0 0 0 2-2v-4.666H19a2 2 0 0 0 2-2v-.667a2 2 0 0 0-2-2m1 2.667a1 1 0 0 1-1 1h-5.668V19a1 1 0 0 1-1 1h-.667a1 1 0 0 1-1-1v-5.666H5a1 1 0 0 1-1-1v-.667a1 1 0 0 1 1-1h5.665V5a1 1 0 0 1 1-1h.667a1 1 0 0 1 1 1v5.667H19a1 1 0 0 1 1 1z" />      <path          android:fillColor="?attr/pointerIconVectorFill" diff --git a/core/res/res/drawable/pointer_context_menu_vector.xml b/core/res/res/drawable/pointer_context_menu_vector.xml index fb2af431ffb6..33b1acefc827 100644 --- a/core/res/res/drawable/pointer_context_menu_vector.xml +++ b/core/res/res/drawable/pointer_context_menu_vector.xml @@ -19,11 +19,11 @@      android:viewportHeight="24"      android:viewportWidth="24">      <group> -        <path android:fillColor="#FFFFFF" android:pathData="M19.475 2.604h-2.66c-.842 0-1.527.685-1.527 1.527v2.66c0 .842.685 1.527 1.527 1.527h2.66c.842 0 1.527-.685 1.527-1.527v-2.66c0-.842-.685-1.527-1.527-1.527m.67 4.187c0 .37-.3.67-.67.67h-2.66a.67.67 0 0 1-.67-.67v-2.66c0-.37.3-.67.67-.67h2.66c.37 0 .67.3.67.67z" /> -        <path android:fillColor="#FFFFFF" android:pathData="M19.175 4.17h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .886h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .868h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6" /> +        <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="M19.475 2.604h-2.66c-.842 0-1.527.685-1.527 1.527v2.66c0 .842.685 1.527 1.527 1.527h2.66c.842 0 1.527-.685 1.527-1.527v-2.66c0-.842-.685-1.527-1.527-1.527m.67 4.187c0 .37-.3.67-.67.67h-2.66a.67.67 0 0 1-.67-.67v-2.66c0-.37.3-.67.67-.67h2.66c.37 0 .67.3.67.67z" /> +        <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="M19.175 4.17h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .886h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .868h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6" />      </group>      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="M16.938 10.38 7.372 3.216a2.77 2.77 0 0 0-2.931-.262A2.75 2.75 0 0 0 2.894 5.46l.009 11.951a2.785 2.785 0 0 0 1.776 2.604c.33.129.691.197 1.044.197a2.75 2.75 0 0 0 2.031-.897l2.969-3.193a.8.8 0 0 1 .5-.25l4.336-.467c1.397-.15 2.157-1.153 2.401-2.041a2.785 2.785 0 0 0-1.022-2.984m.058 2.718c-.157.571-.645 1.216-1.544 1.312l-4.335.467a1.8 1.8 0 0 0-1.126.563l-2.97 3.193a1.74 1.74 0 0 1-1.298.578 1.9 1.9 0 0 1-.678-.128c-.551-.217-1.141-.771-1.142-1.674l-.009-11.95c0-.697.371-1.299.994-1.611.262-.131.538-.196.813-.196.377 0 .75.123 1.072.365l9.566 7.163c.723.542.814 1.346.657 1.918" />      <path          android:fillColor="?attr/pointerIconVectorFill" diff --git a/core/res/res/drawable/pointer_copy_vector.xml b/core/res/res/drawable/pointer_copy_vector.xml index 3f138685e6eb..8ac8c2181ed5 100644 --- a/core/res/res/drawable/pointer_copy_vector.xml +++ b/core/res/res/drawable/pointer_copy_vector.xml @@ -23,7 +23,7 @@          <path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M19.299 6H18V4.7a.5.5 0 0 0-1 0V6h-1.301a.5.5 0 0 0 0 1H17v1.3a.5.5 0 0 0 1 0V7h1.299a.5.5 0 0 0 0-1" />      </group>      <path -        android:fillColor="#000000" +        android:fillColor="?attr/pointerIconVectorStrokeInverse"          android:pathData="M18.485 10.884v1.739q.013.189.013.38c0 3.045-2.518 5.514-5.563 5.514a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022q.22-.018.446-.018c.062 0 .123.007.185.009A4.4 4.4 0 0 1 13 6.5c0-.378.061-.739.149-1.09a1.97 1.97 0 0 0-1.659-.926c-.903 0-1.658.603-1.906 1.425a1.997 1.997 0 0 0-3.091 1.674v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514q-.001-.192-.013-.381v-2.108c-.316.156-.645.29-.999.37" />      <path          android:fillColor="#1FA54A" diff --git a/core/res/res/drawable/pointer_crosshair_vector.xml b/core/res/res/drawable/pointer_crosshair_vector.xml index 8a50d1bdd497..4fba08076e7a 100644 --- a/core/res/res/drawable/pointer_crosshair_vector.xml +++ b/core/res/res/drawable/pointer_crosshair_vector.xml @@ -19,7 +19,7 @@      android:viewportHeight="24"      android:viewportWidth="24">      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="M19.25 10.25h-5.5v-5.5a1.75 1.75 0 0 0-3.5 0v5.5h-5.5a1.75 1.75 0 0 0 0 3.5h5.5v5.5a1.75 1.75 0 0 0 3.5 0v-5.5h5.5a1.75 1.75 0 0 0 0-3.5m0 2.5h-6.5v6.5a.75.75 0 0 1-1.5 0v-6.5h-6.5a.75.75 0 0 1 0-1.5h6.5v-6.5a.75.75 0 0 1 1.5 0v6.5h6.5a.75.75 0 0 1 0 1.5" />      <path          android:fillType="evenOdd" diff --git a/core/res/res/drawable/pointer_grab_vector.xml b/core/res/res/drawable/pointer_grab_vector.xml index 48c01ceb6588..d9e1f18919fa 100644 --- a/core/res/res/drawable/pointer_grab_vector.xml +++ b/core/res/res/drawable/pointer_grab_vector.xml @@ -19,7 +19,7 @@      android:viewportHeight="24"      android:viewportWidth="24">      <path -        android:fillColor="#000000" +        android:fillColor="?attr/pointerIconVectorStrokeInverse"          android:pathData="M20.442 7.562a2 2 0 0 0-2-2c-.366 0-.705.106-1 .277V4.686a2 2 0 0 0-2-2 2 2 0 0 0-1.004.279 1.995 1.995 0 0 0-3.986-.06 2 2 0 0 0-1.006-.28 2 2 0 0 0-2 2v6.501l-.247-.253a2.216 2.216 0 0 0-3.178 0 2.286 2.286 0 0 0 0 3.186l5.106 5.224q.063.061.131.118l-.001.001a6.58 6.58 0 0 0 4.624 1.901c3.587 0 6.565-2.906 6.565-6.516q0-.105-.004-.21m-6.561 5.727a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-5.106-5.224a1.286 1.286 0 0 1 0-1.788 1.215 1.215 0 0 1 1.747 0l1.962 2.008V4.625a1 1 0 0 1 2 0v5.833q.463-.362.996-.623V3a1 1 0 0 1 2 0v6.29a6 6 0 0 1 1 .011V4.686a1 1 0 0 1 2 0v5.21c.357.185.693.408 1 .663V7.562a1 1 0 0 1 2 0v7.019h.001-.001q.004.104.004.207c.001 3.046-2.518 5.516-5.564 5.516" />      <path          android:fillColor="?attr/pointerIconVectorFillInverse" diff --git a/core/res/res/drawable/pointer_grabbing_vector.xml b/core/res/res/drawable/pointer_grabbing_vector.xml index ad9f86c1848c..7f52fc5f42ba 100644 --- a/core/res/res/drawable/pointer_grabbing_vector.xml +++ b/core/res/res/drawable/pointer_grabbing_vector.xml @@ -19,7 +19,7 @@      android:viewportHeight="24"      android:viewportWidth="24">      <path -        android:fillColor="#000000" +        android:fillColor="?attr/pointerIconVectorStrokeInverse"          android:pathData="M19.485 12.622V8.508a2 2 0 0 0-3.12-1.657 1.993 1.993 0 0 0-2.99-1.006 1.99 1.99 0 0 0-1.886-1.361c-.903 0-1.658.603-1.906 1.425a2 2 0 0 0-3.09 1.674v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514a5 5 0 0 0-.012-.381m-6.55 5.895a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022a5.5 5.5 0 0 1 .996.009v-.007a1 1 0 0 1 2 0v.599q.537.277 1 .66v-.259a1 1 0 0 1 2 0v4.115q.013.189.013.38c-.001 3.045-2.518 5.514-5.564 5.514" />      <path          android:fillColor="?attr/pointerIconVectorFillInverse" diff --git a/core/res/res/drawable/pointer_hand_vector.xml b/core/res/res/drawable/pointer_hand_vector.xml index a06dc08b8b3f..115b53b5d495 100644 --- a/core/res/res/drawable/pointer_hand_vector.xml +++ b/core/res/res/drawable/pointer_hand_vector.xml @@ -19,7 +19,7 @@      android:viewportHeight="24"      android:viewportWidth="24">      <path -        android:fillColor="#000000" +        android:fillColor="?attr/pointerIconVectorStrokeInverse"          android:pathData="M20.492 15.197v-4.198A1.995 1.995 0 0 0 18.5 9.001c-.413 0-.797.126-1.115.342a1.99 1.99 0 0 0-1.873-1.341c-.411 0-.792.125-1.109.339a1.99 1.99 0 0 0-1.879-1.361c-.363 0-.699.105-.992.275V3.998A1.99 1.99 0 0 0 9.542 2c-1.1 0-1.992.895-1.992 1.998v7.831l-.242-.249a2.2 2.2 0 0 0-3.164 0 2.29 2.29 0 0 0 0 3.183l5.084 5.219q.063.061.13.118l-.001.001A6.54 6.54 0 0 0 13.963 22c3.572 0 6.537-2.903 6.537-6.509q0-.148-.008-.294m-6.529 5.804a5.55 5.55 0 0 1-3.906-1.611 1 1 0 0 1-.117-.106l-5.084-5.219a1.286 1.286 0 0 1 0-1.786 1.21 1.21 0 0 1 1.74 0l1.95 2.002V3.998c0-.552.446-.999.996-.999s.996.447.996.999v7.17l.011-.007a.495.495 0 0 0 .989-.037V8.939a.992.992 0 0 1 1.984.039v.796l-.007 1.386a.5.5 0 0 0 .495.502h.003a.5.5 0 0 0 .498-.497l.006-1.157h.001V10a.997.997 0 1 1 1.991 0v.601l.004.003v1.02q.001.107.042.199a.5.5 0 0 0 .153.187l.031.021a.5.5 0 0 0 .231.083c.014.001.026.008.04.008a.5.5 0 0 0 .498-.5v-.642a.996.996 0 0 1 .993-.98c.55 0 .996.447.996.999v4.199a6 6 0 0 1 .008.293c-.001 3.043-2.509 5.51-5.542 5.51" />      <path          android:fillColor="?attr/pointerIconVectorFillInverse" diff --git a/core/res/res/drawable/pointer_handwriting_vector.xml b/core/res/res/drawable/pointer_handwriting_vector.xml index 849759291101..6f62abaca7aa 100644 --- a/core/res/res/drawable/pointer_handwriting_vector.xml +++ b/core/res/res/drawable/pointer_handwriting_vector.xml @@ -19,8 +19,8 @@      android:viewportHeight="24"      android:viewportWidth="24">      <group> -        <path android:fillColor="#FFFFFF" android:pathData="M20.12 6.8 18.7 5.38c-.57-.57-1.32-.88-2.12-.88s-1.56.31-2.13.88l-7.16 7.16-.29.29V5c0-1.1-.9-2-2-2s-2 .9-2 2v14c0 1.1.9 2 2 2s2-.9 2-2v-.5h5.67l.29-.29 7.16-7.16c.57-.57.88-1.32.88-2.12s-.31-1.57-.88-2.13M6 19c0 .55-.45 1-1 1s-1-.45-1-1V5c0-.55.45-1 1-1s1 .45 1 1zm13.41-8.66-7.16 7.16H8v-4.25l7.16-7.16c.39-.39.9-.59 1.41-.59h.01c.51 0 1.02.2 1.41.59l1.42 1.42c.78.78.78 2.05 0 2.83" /> -        <path android:fillColor="#FFFFFF" android:pathData="m16.431 7.64-6.29 6.29 1.43 1.43 6.29-6.29-1.42-1.43z" /> +        <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="M20.12 6.8 18.7 5.38c-.57-.57-1.32-.88-2.12-.88s-1.56.31-2.13.88l-7.16 7.16-.29.29V5c0-1.1-.9-2-2-2s-2 .9-2 2v14c0 1.1.9 2 2 2s2-.9 2-2v-.5h5.67l.29-.29 7.16-7.16c.57-.57.88-1.32.88-2.12s-.31-1.57-.88-2.13M6 19c0 .55-.45 1-1 1s-1-.45-1-1V5c0-.55.45-1 1-1s1 .45 1 1zm13.41-8.66-7.16 7.16H8v-4.25l7.16-7.16c.39-.39.9-.59 1.41-.59h.01c.51 0 1.02.2 1.41.59l1.42 1.42c.78.78.78 2.05 0 2.83" /> +        <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="m16.431 7.64-6.29 6.29 1.43 1.43 6.29-6.29-1.42-1.43z" />      </group>      <path          android:fillColor="?attr/pointerIconVectorFill" diff --git a/core/res/res/drawable/pointer_help_vector.xml b/core/res/res/drawable/pointer_help_vector.xml index 07970fbdd67a..63cf287080aa 100644 --- a/core/res/res/drawable/pointer_help_vector.xml +++ b/core/res/res/drawable/pointer_help_vector.xml @@ -22,7 +22,7 @@          android:fillColor="?attr/pointerIconVectorFill"          android:pathData="M16.339 11.18 6.773 4.017a1.78 1.78 0 0 0-1.072-.365c-.274 0-.551.065-.813.196a1.77 1.77 0 0 0-.994 1.611l.009 11.951c0 .903.59 1.457 1.142 1.674.2.078.433.128.678.128.434 0 .906-.155 1.298-.578l2.97-3.193a1.8 1.8 0 0 1 1.126-.563l4.335-.467c.899-.097 1.387-.741 1.544-1.312.157-.573.066-1.377-.657-1.919" />      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27A2.75 2.75 0 0 0 2.9 5.46l.01 11.95a2.79 2.79 0 0 0 2.82 2.8c.78 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92zm2.64-10.83a2.5 2.5 0 0 0-1.84-.72 3 3 0 0 0-2.83 1.93l-.39.94.96.37.86.32.12.05-.02.03c-.22.4-.3.82-.3 1.33v.94a1.56 1.56 0 0 0 .4 1.47 1.54 1.54 0 0 0 2.24.01 1.55 1.55 0 0 0 .28-1.84v-.52c0-.1.02-.17.03-.25l.16-.15c.32-.25.6-.56.78-.93.18-.37.26-.76.26-1.16 0-.68-.21-1.32-.7-1.82zm-1.5 5.96a.55.55 0 0 1-.82 0 .56.56 0 0 1-.17-.4c0-.16.06-.3.17-.4a.55.55 0 0 1 .41-.18c.15 0 .28.06.4.17a.55.55 0 0 1 0 .81zm1.05-3.42c-.1.22-.28.42-.52.6-.26.22-.42.42-.47.6-.05.18-.08.37-.08.57l-.93-.06c0-.38.07-.62.19-.86.13-.24.3-.46.54-.66.17-.13.3-.28.4-.43s.14-.3.14-.46c0-.2-.08-.37-.22-.5s-.31-.17-.52-.17c-.2 0-.39.06-.56.18-.17.13-.3.31-.4.56l-.87-.33a2.03 2.03 0 0 1 1.91-1.3c.48 0 .86.14 1.13.42.28.28.41.65.41 1.12 0 .26-.05.5-.15.72z" />      <path          android:fillColor="?attr/pointerIconVectorFill" diff --git a/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml index 32c56b6aa098..7aa65713dbf0 100644 --- a/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml +++ b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml @@ -19,7 +19,7 @@      android:viewportHeight="24"      android:viewportWidth="24">      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="m19.963 10.185-3.065-1.758c-1.327-.761-2.96.14-3.072 1.633h-3.651c-.113-1.492-1.746-2.394-3.072-1.633l-3.065 1.758c-1.383.793-1.383 2.786 0 3.579l3.065 1.758c1.311.752 2.918-.12 3.065-1.581h3.666c.147 1.46 1.754 2.333 3.065 1.581l3.065-1.758c1.382-.793 1.382-2.786-.001-3.579m-.498 2.712L16.4 14.655a1.065 1.065 0 0 1-1.596-.922v-.791H9.195v.791c0 .818-.886 1.33-1.596.922l-3.065-1.758a1.063 1.063 0 0 1 0-1.845l3.065-1.758a1.065 1.065 0 0 1 1.596.922v.843h5.609v-.843c0-.818.886-1.33 1.596-.922l3.065 1.758a1.063 1.063 0 0 1 0 1.845" />      <path          android:fillColor="?attr/pointerIconVectorFill" diff --git a/core/res/res/drawable/pointer_nodrop_vector.xml b/core/res/res/drawable/pointer_nodrop_vector.xml index 6108e9681fa5..7cef01af77af 100644 --- a/core/res/res/drawable/pointer_nodrop_vector.xml +++ b/core/res/res/drawable/pointer_nodrop_vector.xml @@ -23,7 +23,7 @@          <path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M17.5 4c-.493 0-.95.148-1.337.395l3.442 3.442C19.852 7.45 20 6.993 20 6.5 20 5.121 18.879 4 17.5 4M15 6.5C15 7.879 16.121 9 17.5 9c.525 0 1.011-.164 1.413-.441l-3.472-3.472A2.5 2.5 0 0 0 15 6.5" />      </group>      <path -        android:fillColor="#000000" +        android:fillColor="?attr/pointerIconVectorStrokeInverse"          android:pathData="M18.485 10.932v1.69q.013.188.013.38c0 3.045-2.518 5.514-5.563 5.514a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022q.22-.018.446-.018c.046 0 .09.006.135.007a4.5 4.5 0 0 1-.117-.995c0-.399.068-.779.165-1.148a1.97 1.97 0 0 0-1.629-.867c-.903 0-1.658.603-1.906 1.425a2 2 0 0 0-1.091-.327 2 2 0 0 0-2 2v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514q-.001-.192-.013-.381v-2.056a4.5 4.5 0 0 1-.999.366" />      <path          android:fillColor="#B22A25" diff --git a/core/res/res/drawable/pointer_text_vector.xml b/core/res/res/drawable/pointer_text_vector.xml index a14727309674..2fa133258ac1 100644 --- a/core/res/res/drawable/pointer_text_vector.xml +++ b/core/res/res/drawable/pointer_text_vector.xml @@ -22,6 +22,6 @@          android:fillColor="?attr/pointerIconVectorFill"          android:pathData="M12 3c-.551 0-1 .448-1 1v14a1.001 1.001 0 0 0 2 0V4c0-.552-.449-1-1-1" />      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="M12 2c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2s2-.897 2-2V4c0-1.103-.897-2-2-2m1 16a1.001 1.001 0 0 1-2 0V4a1.001 1.001 0 0 1 2 0z" />  </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml index 7f95207d9e82..cd4e5e703fa9 100644 --- a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml +++ b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml @@ -19,7 +19,7 @@      android:viewportHeight="24"      android:viewportWidth="24">      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="m18.896 16.365-.924-3.41c-.398-1.467-2.169-1.985-3.305-1.035L12.08 9.333c.952-1.136.434-2.908-1.034-3.306l-3.41-.924c-1.539-.416-2.948.993-2.532 2.532l.924 3.41c.398 1.468 2.17 1.986 3.306 1.034l2.586 2.586c-.953 1.136-.435 2.91 1.033 3.307l3.41.924c1.54.417 2.949-.992 2.533-2.531m-2.27 1.566-3.41-.924a1.065 1.065 0 0 1-.476-1.781l.579-.579-3.966-3.966-.579.579a1.066 1.066 0 0 1-1.781-.476L6.07 7.373a1.063 1.063 0 0 1 1.304-1.304l3.41.924a1.065 1.065 0 0 1 .476 1.781l-.578.578 3.966 3.966.577-.577a1.066 1.066 0 0 1 1.781.477l.924 3.41a1.062 1.062 0 0 1-1.304 1.303" />      <path          android:fillType="evenOdd" diff --git a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml index 8a3371524429..3736290c94ad 100644 --- a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml +++ b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml @@ -19,7 +19,7 @@      android:viewportHeight="24"      android:viewportWidth="24">      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="m16.365 5.104-3.41.924c-1.468.398-1.986 2.171-1.033 3.307l-2.586 2.586c-1.136-.952-2.909-.434-3.306 1.034l-.924 3.41c-.417 1.539.992 2.948 2.531 2.531l3.41-.924c1.468-.398 1.986-2.17 1.034-3.306l2.587-2.587c1.136.951 2.908.432 3.305-1.035l.924-3.41c.415-1.538-.994-2.947-2.532-2.53m1.565 2.269-.924 3.41a1.065 1.065 0 0 1-1.781.476l-.577-.577-3.966 3.966.578.578a1.066 1.066 0 0 1-.476 1.781l-3.41.924a1.063 1.063 0 0 1-1.304-1.304l.924-3.41a1.066 1.066 0 0 1 1.781-.477l.578.578 3.966-3.966-.579-.579a1.066 1.066 0 0 1 .476-1.781l3.41-.924a1.063 1.063 0 0 1 1.304 1.305" />      <path          android:fillColor="?attr/pointerIconVectorFill" diff --git a/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml index 889372c39433..ff99f4101aab 100644 --- a/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml +++ b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml @@ -19,7 +19,7 @@      android:viewportHeight="24"      android:viewportWidth="24">      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="M13.945 13.829V10.17c1.476-.131 2.363-1.75 1.606-3.069l-1.758-3.065c-.793-1.383-2.786-1.383-3.579 0L8.455 7.102c-.757 1.319.131 2.939 1.607 3.069v3.658c-1.477.13-2.364 1.75-1.607 3.069l1.758 3.065c.793 1.383 2.786 1.383 3.579 0l1.758-3.065c.758-1.319-.129-2.938-1.605-3.069m.739 2.572-1.758 3.065a1.063 1.063 0 0 1-1.845 0l-1.758-3.065a1.065 1.065 0 0 1 .922-1.596h.818v-5.61h-.818c-.818 0-1.33-.886-.922-1.596l1.758-3.065a1.063 1.063 0 0 1 1.845 0l1.758 3.065a1.065 1.065 0 0 1-.922 1.596h-.817v5.609h.817c.817.001 1.329.886.922 1.597" />      <path          android:fillColor="?attr/pointerIconVectorFill" diff --git a/core/res/res/drawable/pointer_vertical_text_vector.xml b/core/res/res/drawable/pointer_vertical_text_vector.xml index 9238f94fa808..d610d0488338 100644 --- a/core/res/res/drawable/pointer_vertical_text_vector.xml +++ b/core/res/res/drawable/pointer_vertical_text_vector.xml @@ -22,6 +22,6 @@          android:fillColor="?attr/pointerIconVectorFill"          android:pathData="M19 11H5a1 1 0 0 0 0 2h14a1 1 0 0 0 0-2" />      <path -        android:fillColor="#FFFFFF" +        android:fillColor="?attr/pointerIconVectorStroke"          android:pathData="M19 10H5c-1.103 0-2 .897-2 2s.897 2 2 2h14c1.103 0 2-.897 2-2s-.897-2-2-2m0 3H5a1 1 0 0 1 0-2h14a1 1 0 0 1 0 2" />  </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/pointer_zoom_in_vector.xml b/core/res/res/drawable/pointer_zoom_in_vector.xml index a7f56c23d5fb..c4aa95bbc384 100644 --- a/core/res/res/drawable/pointer_zoom_in_vector.xml +++ b/core/res/res/drawable/pointer_zoom_in_vector.xml @@ -19,8 +19,8 @@      android:viewportHeight="24"      android:viewportWidth="24">      <group> -        <path android:fillColor="#FFFFFF" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" /> -        <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-2v2a.5.5 0 0 1-1 0v-2h-2a.5.5 0 0 1 0-1h2V7a.5.5 0 0 1 1 0v2h2a.5.5 0 0 1 0 1" /> +        <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" /> +        <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-2v2a.5.5 0 0 1-1 0v-2h-2a.5.5 0 0 1 0-1h2V7a.5.5 0 0 1 1 0v2h2a.5.5 0 0 1 0 1" />      </group>      <group>          <path android:fillColor="?attr/pointerIconVectorFill" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" /> diff --git a/core/res/res/drawable/pointer_zoom_out_vector.xml b/core/res/res/drawable/pointer_zoom_out_vector.xml index e46b978df5b6..16c4520a8637 100644 --- a/core/res/res/drawable/pointer_zoom_out_vector.xml +++ b/core/res/res/drawable/pointer_zoom_out_vector.xml @@ -19,8 +19,8 @@      android:viewportHeight="24"      android:viewportWidth="24">      <group> -        <path android:fillColor="#FFFFFF" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" /> -        <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1" /> +        <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" /> +        <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1" />      </group>      <group>          <path android:fillColor="?attr/pointerIconVectorFill" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" /> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index ea993ee1a208..4892f59bd9fb 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -10042,6 +10042,8 @@      <declare-styleable name="PointerIconVectorTheme">          <attr name="pointerIconVectorFill" format="color" />          <attr name="pointerIconVectorFillInverse" format="color" /> +        <attr name="pointerIconVectorStroke" format="color" /> +        <attr name="pointerIconVectorStrokeInverse" format="color" />      </declare-styleable>      <declare-styleable name="Storage"> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 335b74025da3..ca80e2277ced 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4136,6 +4136,9 @@      <!-- The default value for keyboard vibration toggle in settings. -->      <bool name="config_defaultKeyboardVibrationEnabled">true</bool> +    <!-- Indicating if keyboard vibration settings supported or not. --> +    <bool name="config_keyboardVibrationSettingsSupported">false</bool> +      <!-- If the device should still vibrate even in low power mode, for certain priority vibrations       (e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->      <bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 61c7a8cde7d5..cdd8557393b8 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -393,6 +393,12 @@      <bool name="config_wait_for_device_alignment_in_demo_datagram">false</bool>      <java-symbol type="bool" name="config_wait_for_device_alignment_in_demo_datagram" /> +    <!-- Boolean indicating whether to enable MMS to be attempted on IWLAN if possible, even if +     existing cellular networks already supports IWLAN. +     --> +    <bool name="force_iwlan_mms_feature_enabled">false</bool> +    <java-symbol type="bool" name="force_iwlan_mms_feature_enabled" /> +      <!-- The time duration in millis after which Telephony will abort the last message datagram       sending requests. Telephony starts a timer when receiving a last message datagram sending       request in either OFF, IDLE, or NOT_CONNECTED state. In NOT_CONNECTED, the duration of the diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml index bd93aa9402c3..77d2e873682f 100644 --- a/core/res/res/values/locale_config.xml +++ b/core/res/res/values/locale_config.xml @@ -14,6 +14,11 @@       limitations under the License.  --> +<!-- +     ICU Version: 75.1 +     CLDR Data Version: 45 +--> +  <resources>      <string-array translatable="false" name="supported_locales"> @@ -88,6 +93,11 @@          <item>bem-ZM</item> <!-- Bemba (Zambia) -->          <item>bez-TZ</item> <!-- Bena (Tanzania) -->          <item>bg-BG</item> <!-- Bulgarian (Bulgaria) --> +        <item>bgc-IN</item> <!-- Haryanvi (India) --> +        <item>bgc-IN-u-nu-latn</item> <!-- Haryanvi (India, Western Digits) --> +        <item>bho-IN</item> <!-- Bhojpuri (India) --> +        <item>bho-IN-u-nu-latn</item> <!-- Bhojpuri (India, Western Digits) --> +        <item>blo-BJ</item> <!-- Anii (Benin) -->          <item>bm-ML</item> <!-- Bambara (Mali) -->          <item>bn-BD</item> <!-- Bangla (Bangladesh) -->          <item>bn-BD-u-nu-latn</item> <!-- Bangla (Bangladesh, Western Digits) --> @@ -108,6 +118,7 @@          <item>cgg-UG</item> <!-- Chiga (Uganda) -->          <item>chr-US</item> <!-- Cherokee (United States) -->          <item>cs-CZ</item> <!-- Czech (Czechia) --> +        <item>csw-CA</item> <!-- Swampy Cree (Canada) -->          <item>cv-RU</item> <!-- Chuvash (Russia) -->          <item>cy-GB</item> <!-- Welsh (United Kingdom) -->          <item>da-DK</item> <!-- Danish (Denmark) --> @@ -170,6 +181,7 @@          <item>en-GU</item> <!-- English (Guam) -->          <item>en-GY</item> <!-- English (Guyana) -->          <item>en-HK</item> <!-- English (Hong Kong) --> +        <item>en-ID</item> <!-- English (Indonesia) -->          <item>en-IE</item> <!-- English (Ireland) -->          <item>en-IL</item> <!-- English (Israel) -->          <item>en-IM</item> <!-- English (Isle of Man) --> @@ -237,6 +249,7 @@          <item>en-ZA</item> <!-- English (South Africa) -->          <item>en-ZM</item> <!-- English (Zambia) -->          <item>en-ZW</item> <!-- English (Zimbabwe) --> +        <item>eo-001</item> <!-- Esperanto (world) -->          <item>es-AR</item> <!-- Spanish (Argentina) -->          <item>es-BO</item> <!-- Spanish (Bolivia) -->          <item>es-BR</item> <!-- Spanish (Brazil) --> @@ -381,6 +394,7 @@          <item>hu-HU</item> <!-- Hungarian (Hungary) -->          <item>hy-AM</item> <!-- Armenian (Armenia) -->          <item>ia-001</item> <!-- Interlingua (world) --> +        <item>ie-EE</item> <!-- Interlingue (Estonia) -->          <item>ig-NG</item> <!-- Igbo (Nigeria) -->          <item>ii-CN</item> <!-- Sichuan Yi (China) -->          <item>in-ID</item> <!-- Indonesian (Indonesia) --> @@ -408,6 +422,7 @@          <item>kln-KE</item> <!-- Kalenjin (Kenya) -->          <item>km-KH</item> <!-- Khmer (Cambodia) -->          <item>kn-IN</item> <!-- Kannada (India) --> +        <item>ko-CN</item> <!-- Korean (China) -->          <item>ko-KP</item> <!-- Korean (North Korea) -->          <item>ko-KR</item> <!-- Korean (South Korea) -->          <item>kok-IN</item> <!-- Konkani (India) --> @@ -417,12 +432,19 @@          <item>ksb-TZ</item> <!-- Shambala (Tanzania) -->          <item>ksf-CM</item> <!-- Bafia (Cameroon) -->          <item>ksh-DE</item> <!-- Colognian (Germany) --> +        <item>ku-TR</item> <!-- Kurdish (Türkiye) -->          <item>kw-GB</item> <!-- Cornish (United Kingdom) --> +        <item>kxv-Deva-IN</item> <!-- Kuvi (Devanagari, India) --> +        <item>kxv-Latn-IN</item> <!-- Kuvi (Latin, India) --> +        <item>kxv-Orya-IN</item> <!-- Kuvi (Odia, India) --> +        <item>kxv-Telu-IN</item> <!-- Kuvi (Telugu, India) -->          <item>ky-KG</item> <!-- Kyrgyz (Kyrgyzstan) -->          <item>lag-TZ</item> <!-- Langi (Tanzania) -->          <item>lb-LU</item> <!-- Luxembourgish (Luxembourg) -->          <item>lg-UG</item> <!-- Ganda (Uganda) --> +        <item>lij-IT</item> <!-- Ligurian (Italy) -->          <item>lkt-US</item> <!-- Lakota (United States) --> +        <item>lmo-IT</item> <!-- Lombard (Italy) -->          <item>ln-AO</item> <!-- Lingala (Angola) -->          <item>ln-CD</item> <!-- Lingala (Congo - Kinshasa) -->          <item>ln-CF</item> <!-- Lingala (Central African Republic) --> @@ -462,6 +484,8 @@          <item>nb-NO</item> <!-- Norwegian Bokmål (Norway) -->          <item>nb-SJ</item> <!-- Norwegian Bokmål (Svalbard & Jan Mayen) -->          <item>nd-ZW</item> <!-- North Ndebele (Zimbabwe) --> +        <item>nds-DE</item> <!-- Low German (Germany) --> +        <item>nds-NL</item> <!-- Low German (Netherlands) -->          <item>ne-IN</item> <!-- Nepali (India) -->          <item>ne-IN-u-nu-latn</item> <!-- Nepali (India, Western Digits) -->          <item>ne-NP</item> <!-- Nepali (Nepal) --> @@ -475,8 +499,12 @@          <item>nl-SX</item> <!-- Dutch (Sint Maarten) -->          <item>nn-NO</item> <!-- Norwegian Nynorsk (Norway) -->          <item>nnh-CM</item> <!-- Ngiemboon (Cameroon) --> +        <item>nqo-GN</item> <!-- N’Ko (Guinea) --> +        <item>nqo-GN-u-nu-latn</item> <!-- N’Ko (Guinea, Western Digits) -->          <item>nus-SS</item> <!-- Nuer (South Sudan) -->          <item>nyn-UG</item> <!-- Nyankole (Uganda) --> +        <item>oc-ES</item> <!-- Occitan (Spain) --> +        <item>oc-FR</item> <!-- Occitan (France) -->          <item>om-ET</item> <!-- Oromo (Ethiopia) -->          <item>om-KE</item> <!-- Oromo (Kenya) -->          <item>or-IN</item> <!-- Odia (India) --> @@ -487,6 +515,7 @@          <item>pa-Guru-IN</item> <!-- Punjabi (Gurmukhi, India) -->          <item>pcm-NG</item> <!-- Nigerian Pidgin (Nigeria) -->          <item>pl-PL</item> <!-- Polish (Poland) --> +        <item>prg-PL</item> <!-- Prussian (Poland) -->          <item>ps-AF</item> <!-- Pashto (Afghanistan) -->          <item>ps-AF-u-nu-latn</item> <!-- Pashto (Afghanistan, Western Digits) -->          <item>ps-PK</item> <!-- Pashto (Pakistan) --> @@ -566,6 +595,9 @@          <item>sw-KE</item> <!-- Swahili (Kenya) -->          <item>sw-TZ</item> <!-- Swahili (Tanzania) -->          <item>sw-UG</item> <!-- Swahili (Uganda) --> +        <item>syr-IQ</item> <!-- Syriac (Iraq) --> +        <item>syr-SY</item> <!-- Syriac (Syria) --> +        <item>szl-PL</item> <!-- Silesian (Poland) -->          <item>ta-IN</item> <!-- Tamil (India) -->          <item>ta-LK</item> <!-- Tamil (Sri Lanka) -->          <item>ta-MY</item> <!-- Tamil (Malaysia) --> @@ -580,7 +612,7 @@          <item>tk-TM</item> <!-- Turkmen (Turkmenistan) -->          <item>to-TO</item> <!-- Tongan (Tonga) -->          <item>tr-CY</item> <!-- Turkish (Cyprus) --> -        <item>tr-TR</item> <!-- Turkish (Turkey) --> +        <item>tr-TR</item> <!-- Turkish (Türkiye) -->          <item>tt-RU</item> <!-- Tatar (Russia) -->          <item>twq-NE</item> <!-- Tasawaq (Niger) -->          <item>tzm-MA</item> <!-- Central Atlas Tamazight (Morocco) --> @@ -594,11 +626,14 @@          <item>uz-Arab-AF-u-nu-latn</item> <!-- Uzbek (Arabic, Afghanistan, Western Digits) -->          <item>uz-Cyrl-UZ</item> <!-- Uzbek (Cyrillic, Uzbekistan) -->          <item>uz-Latn-UZ</item> <!-- Uzbek (Latin, Uzbekistan) --> +        <item>vec-IT</item> <!-- Venetian (Italy) -->          <item>vi-VN</item> <!-- Vietnamese (Vietnam) --> +        <item>vmw-MZ</item> <!-- Makhuwa (Mozambique) -->          <item>vun-TZ</item> <!-- Vunjo (Tanzania) -->          <item>wae-CH</item> <!-- Walser (Switzerland) -->          <item>wo-SN</item> <!-- Wolof (Senegal) -->          <item>xh-ZA</item> <!-- Xhosa (South Africa) --> +        <item>xnr-IN</item> <!-- Kangri (India) -->          <item>xog-UG</item> <!-- Soga (Uganda) -->          <item>yav-CM</item> <!-- Yangben (Cameroon) -->          <item>yo-BJ</item> <!-- Yoruba (Benin) --> @@ -608,6 +643,7 @@          <item>yrl-VE</item> <!-- Nheengatu (Venezuela) -->          <item>yue-Hans-CN</item> <!-- Cantonese (Simplified, China) -->          <item>yue-Hant-HK</item> <!-- Cantonese (Traditional, Hong Kong) --> +        <item>za-CN</item> <!-- Zhuang (China) -->          <item>zgh-MA</item> <!-- Standard Moroccan Tamazight (Morocco) -->          <item>zh-Hans-CN</item> <!-- Chinese (Simplified, China) -->          <item>zh-Hans-HK</item> <!-- Chinese (Simplified, Hong Kong) --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 50c3b1a93d75..aabc8ca5aef6 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1527,6 +1527,24 @@ please see styles_device_defaults.xml.      </style>      <!-- @hide --> +    <style name="PointerIconVectorStyleStrokeWhite"> +        <item name="pointerIconVectorStroke">@color/white</item> +        <item name="pointerIconVectorStrokeInverse">@color/black</item> +    </style> + +    <!-- @hide --> +    <style name="PointerIconVectorStyleStrokeBlack"> +        <item name="pointerIconVectorStroke">@color/black</item> +        <item name="pointerIconVectorStrokeInverse">@color/white</item> +    </style> + +    <!-- @hide --> +    <style name="PointerIconVectorStyleStrokeNone"> +        <item name="pointerIconVectorStroke">@color/transparent</item> +        <item name="pointerIconVectorStrokeInverse">@color/transparent</item> +    </style> + +    <!-- @hide -->      <style name="aerr_list_item" parent="Widget.Material.Light.Button.Borderless">          <item name="minHeight">?attr/listPreferredItemHeightSmall</item>          <item name="textAppearance">?attr/textAppearanceListItemSmall</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7d50d227ee13..9661b4638718 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1704,6 +1704,10 @@    <java-symbol type="style" name="PointerIconVectorStyleFillPink" />    <java-symbol type="style" name="PointerIconVectorStyleFillBlue" />    <java-symbol type="attr" name="pointerIconVectorFill" /> +  <java-symbol type="style" name="PointerIconVectorStyleStrokeWhite" /> +  <java-symbol type="style" name="PointerIconVectorStyleStrokeBlack" /> +  <java-symbol type="style" name="PointerIconVectorStyleStrokeNone" /> +  <java-symbol type="attr" name="pointerIconVectorStroke" />    <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Title" />    <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Info" /> @@ -2122,6 +2126,7 @@    <java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />    <java-symbol type="dimen" name="config_keyboardHapticFeedbackFixedAmplitude" />    <java-symbol type="bool" name="config_defaultKeyboardVibrationEnabled" /> +  <java-symbol type="bool" name="config_keyboardVibrationSettingsSupported" />    <java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />    <java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" />    <java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" /> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index 612b38762d2d..9ea2943bc6da 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -50,6 +50,7 @@ import androidx.annotation.NonNull;  import androidx.annotation.Nullable;  import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags;  import java.util.Map;  import java.util.concurrent.Executor; @@ -393,19 +394,31 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {          if (splitAttributes == null) {              return TaskFragmentAnimationParams.DEFAULT;          } +        final TaskFragmentAnimationParams.Builder builder = +                new TaskFragmentAnimationParams.Builder();          final int animationBackgroundColor = getAnimationBackgroundColor(splitAttributes); -        TaskFragmentAnimationParams.Builder builder = new TaskFragmentAnimationParams.Builder(); -        if (animationBackgroundColor != DEFAULT_ANIMATION_BACKGROUND_COLOR) { -            builder.setAnimationBackgroundColor(animationBackgroundColor); +        builder.setAnimationBackgroundColor(animationBackgroundColor); +        if (Flags.activityEmbeddingAnimationCustomizationFlag()) { +            final int openAnimationResId = +                    splitAttributes.getAnimationParams().getOpenAnimationResId(); +            builder.setOpenAnimationResId(openAnimationResId); +            final int closeAnimationResId = +                    splitAttributes.getAnimationParams().getCloseAnimationResId(); +            builder.setCloseAnimationResId(closeAnimationResId); +            final int changeAnimationResId = +                    splitAttributes.getAnimationParams().getChangeAnimationResId(); +            builder.setChangeAnimationResId(changeAnimationResId);          } -        // TODO(b/293658614): Allow setting custom open/close/changeAnimationResId.          return builder.build();      }      @ColorInt      private static int getAnimationBackgroundColor(@NonNull SplitAttributes splitAttributes) {          int animationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR; -        final AnimationBackground animationBackground = splitAttributes.getAnimationBackground(); +        AnimationBackground animationBackground = splitAttributes.getAnimationBackground(); +        if (Flags.activityEmbeddingAnimationCustomizationFlag()) { +            animationBackground = splitAttributes.getAnimationParams().getAnimationBackground(); +        }          if (animationBackground instanceof AnimationBackground.ColorBackground colorBackground) {              animationBackgroundColor = colorBackground.getColor();          } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java index 4267749dfa6b..c5aaddc4e0ed 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java @@ -29,6 +29,7 @@ import android.view.WindowManager;  import androidx.test.ext.junit.runners.AndroidJUnit4;  import androidx.test.filters.SmallTest;  import androidx.window.extensions.embedding.AnimationBackground; +import androidx.window.extensions.embedding.AnimationParams;  import androidx.window.extensions.embedding.SplitAttributes;  import org.junit.Before; @@ -112,5 +113,13 @@ public class WindowExtensionsTest {                  .isEqualTo(new SplitAttributes.SplitType.RatioSplitType(0.5f));          assertThat(splitAttributes.getAnimationBackground())                  .isEqualTo(AnimationBackground.ANIMATION_BACKGROUND_DEFAULT); +        assertThat(splitAttributes.getAnimationParams().getAnimationBackground()) +                .isEqualTo(AnimationBackground.ANIMATION_BACKGROUND_DEFAULT); +        assertThat(splitAttributes.getAnimationParams().getOpenAnimationResId()) +                .isEqualTo(AnimationParams.DEFAULT_ANIMATION_RESOURCES_ID); +        assertThat(splitAttributes.getAnimationParams().getCloseAnimationResId()) +                .isEqualTo(AnimationParams.DEFAULT_ANIMATION_RESOURCES_ID); +        assertThat(splitAttributes.getAnimationParams().getChangeAnimationResId()) +                .isEqualTo(AnimationParams.DEFAULT_ANIMATION_RESOURCES_ID);      }  } diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp index c6dbd9b25e7f..1871203c7600 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp @@ -22,6 +22,40 @@ package {      default_team: "trendy_team_multitasking_windowing",  } +android_app { +    name: "WMShellRobolectricScreenshotTestApp", +    platform_apis: true, +    certificate: "platform", +    static_libs: [ +        "WindowManager-Shell", +        "platform-screenshot-diff-core", +    ], +    asset_dirs: ["goldens/robolectric"], +    manifest: "AndroidManifestRobolectric.xml", +    use_resource_processor: true, +} + +android_robolectric_test { +    name: "WMShellRobolectricScreenshotTests", +    instrumentation_for: "WMShellRobolectricScreenshotTestApp", +    upstream: true, +    java_resource_dirs: [ +        "robolectric/config", +    ], +    srcs: [ +        "src/**/*.kt", +    ], +    static_libs: [ +        "junit", +        "androidx.test.runner", +        "androidx.test.rules", +        "androidx.test.ext.junit", +        "truth", +        "platform-parametric-runner-lib", +    ], +    auto_gen_config: true, +} +  android_test {      name: "WMShellMultivalentScreenshotTestsOnDevice",      srcs: [ diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml index a7a3f1313a9b..b4bdaeaf0eac 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml @@ -16,7 +16,6 @@  <manifest xmlns:android="http://schemas.android.com/apk/res/android"      package="com.android.wm.shell.multivalentscreenshot">      <application android:debuggable="true" android:supportsRtl="true"> -        <uses-library android:name="android.test.runner" />          <activity              android:name="platform.test.screenshot.ScreenshotActivity"              android:exported="true"> diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.pngBinary files differ new file mode 100644 index 000000000000..723c6b8d9c93 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.pngBinary files differ new file mode 100644 index 000000000000..723c6b8d9c93 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties b/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties index 7a0527ccaafb..d50d976c9e84 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties @@ -1,2 +1,3 @@  sdk=NEWEST_SDK +graphicsMode=NATIVE 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 dfc9c82d85ff..8f7fdd6a7b84 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 @@ -37,15 +37,12 @@ enum class DesktopModeFlags(    // All desktop mode related flags will be added here    DESKTOP_WINDOWING_MODE(DesktopModeStatus::isDesktopModeFlagEnabled, true); -  private val TAG = "DesktopModeFlags" - -  // Cache for toggle override, which is initialized once on its first access. It needs to be refreshed -  // only on reboots as overridden state takes effect on reboots. +  // Local cache for toggle override, which is initialized once on its first access. It needs to be +  // refreshed only on reboots as overridden state takes effect on reboots.    private var cachedToggleOverride: ToggleOverride? = null    /** -   * Determines state of flag based on the actual flag and desktop mode developer option -   * overrides. +   * Determines state of flag based on the actual flag and desktop mode developer option overrides.     *     * Note, this method makes sure that a constant developer toggle overrides is read until reboot.     */ @@ -63,34 +60,44 @@ enum class DesktopModeFlags(        }    private fun getToggleOverride(context: Context): ToggleOverride { -    val override = cachedToggleOverride ?: run { -      // Cache toggle override the first time we encounter context. Override does not change -      // with context, as context is just used to fetch a system property. +    val override = +        cachedToggleOverride +            ?: run { +              val override = getToggleOverrideFromSystem(context) +              // Cache toggle override the first time we encounter context. Override does not change +              // with context, as context is just used to fetch System Property and Settings.Global +              cachedToggleOverride = override +              Log.d(TAG, "Toggle override initialized to: $override") +              override +            } -      // TODO(b/348193756): Cache a persistent value for Settings.Global until reboot. Current -      //  cache will change with process restart. -      val toggleOverride = -          Settings.Global.getInt( -              context.contentResolver, -              Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, -              ToggleOverride.OVERRIDE_UNSET.setting) +    return override +  } -      val newOverride = -          settingToToggleOverrideMap[toggleOverride] -              ?: run { -                Log.w(TAG, "Unknown toggleOverride $toggleOverride") -                ToggleOverride.OVERRIDE_UNSET -              } -      cachedToggleOverride = newOverride -      Log.d(TAG, "Toggle override initialized to: $newOverride") -      newOverride -    } +  private fun getToggleOverrideFromSystem(context: Context): ToggleOverride { +    // A non-persistent System Property is used to store override to ensure it remains +    // constant till reboot. +    val overrideFromSystemProperties: ToggleOverride? = +        System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null).convertToToggleOverride() +    return overrideFromSystemProperties +        ?: run { +          // Read Setting Global if System Property is not present (just after reboot) +          // or not valid (user manually changed the value) +          val overrideFromSettingsGlobal = +              Settings.Global.getInt( +                      context.contentResolver, +                      Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, +                      ToggleOverride.OVERRIDE_UNSET.setting) +                  .convertToToggleOverrideWithFallback(ToggleOverride.OVERRIDE_UNSET) +          // Initialize System Property +          System.setProperty( +              SYSTEM_PROPERTY_OVERRIDE_KEY, overrideFromSettingsGlobal.setting.toString()) -    return override +          overrideFromSettingsGlobal +        }    } -  // TODO(b/348193756): Share ToggleOverride enum with Settings -  // 'DesktopModePreferenceController' +  // TODO(b/348193756): Share ToggleOverride enum with Settings 'DesktopModePreferenceController'    /**     * Override state of desktop mode developer option toggle.     * @@ -107,4 +114,33 @@ enum class DesktopModeFlags(    }    private val settingToToggleOverrideMap = ToggleOverride.entries.associateBy { it.setting } + +  private fun String?.convertToToggleOverride(): ToggleOverride? { +    val intValue = this?.toIntOrNull() ?: return null +    return settingToToggleOverrideMap[intValue] +        ?: run { +          Log.w(TAG, "Unknown toggleOverride int $intValue") +          null +        } +  } + +  private fun Int.convertToToggleOverrideWithFallback( +      fallbackOverride: ToggleOverride +  ): ToggleOverride { +    return settingToToggleOverrideMap[this] +        ?: run { +          Log.w(TAG, "Unknown toggleOverride int $this") +          fallbackOverride +        } +  } + +  private companion object { +    const val TAG = "DesktopModeFlags" + +    /** +     * Key for non-persistent System Property which is used to store desktop windowing developer +     * option overrides. +     */ +    const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override" +  }  } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java index d270d2b4ccf1..5696a544152c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java @@ -266,6 +266,9 @@ class ActivityEmbeddingAnimationRunner {              final Animation animation =                      animationProvider.get(info, change, openingWholeScreenBounds);              if (shouldUseJumpCutForAnimation(animation)) { +                if (Flags.activityEmbeddingAnimationCustomizationFlag()) { +                    return new ArrayList<>(); +                }                  continue;              }              final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( @@ -291,6 +294,9 @@ class ActivityEmbeddingAnimationRunner {              final Animation animation =                      animationProvider.get(info, change, closingWholeScreenBounds);              if (shouldUseJumpCutForAnimation(animation)) { +                if (Flags.activityEmbeddingAnimationCustomizationFlag()) { +                    return new ArrayList<>(); +                }                  continue;              }              final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java index f49b90d08a75..3046307702c2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java @@ -97,7 +97,7 @@ class ActivityEmbeddingAnimationSpec {      Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo info,              @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {          if (Flags.activityEmbeddingAnimationCustomizationFlag()) { -            final Animation customAnimation = loadCustomAnimation(info, change); +            final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);              if (customAnimation != null) {                  return customAnimation;              } @@ -131,7 +131,7 @@ class ActivityEmbeddingAnimationSpec {      Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo info,              @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {          if (Flags.activityEmbeddingAnimationCustomizationFlag()) { -            final Animation customAnimation = loadCustomAnimation(info, change); +            final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);              if (customAnimation != null) {                  return customAnimation;              } @@ -172,7 +172,7 @@ class ActivityEmbeddingAnimationSpec {              // TODO(b/293658614): Support more complicated animations that may need more than a noop              // animation as the start leash.              final Animation noopAnimation = createNoopAnimation(change); -            final Animation customAnimation = loadCustomAnimation(info, change); +            final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);              if (customAnimation != null) {                  return new Animation[]{noopAnimation, customAnimation};              } @@ -227,7 +227,7 @@ class ActivityEmbeddingAnimationSpec {      Animation loadOpenAnimation(@NonNull TransitionInfo info,              @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {          final boolean isEnter = TransitionUtil.isOpeningType(change.getMode()); -        final Animation customAnimation = loadCustomAnimation(info, change); +        final Animation customAnimation = loadCustomAnimation(info, change, change.getMode());          final Animation animation;          if (customAnimation != null) {              animation = customAnimation; @@ -254,7 +254,7 @@ class ActivityEmbeddingAnimationSpec {      Animation loadCloseAnimation(@NonNull TransitionInfo info,              @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {          final boolean isEnter = TransitionUtil.isOpeningType(change.getMode()); -        final Animation customAnimation = loadCustomAnimation(info, change); +        final Animation customAnimation = loadCustomAnimation(info, change, change.getMode());          final Animation animation;          if (customAnimation != null) {              animation = customAnimation; @@ -287,14 +287,14 @@ class ActivityEmbeddingAnimationSpec {      @Nullable      private Animation loadCustomAnimation(@NonNull TransitionInfo info, -            @NonNull TransitionInfo.Change change) { +            @NonNull TransitionInfo.Change change, @WindowManager.TransitionType int mode) {          final TransitionInfo.AnimationOptions options;          if (Flags.moveAnimationOptionsToChange()) {              options = change.getAnimationOptions();          } else {              options = info.getAnimationOptions();          } -        return loadCustomAnimationFromOptions(options, change.getMode()); +        return loadCustomAnimationFromOptions(options, mode);      }      @Nullable @@ -319,8 +319,14 @@ class ActivityEmbeddingAnimationSpec {              return null;          } -        final Animation anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(), -                resId); +        final Animation anim; +        if (Flags.activityEmbeddingAnimationCustomizationFlag()) { +            // TODO(b/293658614): Consider allowing custom animations from non-default packages. +            // Enforce limiting to animations from the default "android" package for now. +            anim = mTransitionAnimation.loadDefaultAnimationRes(resId); +        } else { +            anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(), resId); +        }          if (anim != null) {              return anim;          } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt index 6781d08c9904..d1b2347a4411 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt @@ -19,6 +19,16 @@  package com.android.wm.shell.compatui  import android.app.TaskInfo -fun isSingleTopActivityTranslucent(task: TaskInfo) = -    task.isTopActivityTransparent && task.numActivities == 1 +import android.content.Context +import com.android.internal.R +// TODO(b/347289970): Consider replacing with API +fun isTopActivityExemptFromDesktopWindowing(context: Context, task: TaskInfo) = +    isSystemUiTask(context, task) || (task.isTopActivityTransparent && task.numActivities == 1 +            && !task.isTopActivityStyleFloating) + +private fun isSystemUiTask(context: Context, task: TaskInfo): Boolean { +    val sysUiPackageName: String = +        context.resources.getString(R.string.config_systemUi) +    return task.baseActivity?.packageName == sysUiPackageName +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index da1d6daff8c6..e792f7ad4263 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -24,7 +24,6 @@ import android.os.Handler;  import android.os.UserManager;  import android.view.Choreographer;  import android.view.IWindowManager; -import android.view.SurfaceControl;  import android.view.WindowManager;  import com.android.internal.jank.InteractionJankMonitor; @@ -404,8 +403,7 @@ public abstract class WMShellModule {              Optional<RecentTasksController> recentTasksController,              HomeTransitionObserver homeTransitionObserver) {          return new RecentsTransitionHandler(shellInit, transitions, -                recentTasksController.orElse(null), homeTransitionObserver, -                SurfaceControl.Transaction::new); +                recentTasksController.orElse(null), homeTransitionObserver);      }      // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 4868a2f2f905..e24791292085 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -66,7 +66,7 @@ import com.android.wm.shell.common.SyncTransactionQueue  import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource  import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT  import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT -import com.android.wm.shell.compatui.isSingleTopActivityTranslucent +import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing  import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener  import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener  import com.android.wm.shell.draganddrop.DragAndDropController @@ -158,8 +158,6 @@ class DesktopTasksController(                  visualIndicator = null              }          } -    private val sysUIPackageName = context.resources.getString( -        com.android.internal.R.string.config_systemUi)      private val transitionAreaHeight          get() = @@ -219,11 +217,6 @@ class DesktopTasksController(          return visualIndicator      } -    // TODO(b/347289970): Consider replacing with API -    private fun isSystemUIApplication(taskInfo: RunningTaskInfo): Boolean { -        return taskInfo.baseActivity?.packageName == sysUIPackageName -    } -      fun setOnTaskResizeAnimationListener(listener: OnTaskResizeAnimationListener) {          toggleResizeDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener)          enterDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener) @@ -351,19 +344,12 @@ class DesktopTasksController(          wct: WindowContainerTransaction = WindowContainerTransaction(),          transitionSource: DesktopModeTransitionSource,      ) { -        if (Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)) { -            KtProtoLog.w( -                WM_SHELL_DESKTOP_MODE, -                "DesktopTasksController: Cannot enter desktop, " + -                    "translucent top activity found. This is likely a modal dialog." -            ) -            return -        } -        if (isSystemUIApplication(task)) { +        if (Flags.enableDesktopWindowingModalsPolicy() +            && isTopActivityExemptFromDesktopWindowing(context, task)) {              KtProtoLog.w(                  WM_SHELL_DESKTOP_MODE,                  "DesktopTasksController: Cannot enter desktop, " + -                        "systemUI top activity found." +                        "ineligible top activity found."              )              return          } @@ -942,10 +928,8 @@ class DesktopTasksController(                  when {                      // Check if the closing task needs to be handled                      TransitionUtil.isClosingType(request.type) -> handleTaskClosing(task) -                    // Check if the task has a top transparent activity -                    shouldLaunchAsModal(task) -> handleIncompatibleTaskLaunch(task) -                    // Check if the task has a top systemUI activity -                    isSystemUIApplication(task) -> handleIncompatibleTaskLaunch(task) +                    // Check if the top task shouldn't be allowed to enter desktop mode +                    isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task)                      // Check if fullscreen task should be updated                      task.isFullscreen -> handleFullscreenTaskLaunch(task, transition)                      // Check if freeform task should be updated @@ -979,9 +963,9 @@ class DesktopTasksController(              .forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }      } -    // TODO(b/347289970): Consider replacing with API -    private fun shouldLaunchAsModal(task: TaskInfo) = -        Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task) +    private fun isIncompatibleTask(task: TaskInfo) = +        Flags.enableDesktopWindowingModalsPolicy() +                && isTopActivityExemptFromDesktopWindowing(context, task)      private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean {          return Flags.enableDesktopWindowingWallpaperActivity() && diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index e46625debcc6..234b4d0f86db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -74,7 +74,6 @@ import com.android.wm.shell.transition.Transitions;  import java.util.ArrayList;  import java.util.function.Consumer; -import java.util.function.Supplier;  /**   * Handles the Recents (overview) animation. Only one of these can run at a time. A recents @@ -85,7 +84,6 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {      private final Transitions mTransitions;      private final ShellExecutor mExecutor; -    private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;      @Nullable      private final RecentTasksController mRecentTasksController;      private IApplicationThread mAnimApp = null; @@ -103,13 +101,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {      public RecentsTransitionHandler(ShellInit shellInit, Transitions transitions,              @Nullable RecentTasksController recentTasksController, -            HomeTransitionObserver homeTransitionObserver, -            Supplier<SurfaceControl.Transaction> transactionSupplier) { +            HomeTransitionObserver homeTransitionObserver) {          mTransitions = transitions;          mExecutor = transitions.getMainExecutor();          mRecentTasksController = recentTasksController;          mHomeTransitionObserver = homeTransitionObserver; -        mTransactionSupplier = transactionSupplier;          if (!Transitions.ENABLE_SHELL_TRANSITIONS) return;          if (recentTasksController == null) return;          shellInit.addInitCallback(() -> { @@ -1060,7 +1056,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {              final Transitions.TransitionFinishCallback finishCB = mFinishCB;              mFinishCB = null; -            SurfaceControl.Transaction t = mFinishTransaction; +            final SurfaceControl.Transaction t = mFinishTransaction;              final WindowContainerTransaction wct = new WindowContainerTransaction();              if (mKeyguardLocked && mRecentsTask != null) { @@ -1110,16 +1106,6 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {                      }                  }                  ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "  normal finish"); -                if (toHome && !mOpeningTasks.isEmpty()) { -                    // Attempting to start a task after swipe to home, don't show it, -                    // move recents to top -                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, -                            "  attempting to start a task after swipe to home"); -                    t = mTransactionSupplier.get(); -                    wct.reorder(mRecentsTask, true /*onTop*/); -                    mClosingTasks.addAll(mOpeningTasks); -                    mOpeningTasks.clear(); -                }                  // The general case: committing to recents, going home, or switching tasks.                  for (int i = 0; i < mOpeningTasks.size(); ++i) {                      t.show(mOpeningTasks.get(i).mTaskSurface); @@ -1188,10 +1174,6 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {                      mPipTransaction = null;                  }              } -            if (t != mFinishTransaction) { -                // apply after merges because these changes are accounting for finishWCT changes. -                mTransitions.setAfterMergeFinishTransaction(mTransition, t); -            }              cleanUp();              finishCB.onTransitionFinished(wct.isEmpty() ? null : wct);              if (runnerFinishCb != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index b8abf8f2a3c0..21307a2edae0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -77,6 +77,7 @@ import androidx.annotation.BinderThread;  import com.android.internal.R;  import com.android.internal.annotations.VisibleForTesting;  import com.android.internal.protolog.ProtoLog; +import com.android.window.flags.Flags;  import com.android.wm.shell.RootTaskDisplayAreaOrganizer;  import com.android.wm.shell.ShellTaskOrganizer;  import com.android.wm.shell.common.DisplayController; @@ -242,13 +243,6 @@ public class Transitions implements RemoteCallable<Transitions>,          /** Ordered list of transitions which have been merged into this one. */          private ArrayList<ActiveTransition> mMerged; -        /** -         * @deprecated DO NOT USE THIS unless absolutely necessary. It will be removed once -         * everything migrates off finishWCT. -         */ -        @java.lang.Deprecated -        SurfaceControl.Transaction mAfterMergeFinishT; -          ActiveTransition(IBinder token) {              mToken = token;          } @@ -586,6 +580,14 @@ public class Transitions implements RemoteCallable<Transitions>,          final boolean isOpening = isOpeningType(transitType);          final boolean isClosing = isClosingType(transitType);          final int mode = change.getMode(); +        // Ensure wallpapers stay in the back +        if (change.hasFlags(FLAG_IS_WALLPAPER) && Flags.ensureWallpaperInTransitions()) { +            if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { +                return -zSplitLine + numChanges - i; +            } else { +                return -zSplitLine - i; +            } +        }          // Put all the OPEN/SHOW on top          if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {              if (isOpening) { @@ -1034,20 +1036,6 @@ public class Transitions implements RemoteCallable<Transitions>,          return null;      } -    /** @deprecated */ -    @java.lang.Deprecated -    public void setAfterMergeFinishTransaction(IBinder transition, -            SurfaceControl.Transaction afterMergeFinishT) { -        final ActiveTransition at = mKnownTransitions.get(transition); -        if (at == null) return; -        if (at.mAfterMergeFinishT != null) { -            Log.e(TAG, "Setting after-merge-t >1 time on transition: " + at.mInfo.getDebugId()); -            at.mAfterMergeFinishT.merge(afterMergeFinishT); -            return; -        } -        at.mAfterMergeFinishT = afterMergeFinishT; -    } -      /** Aborts a transition. This will still queue it up to maintain order. */      private void onAbort(ActiveTransition transition) {          final Track track = mTracks.get(transition.getTrack()); @@ -1108,7 +1096,6 @@ public class Transitions implements RemoteCallable<Transitions>,          }          // Merge all associated transactions together          SurfaceControl.Transaction fullFinish = active.mFinishT; -        SurfaceControl.Transaction afterMergeFinish = active.mAfterMergeFinishT;          if (active.mMerged != null) {              for (int iM = 0; iM < active.mMerged.size(); ++iM) {                  final ActiveTransition toMerge = active.mMerged.get(iM); @@ -1128,21 +1115,6 @@ public class Transitions implements RemoteCallable<Transitions>,                          fullFinish.merge(toMerge.mFinishT);                      }                  } -                if (toMerge.mAfterMergeFinishT != null) { -                    if (afterMergeFinish == null) { -                        afterMergeFinish = toMerge.mAfterMergeFinishT; -                    } else { -                        afterMergeFinish.merge(toMerge.mAfterMergeFinishT); -                    } -                    toMerge.mAfterMergeFinishT = null; -                } -            } -        } -        if (afterMergeFinish != null) { -            if (fullFinish == null) { -                fullFinish = afterMergeFinish; -            } else { -                fullFinish.merge(afterMergeFinish);              }          }          if (fullFinish != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index e3aa31f7e286..440fc4d314bb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -32,7 +32,7 @@ import static android.view.WindowInsets.Type.statusBars;  import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;  import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.compatui.AppCompatUtils.isSingleTopActivityTranslucent; +import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing;  import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;  import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;  import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; @@ -105,7 +105,6 @@ import com.android.wm.shell.windowdecor.extension.TaskInfoKt;  import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;  import java.io.PrintWriter; -import java.util.Objects;  import java.util.Optional;  import java.util.function.Supplier; @@ -1034,12 +1033,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {                  && taskInfo.isFocused) {              return false;          } -        // TODO(b/347289970): Consider replacing with API          if (Flags.enableDesktopWindowingModalsPolicy() -                && isSingleTopActivityTranslucent(taskInfo)) { -            return false; -        } -        if (isSystemUIApplication(taskInfo)) { +                && isTopActivityExemptFromDesktopWindowing(mContext, taskInfo)) {              return false;          }          return DesktopModeStatus.canEnterDesktopMode(mContext) @@ -1118,14 +1113,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {                  && mSplitScreenController.isTaskInSplitScreen(taskId);      } -    // TODO(b/347289970): Consider replacing with API -    private boolean isSystemUIApplication(RunningTaskInfo taskInfo) { -        if (taskInfo.baseActivity != null) { -            return (Objects.equals(taskInfo.baseActivity.getPackageName(), mSysUIPackageName)); -        } -        return false; -    } -      private void dump(PrintWriter pw, String prefix) {          final String innerPrefix = prefix + "  ";          pw.println(prefix + "DesktopModeWindowDecorViewModel"); diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt index a0a61fe2cf72..d0e8215e662e 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt @@ -117,12 +117,10 @@ class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(fli      /**       * Checks that all parts of the screen are covered at the start and end of the transition -     * -     * TODO b/197726599 Prevents all states from being checked       */      @Presubmit      @Test -    fun entireScreenCoveredAtStartAndEnd() = flicker.entireScreenCovered(allStates = false) +    fun entireScreenCoveredAtStartAndEnd() = flicker.entireScreenCovered()      /** Checks [pipApp] window remains visible and on top throughout the transition */      @Presubmit diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt index 4cd2a366f5eb..ecaf970ae389 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt @@ -16,8 +16,10 @@  package com.android.wm.shell.compatui +import android.content.ComponentName  import android.testing.AndroidTestingRunner  import androidx.test.filters.SmallTest +import com.android.internal.R  import com.android.wm.shell.ShellTestCase  import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask  import org.junit.Assert.assertFalse @@ -34,26 +36,55 @@ import org.junit.runner.RunWith  @RunWith(AndroidTestingRunner::class)  @SmallTest  class AppCompatUtilsTest : ShellTestCase() { -      @Test -    fun testIsSingleTopActivityTranslucent() { -        assertTrue(isSingleTopActivityTranslucent( +    fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent() { +        assertTrue(isTopActivityExemptFromDesktopWindowing(mContext,              createFreeformTask(/* displayId */ 0)                      .apply {                          isTopActivityTransparent = true                          numActivities = 1                      })) -        assertFalse(isSingleTopActivityTranslucent( +        assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,              createFreeformTask(/* displayId */ 0)                      .apply {                          isTopActivityTransparent = true                          numActivities = 0                      })) -        assertFalse(isSingleTopActivityTranslucent( +    } + +    @Test +    fun testIsTopActivityExemptFromDesktopWindowing_singleTopActivity() { +        assertTrue(isTopActivityExemptFromDesktopWindowing(mContext, +            createFreeformTask(/* displayId */ 0) +                    .apply { +                        isTopActivityTransparent = true +                        numActivities = 1 +                    })) +        assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,              createFreeformTask(/* displayId */ 0)                      .apply {                          isTopActivityTransparent = false                          numActivities = 1                      }))      } -}
\ No newline at end of file + +    @Test +    fun testIsTopActivityExemptFromDesktopWindowing__topActivityStyleFloating() { +        assertFalse(isTopActivityExemptFromDesktopWindowing(mContext, +            createFreeformTask(/* displayId */ 0) +                    .apply { +                        isTopActivityStyleFloating = true +                    })) +    } + +    @Test +    fun testIsTopActivityExemptFromDesktopWindowing_systemUiTask() { +        val systemUIPackageName = context.resources.getString(R.string.config_systemUi) +        val baseComponent = ComponentName(systemUIPackageName, /* class */ "") +        assertTrue(isTopActivityExemptFromDesktopWindowing(mContext, +            createFreeformTask(/* displayId */ 0) +                    .apply { +                        baseActivity = baseComponent +                    })) +    } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 8c7de5c24bb8..bd38d360a15b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -700,19 +700,37 @@ class DesktopTasksControllerTest : ShellTestCase() {    }    @Test -  fun moveToDesktop_topActivityTranslucent_doesNothing() { -    setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) +  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) +  fun moveToDesktop_topActivityTranslucentWithStyleFloating_taskIsMovedToDesktop() {      val task = -        setUpFullscreenTask().apply { -          isTopActivityTransparent = true -          numActivities = 1 -        } +      setUpFullscreenTask().apply { +        isTopActivityTransparent = true +        isTopActivityStyleFloating = true +        numActivities = 1 +      } + +    controller.moveToDesktop(task, transitionSource = UNKNOWN) + +    val wct = getLatestEnterDesktopWct() +    assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM) +  } + +  @Test +  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) +  fun moveToDesktop_topActivityTranslucentWithoutStyleFloating_doesNothing() { +    val task = +      setUpFullscreenTask().apply { +        isTopActivityTransparent = true +        isTopActivityStyleFloating = false +        numActivities = 1 +      }      controller.moveToDesktop(task, transitionSource = UNKNOWN)      verifyEnterDesktopWCTNotExecuted()    }    @Test +  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)    fun moveToDesktop_systemUIActivity_doesNothing() {      val task = setUpFullscreenTask() @@ -1374,20 +1392,40 @@ class DesktopTasksControllerTest : ShellTestCase() {    }    @Test -  fun handleRequest_shouldLaunchAsModal_returnSwitchToFullscreenWCT() { -    setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) +  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) +  fun handleRequest_topActivityTransparentWithStyleFloating_returnSwitchToFreeformWCT() { +    val freeformTask = setUpFreeformTask() +    markTaskVisible(freeformTask) +      val task = -        setUpFreeformTask().apply { -          isTopActivityTransparent = true -          numActivities = 1 -        } +      setUpFullscreenTask().apply { +        isTopActivityTransparent = true +        isTopActivityStyleFloating = true +        numActivities = 1 +      }      val result = controller.handleRequest(Binder(), createTransition(task))      assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode) -        .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN +            .isEqualTo(WINDOWING_MODE_FREEFORM) +  } + +  @Test +  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) +  fun handleRequest_topActivityTransparentWithoutStyleFloating_returnSwitchToFullscreenWCT() { +    val task = +      setUpFreeformTask().apply { +        isTopActivityTransparent = true +        isTopActivityStyleFloating = false +        numActivities = 1 +      } + +    val result = controller.handleRequest(Binder(), createTransition(task)) +    assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode) +            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN    }    @Test +  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)    fun handleRequest_systemUIActivity_returnSwitchToFullscreenWCT() {      val task = setUpFreeformTask() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt index 115b218f2e82..17983b27f4e8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt @@ -82,7 +82,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_unsetOverride_featureFlagOn_returnsTrue() { +  fun isEnabled_overrideUnset_featureFlagOn_returnsTrue() {      setOverride(OVERRIDE_UNSET.setting)      // For overridableFlag, for unset overrides, follow flag @@ -92,7 +92,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_unsetOverride_featureFlagOff_returnsFalse() { +  fun isEnabled_overrideUnset_featureFlagOff_returnsFalse() {      setOverride(OVERRIDE_UNSET.setting)      // For overridableFlag, for unset overrides, follow flag @@ -101,7 +101,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_noOverride_featureFlagOn_returnsTrue() { +  fun isEnabled_noOverride_featureFlagOn_returnsTrue() {      setOverride(null)      // For overridableFlag, in absence of overrides, follow flag @@ -111,7 +111,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_noOverride_featureFlagOff_returnsFalse() { +  fun isEnabled_noOverride_featureFlagOff_returnsFalse() {      setOverride(null)      // For overridableFlag, in absence of overrides, follow flag @@ -120,7 +120,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_unrecognizableOverride_featureFlagOn_returnsTrue() { +  fun isEnabled_unrecognizableOverride_featureFlagOn_returnsTrue() {      setOverride(-2)      // For overridableFlag, for recognizable overrides, follow flag @@ -130,7 +130,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_unrecognizableOverride_featureFlagOff_returnsFalse() { +  fun isEnabled_unrecognizableOverride_featureFlagOff_returnsFalse() {      setOverride(-2)      // For overridableFlag, for recognizable overrides, follow flag @@ -139,7 +139,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_overrideOff_featureFlagOn_returnsFalse() { +  fun isEnabled_overrideOff_featureFlagOn_returnsFalse() {      setOverride(OVERRIDE_OFF.setting)      // For overridableFlag, follow override if they exist @@ -149,7 +149,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_overrideOn_featureFlagOff_returnsTrue() { +  fun isEnabled_overrideOn_featureFlagOff_returnsTrue() {      setOverride(OVERRIDE_ON.setting)      // For overridableFlag, follow override if they exist @@ -158,7 +158,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() { +  fun isEnabled_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() {      setOverride(OVERRIDE_OFF.setting)      // For overridableFlag, follow override if they exist @@ -173,7 +173,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() { +  fun isEnabled_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() {      setOverride(OVERRIDE_ON.setting)      // For overridableFlag, follow override if they exist @@ -187,7 +187,7 @@ class DesktopModeFlagsTest : ShellTestCase() {    @Test    @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) -  fun isEnabled_flagOverridable_noOverride_featureFlagOnThenOff_returnsTrueAndFalse() { +  fun isEnabled_noOverride_featureFlagOnThenOff_returnsTrueAndFalse() {      setOverride(null)      // For overridableFlag, in absence of overrides, follow flag      assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() @@ -206,6 +206,108 @@ class DesktopModeFlagsTest : ShellTestCase() {      }    } +  @Test +  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) +  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) +  fun isEnabled_noSystemProperty_overrideOn_featureFlagOff_returnsTrueAndStoresPropertyOn() { +    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY) +    setOverride(OVERRIDE_ON.setting) + +    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() +    // Store System Property if not present +    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) +        .isEqualTo(OVERRIDE_ON.setting.toString()) +  } + +  @Test +  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) +  fun isEnabled_noSystemProperty_overrideUnset_featureFlagOn_returnsTrueAndStoresPropertyUnset() { +    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY) +    setOverride(OVERRIDE_UNSET.setting) + +    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() +    // Store System Property if not present +    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) +        .isEqualTo(OVERRIDE_UNSET.setting.toString()) +  } + +  @Test +  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) +  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) +  fun isEnabled_noSystemProperty_overrideUnset_featureFlagOff_returnsFalseAndStoresPropertyUnset() { +    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY) +    setOverride(OVERRIDE_UNSET.setting) + +    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() +    // Store System Property if not present +    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) +        .isEqualTo(OVERRIDE_UNSET.setting.toString()) +  } + +  @Test +  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) +  @Suppress("ktlint:standard:max-line-length") +  fun isEnabled_systemPropertyNotInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() { +    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc") +    setOverride(OVERRIDE_OFF.setting) + +    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() +    // Store System Property if currently invalid +    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) +        .isEqualTo(OVERRIDE_OFF.setting.toString()) +  } + +  @Test +  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) +  @Suppress("ktlint:standard:max-line-length") +  fun isEnabled_systemPropertyInvalidInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() { +    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2") +    setOverride(OVERRIDE_OFF.setting) + +    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() +    // Store System Property if currently invalid +    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) +        .isEqualTo(OVERRIDE_OFF.setting.toString()) +  } + +  @Test +  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) +  fun isEnabled_systemPropertyOff_overrideOn_featureFlagOn_returnsFalseAndDoesNotUpdateProperty() { +    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_OFF.setting.toString()) +    setOverride(OVERRIDE_ON.setting) + +    // Have a consistent override until reboot +    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse() +    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) +        .isEqualTo(OVERRIDE_OFF.setting.toString()) +  } + +  @Test +  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) +  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) +  fun isEnabled_systemPropertyOn_overrideOff_featureFlagOff_returnsTrueAndDoesNotUpdateProperty() { +    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_ON.setting.toString()) +    setOverride(OVERRIDE_OFF.setting) + +    // Have a consistent override until reboot +    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() +    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) +        .isEqualTo(OVERRIDE_ON.setting.toString()) +  } + +  @Test +  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE) +  @Suppress("ktlint:standard:max-line-length") +  fun isEnabled_systemPropertyUnset_overrideOff_featureFlagOn_returnsTrueAndDoesNotUpdateProperty() { +    System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_UNSET.setting.toString()) +    setOverride(OVERRIDE_OFF.setting) + +    // Have a consistent override until reboot +    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue() +    assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)) +        .isEqualTo(OVERRIDE_UNSET.setting.toString()) +  } +    private fun setOverride(setting: Int?) {      val contentResolver = mContext.contentResolver      val key = Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES @@ -217,9 +319,16 @@ class DesktopModeFlagsTest : ShellTestCase() {    }    private fun resetCache() { -    val cacheToggleOverride = +    val cachedToggleOverride =          DESKTOP_WINDOWING_MODE::class.java.getDeclaredField("cachedToggleOverride") -    cacheToggleOverride.isAccessible = true -    cacheToggleOverride.set(DESKTOP_WINDOWING_MODE, null) +    cachedToggleOverride.isAccessible = true +    cachedToggleOverride.set(DESKTOP_WINDOWING_MODE, null) + +    // Clear override cache stored in System property +    System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY) +  } + +  private companion object { +    const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"    }  } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 8331d591fd59..409b87723e79 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -1191,8 +1191,7 @@ public class ShellTransitionTests extends ShellTestCase {                          mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));          final RecentsTransitionHandler recentsHandler =                  new RecentsTransitionHandler(shellInit, transitions, -                        mock(RecentTasksController.class), mock(HomeTransitionObserver.class), -                        () -> mock(SurfaceControl.Transaction.class)); +                        mock(RecentTasksController.class), mock(HomeTransitionObserver.class));          transitions.replaceDefaultHandlerForTest(mDefaultHandler);          shellInit.init(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 0ec671314cdf..0bf5a674aabb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -348,10 +348,35 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {      }      @Test -    fun testDecorationIsNotCreatedForTopTranslucentActivities() { -        setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) +    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) +    fun testDecorationIsCreatedForTopTranslucentActivitiesWithStyleFloating() { +        val mockitoSession: StaticMockitoSession = mockitoSession() +                .strictness(Strictness.LENIENT) +                .spyStatic(DesktopModeStatus::class.java) +                .startMocking() +        try { +            val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply { +                isTopActivityTransparent = true +                isTopActivityStyleFloating = true +                numActivities = 1 +            } +            doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } +            setUpMockDecorationsForTasks(task) + +            onTaskOpening(task) +            verify(mockDesktopModeWindowDecorFactory) +                    .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any()) +        } finally { +            mockitoSession.finishMocking() +        } +    } + +    @Test +    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) +    fun testDecorationIsNotCreatedForTopTranslucentActivitiesWithoutStyleFloating() {          val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {              isTopActivityTransparent = true +            isTopActivityStyleFloating = false              numActivities = 1          }          onTaskOpening(task) @@ -361,6 +386,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {      }      @Test +    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)      fun testDecorationIsNotCreatedForSystemUIActivities() {          val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true) diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp index c36b82d175e2..71ecb4c30543 100644 --- a/packages/SettingsLib/ActionButtonsPreference/Android.bp +++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp @@ -19,7 +19,6 @@ android_library {      static_libs: [          "androidx.preference_preference", -        "SettingsLibUtils",      ],      sdk_version: "system_current", diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java index 3e65d94e3323..5dc11cfc0392 100644 --- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java +++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java @@ -20,6 +20,7 @@ import android.content.Context;  import android.content.res.Resources;  import android.content.res.TypedArray;  import android.graphics.drawable.Drawable; +import android.os.Build;  import android.text.TextUtils;  import android.util.AttributeSet;  import android.util.Log; @@ -31,12 +32,11 @@ import androidx.annotation.StringRes;  import androidx.preference.Preference;  import androidx.preference.PreferenceViewHolder; -import com.android.settingslib.utils.BuildCompatUtils; +import com.android.settingslib.widget.preference.actionbuttons.R; +  import java.util.ArrayList;  import java.util.List; -import com.android.settingslib.widget.preference.actionbuttons.R; -  /**   * This preference provides a four buttons layout with Settings style.   * It looks like below @@ -56,7 +56,7 @@ import com.android.settingslib.widget.preference.actionbuttons.R;  public class ActionButtonsPreference extends Preference {      private static final String TAG = "ActionButtonPreference"; -    private static final boolean mIsAtLeastS = BuildCompatUtils.isAtLeastS(); +    private static final boolean mIsAtLeastS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;      private static final int SINGLE_BUTTON_STYLE = 1;      private static final int TWO_BUTTONS_STYLE = 2;      private static final int THREE_BUTTONS_STYLE = 3; diff --git a/packages/SettingsLib/ActivityEmbedding/Android.bp b/packages/SettingsLib/ActivityEmbedding/Android.bp index 838a9e505ece..556100212445 100644 --- a/packages/SettingsLib/ActivityEmbedding/Android.bp +++ b/packages/SettingsLib/ActivityEmbedding/Android.bp @@ -20,7 +20,6 @@ android_library {          "androidx.annotation_annotation",          "androidx.core_core",          "androidx.window_window", -        "SettingsLibUtils",      ],      sdk_version: "system_current",      min_sdk_version: "21", diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java index f89be9fba583..67aa8f41e227 100644 --- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java +++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java @@ -20,13 +20,11 @@ import android.app.Activity;  import android.content.ComponentName;  import android.content.Context;  import android.content.Intent; +import android.os.Build;  import android.util.Log; -import androidx.core.os.BuildCompat;  import androidx.window.embedding.ActivityEmbeddingController; -import com.android.settingslib.utils.BuildCompatUtils; -  /**   * An util class collecting all common methods for the embedding activity features.   */ @@ -70,7 +68,7 @@ public final class ActivityEmbeddingUtils {       * enabled (unsupported devices).       */      private static ComponentName getEmbeddingActivityComponent(Context context) { -        if (!BuildCompatUtils.isAtLeastSV2()) { +        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S_V2) {              return null;          }          final Intent intent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY); @@ -95,7 +93,7 @@ public final class ActivityEmbeddingUtils {       *                          Settings app       */      public static boolean shouldHideNavigateUpButton(Activity activity, boolean isSecondLayerPage) { -        if (!BuildCompat.isAtLeastT()) { +        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {              return false;          } diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp index 07290de8661e..3f671b9e7b10 100644 --- a/packages/SettingsLib/BannerMessagePreference/Android.bp +++ b/packages/SettingsLib/BannerMessagePreference/Android.bp @@ -20,7 +20,6 @@ android_library {      static_libs: [          "androidx.preference_preference",          "SettingsLibSettingsTheme", -        "SettingsLibUtils",      ],      sdk_version: "system_current", diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java index 33775a64aa82..6cd777e878fe 100644 --- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java @@ -38,7 +38,6 @@ import androidx.annotation.StringRes;  import androidx.preference.Preference;  import androidx.preference.PreferenceViewHolder; -import com.android.settingslib.utils.BuildCompatUtils;  import com.android.settingslib.widget.preference.banner.R;  /**   * Banner message is a banner displaying important information (permission request, page error etc), @@ -84,7 +83,7 @@ public class BannerMessagePreference extends Preference {      }      private static final String TAG = "BannerPreference"; -    private static final boolean IS_AT_LEAST_S = BuildCompatUtils.isAtLeastS(); +    private static final boolean IS_AT_LEAST_S = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;      private final BannerMessagePreference.ButtonInfo mPositiveButtonInfo =              new BannerMessagePreference.ButtonInfo(); diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp index 87ec0b8d46fb..483403971864 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp @@ -22,7 +22,6 @@ android_library {          "androidx.core_core",          "com.google.android.material_material",          "SettingsLibSettingsTransition", -        "SettingsLibUtils",          "SettingsLibSettingsTheme",      ],      sdk_version: "system_current", diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java index 8ebbac39d1d0..b252e5f021ae 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java @@ -16,11 +16,11 @@  package com.android.settingslib.collapsingtoolbar; +import android.os.Build; +  import androidx.fragment.app.FragmentActivity;  import androidx.preference.PreferenceFragmentCompat; -import com.android.settingslib.utils.BuildCompatUtils; -  import com.google.android.material.appbar.AppBarLayout;  /** @@ -58,7 +58,7 @@ public abstract class BasePreferencesFragment extends PreferenceFragmentCompat {          if (activity != null) {              activity.setTitle(getTitle()); -            if (BuildCompatUtils.isAtLeastS()) { +            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {                  AppBarLayout appBarLayout = (AppBarLayout) activity.findViewById(R.id.app_bar);                  if (appBarLayout != null) { diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java index 04c44e6d11be..8b27626a08e4 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java @@ -17,6 +17,7 @@  package com.android.settingslib.collapsingtoolbar;  import android.app.ActionBar; +import android.os.Build;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.View; @@ -26,8 +27,6 @@ import android.widget.Toolbar;  import androidx.annotation.Nullable;  import androidx.appcompat.app.AppCompatActivity; -import com.android.settingslib.utils.BuildCompatUtils; -  import com.google.android.material.appbar.AppBarLayout;  import com.google.android.material.appbar.CollapsingToolbarLayout;  import com.google.android.material.color.DynamicColors; @@ -66,12 +65,12 @@ public class CollapsingToolbarAppCompatActivity extends AppCompatActivity {      @Override      protected void onCreate(@Nullable Bundle savedInstanceState) {          super.onCreate(savedInstanceState); -        if (BuildCompatUtils.isAtLeastS()) { +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {              DynamicColors.applyToActivityIfAvailable(this);          }          setTheme(com.android.settingslib.widget.theme.R.style.Theme_SubSettingsBase); -        if (mCustomizeLayoutResId > 0 && !BuildCompatUtils.isAtLeastS()) { +        if (mCustomizeLayoutResId > 0 && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {              super.setContentView(mCustomizeLayoutResId);              return;          } diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java index 143101f76977..86ce2ab6109f 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java @@ -18,6 +18,7 @@ package com.android.settingslib.collapsingtoolbar;  import android.app.ActionBar;  import android.content.pm.PackageManager; +import android.os.Build;  import android.os.Bundle;  import android.view.LayoutInflater;  import android.view.View; @@ -27,8 +28,6 @@ import android.widget.Toolbar;  import androidx.annotation.Nullable;  import androidx.fragment.app.FragmentActivity; -import com.android.settingslib.utils.BuildCompatUtils; -  import com.google.android.material.appbar.AppBarLayout;  import com.google.android.material.appbar.CollapsingToolbarLayout; @@ -60,7 +59,8 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {      protected void onCreate(@Nullable Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          // for backward compatibility on R devices or wearable devices due to small device size. -        if (mCustomizeLayoutResId > 0 && (!BuildCompatUtils.isAtLeastS() || isWatch())) { +        if (mCustomizeLayoutResId > 0 && (Build.VERSION.SDK_INT < Build.VERSION_CODES.S +                || isWatch())) {              super.setContentView(mCustomizeLayoutResId);              return;          } diff --git a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java index c52386bef07b..7be448207efe 100644 --- a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java +++ b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java @@ -30,7 +30,6 @@ import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup; -import androidx.core.os.BuildCompat;  import androidx.fragment.app.Fragment;  import androidx.viewpager2.widget.ViewPager2; @@ -226,7 +225,8 @@ public abstract class ProfileSelectFragment extends Fragment {      // to be here only for this API level - when then private profile was introduced.      @TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)      private boolean shouldShowPrivateProfileIfItsOne(UserHandle userHandle) { -        if (!BuildCompat.isAtLeastV() || !android.os.Flags.allowPrivateProfile()) { +        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM +                || !android.os.Flags.allowPrivateProfile()) {              return false;          }          try { diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java deleted file mode 100644 index bf3651b2cdad..000000000000 --- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.utils; - -import android.os.Build; - -import androidx.annotation.ChecksSdkIntAtLeast; - -/** - * An util class to check whether the current OS version is higher or equal to sdk version of - * device. - */ -public final class BuildCompatUtils { - -    /** -     * Implementation of BuildCompat.isAtLeastS() suitable for use in Settings -     * -     * @return Whether the current OS version is higher or equal to S. -     */ -    @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) -    public static boolean isAtLeastS() { -        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S; -    } - -    /** -     * Implementation of BuildCompat.isAtLeastS() suitable for use in Settings -     * -     * @return Whether the current OS version is higher or equal to Sv2. -     */ -    @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S_V2) -    public static boolean isAtLeastSV2() { -        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S_V2; -    } - -    /** -     * Implementation of BuildCompat.isAtLeastT() suitable for use in Settings -     * -     * @return Whether the current OS version is higher or equal to T. -     */ -    @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) -    public static boolean isAtLeastT() { -        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU; -    } - -    private BuildCompatUtils() {} -} diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java index 734b92c7ac6e..6ca9279de8c6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -38,8 +38,6 @@ import androidx.annotation.VisibleForTesting;  import androidx.preference.Preference;  import androidx.preference.PreferenceViewHolder; -import com.android.settingslib.utils.BuildCompatUtils; -  /**   * Helper class for managing settings preferences that can be disabled   * by device admins via user restrictions. @@ -120,9 +118,10 @@ public class RestrictedPreferenceHelper {          if (mDisabledSummary) {              final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);              if (summaryView != null) { -                final CharSequence disabledText = BuildCompatUtils.isAtLeastT() -                        ? getDisabledByAdminUpdatableString() -                        : mContext.getString(R.string.disabled_by_admin_summary_text); +                final CharSequence disabledText = +                        (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) +                                ? getDisabledByAdminUpdatableString() +                                : mContext.getString(R.string.disabled_by_admin_summary_text);                  if (mDisabledByAdmin) {                      summaryView.setText(disabledText);                  } else if (mDisabledByEcm) { diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java index 45754eb8f16d..fffbb547c662 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java @@ -25,6 +25,7 @@ import android.app.AppOpsManager;  import android.app.admin.DevicePolicyManager;  import android.content.Context;  import android.content.res.TypedArray; +import android.os.Build;  import android.os.Process;  import android.os.UserHandle;  import android.util.AttributeSet; @@ -40,8 +41,6 @@ import androidx.preference.PreferenceManager;  import androidx.preference.PreferenceViewHolder;  import androidx.preference.SwitchPreferenceCompat; -import com.android.settingslib.utils.BuildCompatUtils; -  /**   * Version of SwitchPreferenceCompat that can be disabled by a device admin   * using a user restriction. @@ -164,7 +163,7 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat {      private static String getUpdatableEnterpriseString(              Context context, String updatableStringId, int resId) { -        if (!BuildCompatUtils.isAtLeastT()) { +        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {              return context.getString(resId);          }          return context.getSystemService(DevicePolicyManager.class).getResources().getString( diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index c2506d353d14..b02b0c454644 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -66,7 +66,6 @@ import com.android.launcher3.util.UserIconInfo;  import com.android.settingslib.drawable.UserIconDrawable;  import com.android.settingslib.fuelgauge.BatteryStatus;  import com.android.settingslib.fuelgauge.BatteryUtils; -import com.android.settingslib.utils.BuildCompatUtils;  import java.util.List; @@ -147,7 +146,7 @@ public class Utils {          String name = info != null ? info.name : null;          if (info.isManagedProfile()) {              // We use predefined values for managed profiles -            return BuildCompatUtils.isAtLeastT() +            return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU                      ? getUpdatableManagedUserTitle(context)                      : context.getString(R.string.managed_user_title);          } else if (info.isGuest()) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index 721e7b93fd32..53441c08776f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -24,6 +24,7 @@ import android.media.AudioManager;  import android.net.Uri;  import android.provider.DeviceConfig;  import android.provider.MediaStore; +import android.provider.Settings;  import android.text.TextUtils;  import android.util.Log;  import android.util.Pair; @@ -808,7 +809,8 @@ public class BluetoothUtils {       * <p>If CachedBluetoothDevice#getGroupId is invalid, fetch group id from       * LeAudioProfile#getGroupId.       */ -    public static int getGroupId(@NonNull CachedBluetoothDevice cachedDevice) { +    public static int getGroupId(@Nullable CachedBluetoothDevice cachedDevice) { +        if (cachedDevice == null) return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;          int groupId = cachedDevice.getGroupId();          String anonymizedAddress = cachedDevice.getDevice().getAnonymizedAddress();          if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) { @@ -824,4 +826,44 @@ public class BluetoothUtils {          Log.d(TAG, "getGroupId return invalid id for device: " + anonymizedAddress);          return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;      } + +    /** Get primary device Uri in broadcast. */ +    @NonNull +    public static String getPrimaryGroupIdUriForBroadcast() { +        return "bluetooth_le_broadcast_fallback_active_group_id"; +    } + +    /** Get primary device group id in broadcast. */ +    @WorkerThread +    public static int getPrimaryGroupIdForBroadcast(@NonNull Context context) { +        return Settings.Secure.getInt( +                context.getContentResolver(), +                getPrimaryGroupIdUriForBroadcast(), +                BluetoothCsipSetCoordinator.GROUP_ID_INVALID); +    } + +    /** Get secondary {@link CachedBluetoothDevice} in broadcast. */ +    @Nullable +    @WorkerThread +    public static CachedBluetoothDevice getSecondaryDeviceForBroadcast( +            @NonNull Context context, @Nullable LocalBluetoothManager localBtManager) { +        if (localBtManager == null) return null; +        int primaryGroupId = getPrimaryGroupIdForBroadcast(context); +        if (primaryGroupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) return null; +        LocalBluetoothLeBroadcastAssistant assistant = +                localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile(); +        CachedBluetoothDeviceManager deviceManager = localBtManager.getCachedDeviceManager(); +        List<BluetoothDevice> devices = assistant.getAllConnectedDevices(); +        for (BluetoothDevice device : devices) { +            CachedBluetoothDevice cachedDevice = deviceManager.findDevice(device); +            if (hasConnectedBroadcastSource(cachedDevice, localBtManager)) { +                int groupId = getGroupId(cachedDevice); +                if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID +                        && groupId != primaryGroupId) { +                    return cachedDevice; +                } +            } +        } +        return null; +    }  } diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java index ea65adecd763..84afb9f7a7e2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java @@ -25,7 +25,6 @@ import android.util.Log;  import androidx.annotation.EmptySuper;  import androidx.annotation.NonNull;  import androidx.annotation.RequiresApi; -import androidx.core.os.BuildCompat;  import androidx.lifecycle.LifecycleOwner;  import androidx.preference.Preference;  import androidx.preference.PreferenceGroup; @@ -141,7 +140,7 @@ public abstract class AbstractPreferenceController {      @RequiresApi(Build.VERSION_CODES.TIRAMISU)      protected void replaceEnterpriseStringTitle(PreferenceScreen screen,              String preferenceKey, String overrideKey, int resource) { -        if (!BuildCompat.isAtLeastT() || mDevicePolicyManager == null) { +        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || mDevicePolicyManager == null) {              return;          } @@ -159,7 +158,7 @@ public abstract class AbstractPreferenceController {      @RequiresApi(Build.VERSION_CODES.TIRAMISU)      protected void replaceEnterpriseStringSummary(              PreferenceScreen screen, String preferenceKey, String overrideKey, int resource) { -        if (!BuildCompat.isAtLeastT() || mDevicePolicyManager == null) { +        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || mDevicePolicyManager == null) {              return;          } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java index f07daa3add8b..243403e018b6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java @@ -48,8 +48,6 @@ import androidx.annotation.NonNull;  import androidx.annotation.RequiresApi;  import androidx.annotation.VisibleForTesting; -import com.android.settingslib.utils.BuildCompatUtils; -  /**   * Converts the user avatar icon to a circularly clipped one with an optional badge and frame   */ @@ -88,7 +86,7 @@ public class UserIconDrawable extends Drawable implements Drawable.Callback {       * @return drawable containing just the badge       */      public static Drawable getManagedUserDrawable(Context context) { -        if (BuildCompatUtils.isAtLeastT()) { +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {              return getUpdatableManagedUserDrawable(context);          } else {              return getDrawableForDisplayDensity( @@ -227,7 +225,7 @@ public class UserIconDrawable extends Drawable implements Drawable.Callback {      }      private static Drawable getManagementBadge(Context context) { -        if (BuildCompatUtils.isAtLeastT()) { +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {              return getUpdatableManagementBadge(context);          } else {              return getDrawableForDisplayDensity( diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt index 4d25237757ea..29527646b89f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt @@ -73,12 +73,12 @@ class ZenModeRepositoryImpl(                  awaitClose { context.unregisterReceiver(receiver) }              } -            .apply { +            .let {                  if (Flags.volumePanelBroadcastFix()) { -                    flowOn(backgroundCoroutineContext) -                    stateIn(scope, SharingStarted.WhileSubscribed(), null) +                    it.flowOn(backgroundCoroutineContext) +                        .stateIn(scope, SharingStarted.WhileSubscribed(), null)                  } else { -                    shareIn( +                    it.shareIn(                          started = SharingStarted.WhileSubscribed(),                          scope = scope,                      ) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index a638df524740..28bf34882365 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -22,11 +22,13 @@ import static com.google.common.truth.Truth.assertThat;  import static org.mockito.ArgumentMatchers.any;  import static org.mockito.Mockito.doReturn;  import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock;  import static org.mockito.Mockito.spy;  import static org.mockito.Mockito.verify;  import static org.mockito.Mockito.when;  import android.bluetooth.BluetoothClass; +import android.bluetooth.BluetoothCsipSetCoordinator;  import android.bluetooth.BluetoothDevice;  import android.bluetooth.BluetoothLeBroadcastReceiveState;  import android.content.Context; @@ -36,10 +38,13 @@ import android.graphics.drawable.Drawable;  import android.media.AudioManager;  import android.net.Uri;  import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings;  import android.util.Pair;  import com.android.settingslib.widget.AdaptiveIcon; +import com.google.common.collect.ImmutableList; +  import org.junit.Before;  import org.junit.Rule;  import org.junit.Test; @@ -562,4 +567,77 @@ public class BluetoothUtilsTest {          assertThat(BluetoothUtils.isAvailableHearingDevice(mCachedBluetoothDevice)).isEqualTo(true);      } + +    @Test +    public void getGroupId_getCsipProfileId() { +        when(mCachedBluetoothDevice.getGroupId()).thenReturn(1); + +        assertThat(BluetoothUtils.getGroupId(mCachedBluetoothDevice)).isEqualTo(1); +    } + +    @Test +    public void getGroupId_getLeAudioProfileId() { +        when(mCachedBluetoothDevice.getGroupId()) +                .thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); +        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); +        LeAudioProfile leAudio = mock(LeAudioProfile.class); +        when(leAudio.getGroupId(mBluetoothDevice)).thenReturn(1); +        when(mCachedBluetoothDevice.getProfiles()).thenReturn(ImmutableList.of(leAudio)); + +        assertThat(BluetoothUtils.getGroupId(mCachedBluetoothDevice)).isEqualTo(1); +    } + +    @Test +    public void getSecondaryDeviceForBroadcast_errorState_returnNull() { +        assertThat(BluetoothUtils.getSecondaryDeviceForBroadcast(mContext, mLocalBluetoothManager)) +                .isNull(); +    } + +    @Test +    public void getSecondaryDeviceForBroadcast_noSecondary_returnNull() { +        Settings.Secure.putInt( +                mContext.getContentResolver(), +                BluetoothUtils.getPrimaryGroupIdUriForBroadcast(), +                1); +        CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class); +        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager); +        when(deviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); +        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); +        when(mCachedBluetoothDevice.getGroupId()).thenReturn(1); +        BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); +        when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(ImmutableList.of(state)); +        when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mBluetoothDevice)); + +        assertThat(BluetoothUtils.getSecondaryDeviceForBroadcast(mContext, mLocalBluetoothManager)) +                .isNull(); +    } + +    @Test +    public void getSecondaryDeviceForBroadcast_returnCorrectDevice() { +        Settings.Secure.putInt( +                mContext.getContentResolver(), +                BluetoothUtils.getPrimaryGroupIdUriForBroadcast(), +                1); +        CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class); +        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager); +        CachedBluetoothDevice cachedBluetoothDevice = mock(CachedBluetoothDevice.class); +        BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class); +        when(cachedBluetoothDevice.getDevice()).thenReturn(bluetoothDevice); +        when(cachedBluetoothDevice.getGroupId()).thenReturn(1); +        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); +        when(mCachedBluetoothDevice.getGroupId()).thenReturn(2); +        when(deviceManager.findDevice(bluetoothDevice)).thenReturn(cachedBluetoothDevice); +        when(deviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); +        BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); +        List<Long> bisSyncState = new ArrayList<>(); +        bisSyncState.add(1L); +        when(state.getBisSyncState()).thenReturn(bisSyncState); +        when(mAssistant.getAllSources(any(BluetoothDevice.class))) +                .thenReturn(ImmutableList.of(state)); +        when(mAssistant.getAllConnectedDevices()) +                .thenReturn(ImmutableList.of(mBluetoothDevice, bluetoothDevice)); + +        assertThat(BluetoothUtils.getSecondaryDeviceForBroadcast(mContext, mLocalBluetoothManager)) +                .isEqualTo(mCachedBluetoothDevice); +    }  } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 00fb7a1feab1..2cdd0aee3d85 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -80,6 +80,7 @@ public class SystemSettings {                  Settings.System.SIP_RECEIVE_CALLS,                  Settings.System.POINTER_SPEED,                  Settings.System.POINTER_FILL_STYLE, +                Settings.System.POINTER_STROKE_STYLE,                  Settings.System.POINTER_SCALE,                  Settings.System.VIBRATE_ON,                  Settings.System.VIBRATE_WHEN_RINGING, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 4235bc4157bf..7b927d793c37 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -30,6 +30,8 @@ import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;  import static android.view.PointerIcon.LARGE_POINTER_SCALE;  import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BEGIN;  import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_END; +import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_BEGIN; +import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_END;  import android.annotation.Nullable;  import android.compat.annotation.UnsupportedAppUsage; @@ -213,6 +215,9 @@ public class SystemSettingsValidators {          VALIDATORS.put(System.POINTER_FILL_STYLE,                  new InclusiveIntegerRangeValidator(POINTER_ICON_VECTOR_STYLE_FILL_BEGIN,                          POINTER_ICON_VECTOR_STYLE_FILL_END)); +        VALIDATORS.put(System.POINTER_STROKE_STYLE, +                new InclusiveIntegerRangeValidator(POINTER_ICON_VECTOR_STYLE_STROKE_BEGIN, +                        POINTER_ICON_VECTOR_STYLE_STROKE_END));          VALIDATORS.put(System.POINTER_SCALE,                  new InclusiveFloatRangeValidator(DEFAULT_POINTER_SCALE, LARGE_POINTER_SCALE));          VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7)); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 384cb7ee9c49..cd37ad171fac 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -2916,6 +2916,9 @@ class SettingsProtoDumpUtil {                  Settings.System.POINTER_FILL_STYLE,                  SystemSettingsProto.Pointer.POINTER_FILL_STYLE);          dumpSetting(s, p, +                Settings.System.POINTER_STROKE_STYLE, +                SystemSettingsProto.Pointer.POINTER_STROKE_STYLE); +        dumpSetting(s, p,                  Settings.System.POINTER_SCALE,                  SystemSettingsProto.Pointer.POINTER_SCALE);          p.end(pointerToken); diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index b5776e253485..469b9cef0411 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -23,7 +23,10 @@ package {  // See: http://go/android-license-faq  license {      name: "frameworks_base_packages_SystemUI_license", -    visibility: [":__subpackages__"], +    visibility: [ +        ":__subpackages__", +        "//development/samples/SceneTransitionLayoutDemo:__subpackages__", +    ],      license_kinds: [          "SPDX-license-identifier-Apache-2.0",      ], diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 18fbf7769426..07f74367fd06 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1052,13 +1052,6 @@ flag {  }  flag { -  name: "glanceable_hub_gesture_handle" -  namespace: "systemui" -  description: "Shows a vertical bar at the right edge to indicate the user can swipe to open the glanceable hub" -  bug: "339667383" -} - -flag {    name: "glanceable_hub_allow_keyguard_when_dreaming"    namespace: "systemui"    description: "Allows users to exit dream to keyguard with glanceable hub enabled" @@ -1143,4 +1136,14 @@ flag {    metadata {      purpose: PURPOSE_BUGFIX    } -}
\ No newline at end of file +} + +flag { +   namespace: "systemui" +   name: "fetch_bookmarks_xml_keyboard_shortcuts" +   description: "Fetches application launch keyboard shortcuts from system server rather than building a hardcoded list." +   bug: "312452252" +   metadata { +       purpose: PURPOSE_BUGFIX +   } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index bb76c1d668e3..cc4e7752ab21 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -9,21 +9,14 @@ import androidx.compose.animation.core.tween  import androidx.compose.foundation.background  import androidx.compose.foundation.gestures.Orientation  import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Arrangement  import androidx.compose.foundation.layout.Box  import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer  import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape  import androidx.compose.runtime.Composable  import androidx.compose.runtime.DisposableEffect  import androidx.compose.runtime.getValue  import androidx.compose.runtime.remember  import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment  import androidx.compose.ui.Modifier  import androidx.compose.ui.draw.alpha  import androidx.compose.ui.draw.drawBehind @@ -48,7 +41,6 @@ import com.android.compose.animation.scene.SwipeDirection  import com.android.compose.animation.scene.observableTransitionState  import com.android.compose.animation.scene.transitions  import com.android.compose.theme.LocalAndroidColorScheme -import com.android.systemui.Flags  import com.android.systemui.communal.shared.model.CommunalBackgroundType  import com.android.systemui.communal.shared.model.CommunalScenes  import com.android.systemui.communal.shared.model.CommunalTransitionKeys @@ -156,8 +148,6 @@ fun CommunalContainer(      val currentSceneKey: SceneKey by          viewModel.currentScene.collectAsStateWithLifecycle(CommunalScenes.Blank)      val touchesAllowed by viewModel.touchesAllowed.collectAsStateWithLifecycle() -    val showGestureIndicator by -        viewModel.showGestureIndicator.collectAsStateWithLifecycle(initialValue = false)      val backgroundType by          viewModel.communalBackground.collectAsStateWithLifecycle(              initialValue = CommunalBackgroundType.ANIMATED @@ -200,19 +190,7 @@ fun CommunalContainer(                  )          ) {              // This scene shows nothing only allowing for transitions to the communal scene. -            // TODO(b/339667383): remove this temporary swipe gesture handle -            Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.End) { -                if (showGestureIndicator && Flags.glanceableHubGestureHandle()) { -                    Box( -                        modifier = -                            Modifier.height(220.dp) -                                .width(4.dp) -                                .align(Alignment.CenterVertically) -                                .background(color = Color.White, RoundedCornerShape(4.dp)) -                    ) -                    Spacer(modifier = Modifier.width(12.dp)) -                } -            } +            Box(modifier = Modifier.fillMaxSize())          }          scene( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index be51c1a76fad..97ed74f0f07a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -119,6 +119,7 @@ import androidx.compose.ui.platform.testTag  import androidx.compose.ui.res.dimensionResource  import androidx.compose.ui.res.stringResource  import androidx.compose.ui.semantics.CustomAccessibilityAction +import androidx.compose.ui.semantics.clearAndSetSemantics  import androidx.compose.ui.semantics.contentDescription  import androidx.compose.ui.semantics.customActions  import androidx.compose.ui.semantics.onClick @@ -871,7 +872,7 @@ private fun CtaTileInViewModeContent(              Icon(                  imageVector = Icons.Outlined.Widgets,                  contentDescription = stringResource(R.string.cta_label_to_open_widget_picker), -                modifier = Modifier.size(Dimensions.IconSize), +                modifier = Modifier.size(Dimensions.IconSize).clearAndSetSemantics {},              )              Spacer(modifier = Modifier.size(6.dp))              Text( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index e8fdfc8da3bc..d95b388eb347 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -913,6 +913,7 @@ internal class NestedScrollHandlerImpl(      private val topOrLeftBehavior: NestedScrollBehavior,      private val bottomOrRightBehavior: NestedScrollBehavior,      private val isExternalOverscrollGesture: () -> Boolean, +    private val pointersInfoOwner: PointersInfoOwner,  ) {      private val layoutState = layoutImpl.state      private val draggableHandler = layoutImpl.draggableHandler(orientation) @@ -924,25 +925,12 @@ internal class NestedScrollHandlerImpl(          // moving on to the next scene.          var canChangeScene = false -        val actionUpOrLeft = -            Swipe( -                direction = -                    when (orientation) { -                        Orientation.Horizontal -> SwipeDirection.Left -                        Orientation.Vertical -> SwipeDirection.Up -                    }, -                pointerCount = 1, -            ) - -        val actionDownOrRight = -            Swipe( -                direction = -                    when (orientation) { -                        Orientation.Horizontal -> SwipeDirection.Right -                        Orientation.Vertical -> SwipeDirection.Down -                    }, -                pointerCount = 1, -            ) +        var _lastPointersInfo: PointersInfo? = null +        fun pointersInfo(): PointersInfo { +            return checkNotNull(_lastPointersInfo) { +                "PointersInfo should be initialized before the transition begins." +            } +        }          fun hasNextScene(amount: Float): Boolean {              val transitionState = layoutState.transitionState @@ -950,8 +938,30 @@ internal class NestedScrollHandlerImpl(              val fromScene = layoutImpl.scene(scene)              val nextScene =                  when { -                    amount < 0f -> fromScene.userActions[actionUpOrLeft] -                    amount > 0f -> fromScene.userActions[actionDownOrRight] +                    amount < 0f -> { +                        val actionUpOrLeft = +                            Swipe( +                                direction = +                                    when (orientation) { +                                        Orientation.Horizontal -> SwipeDirection.Left +                                        Orientation.Vertical -> SwipeDirection.Up +                                    }, +                                pointerCount = pointersInfo().pointersDown, +                            ) +                        fromScene.userActions[actionUpOrLeft] +                    } +                    amount > 0f -> { +                        val actionDownOrRight = +                            Swipe( +                                direction = +                                    when (orientation) { +                                        Orientation.Horizontal -> SwipeDirection.Right +                                        Orientation.Vertical -> SwipeDirection.Down +                                    }, +                                pointerCount = pointersInfo().pointersDown, +                            ) +                        fromScene.userActions[actionDownOrRight] +                    }                      else -> null                  }              if (nextScene != null) return true @@ -985,6 +995,8 @@ internal class NestedScrollHandlerImpl(                      return@PriorityNestedScrollConnection false                  } +                _lastPointersInfo = pointersInfoOwner.pointersInfo() +                  // If the current swipe transition is *not* closed to 0f or 1f, then we want the                  // scroll events to intercept the current transition to continue the scene                  // transition. @@ -1002,6 +1014,8 @@ internal class NestedScrollHandlerImpl(                  val isZeroOffset =                      if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f +                _lastPointersInfo = pointersInfoOwner.pointersInfo() +                  val canStart =                      when (behavior) {                          NestedScrollBehavior.DuringTransitionBetweenScenes -> { @@ -1039,6 +1053,8 @@ internal class NestedScrollHandlerImpl(                  // We could start an overscroll animation                  canChangeScene = false +                _lastPointersInfo = pointersInfoOwner.pointersInfo() +                  val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable)                  if (canStart) {                      isIntercepting = false @@ -1049,10 +1065,11 @@ internal class NestedScrollHandlerImpl(              canContinueScroll = { true },              canScrollOnFling = false,              onStart = { offsetAvailable -> +                val pointersInfo = pointersInfo()                  dragController =                      draggableHandler.onDragStarted( -                        pointersDown = 1, -                        startedPosition = null, +                        pointersDown = pointersInfo.pointersDown, +                        startedPosition = pointersInfo.startedPosition,                          overSlop = if (isIntercepting) 0f else offsetAvailable,                      )              }, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index f40f26578302..cdcfc84ce92a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -29,18 +29,21 @@ import androidx.compose.ui.input.pointer.PointerId  import androidx.compose.ui.input.pointer.PointerInputChange  import androidx.compose.ui.input.pointer.PointerInputScope  import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode -import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed +import androidx.compose.ui.input.pointer.changedToDown  import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed  import androidx.compose.ui.input.pointer.positionChange  import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed  import androidx.compose.ui.input.pointer.util.VelocityTracker  import androidx.compose.ui.input.pointer.util.addPointerInputChange  import androidx.compose.ui.node.CompositionLocalConsumerModifierNode +import androidx.compose.ui.node.DelegatableNode  import androidx.compose.ui.node.DelegatingNode  import androidx.compose.ui.node.ModifierNodeElement  import androidx.compose.ui.node.ObserverModifierNode  import androidx.compose.ui.node.PointerInputModifierNode +import androidx.compose.ui.node.TraversableNode  import androidx.compose.ui.node.currentValueOf +import androidx.compose.ui.node.findNearestAncestor  import androidx.compose.ui.node.observeReads  import androidx.compose.ui.platform.LocalViewConfiguration  import androidx.compose.ui.unit.IntSize @@ -48,11 +51,12 @@ import androidx.compose.ui.unit.Velocity  import androidx.compose.ui.util.fastAll  import androidx.compose.ui.util.fastAny  import androidx.compose.ui.util.fastFirstOrNull -import androidx.compose.ui.util.fastForEach +import androidx.compose.ui.util.fastSumBy  import kotlin.coroutines.cancellation.CancellationException  import kotlin.math.sign  import kotlinx.coroutines.coroutineScope  import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch  /**   * Make an element draggable in the given [orientation]. @@ -112,6 +116,18 @@ private data class MultiPointerDraggableElement(      }  } +private val TRAVERSE_KEY = Any() + +/** Find the nearest [PointersInfoOwner] ancestor or throw. */ +internal fun DelegatableNode.requireAncestorPointersInfoOwner(): PointersInfoOwner { +    val ancestorNode = +        checkNotNull(findNearestAncestor(TRAVERSE_KEY)) { +            "This should never happen! Couldn't find a MultiPointerDraggableNode. " + +                "Are we inside an SceneTransitionLayout?" +        } +    return ancestorNode as PointersInfoOwner +} +  internal class MultiPointerDraggableNode(      orientation: Orientation,      enabled: () -> Boolean, @@ -120,15 +136,19 @@ internal class MultiPointerDraggableNode(          (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,      var swipeDetector: SwipeDetector = DefaultSwipeDetector,  ) : -    PointerInputModifierNode,      DelegatingNode(), +    PointerInputModifierNode,      CompositionLocalConsumerModifierNode, +    TraversableNode, +    PointersInfoOwner,      ObserverModifierNode {      private val pointerInputHandler: suspend PointerInputScope.() -> Unit = { pointerInput() }      private val delegate = delegate(SuspendingPointerInputModifierNode(pointerInputHandler))      private val velocityTracker = VelocityTracker()      private var previousEnabled: Boolean = false +    override val traverseKey: Any = TRAVERSE_KEY +      var enabled: () -> Boolean = enabled          set(value) {              // Reset the pointer input whenever enabled changed. @@ -185,12 +205,42 @@ internal class MultiPointerDraggableNode(          bounds: IntSize      ) = delegate.onPointerEvent(pointerEvent, pass, bounds) +    private var startedPosition: Offset? = null +    private var pointersDown: Int = 0 + +    override fun pointersInfo(): PointersInfo { +        return PointersInfo( +            startedPosition = startedPosition, +            // Note: We could have 0 pointers during fling or for other reasons. +            pointersDown = pointersDown.coerceAtLeast(1), +        ) +    } +      private suspend fun PointerInputScope.pointerInput() {          if (!enabled()) {              return          }          coroutineScope { +            launch { +                // Intercepts pointer inputs and exposes [PointersInfo], via +                // [requireAncestorPointersInfoOwner], to our descendants. +                awaitPointerEventScope { +                    while (isActive) { +                        // During the Initial pass, we receive the event after our ancestors. +                        val pointers = awaitPointerEvent(PointerEventPass.Initial).changes + +                        pointersDown = pointers.countDown() +                        if (pointersDown == 0) { +                            // There are no more pointers down +                            startedPosition = null +                        } else if (startedPosition == null) { +                            startedPosition = pointers.first().position +                        } +                    } +                } +            } +              awaitPointerEventScope {                  while (isActive) {                      try { @@ -314,15 +364,16 @@ internal class MultiPointerDraggableNode(              }          if (drag != null) { -            // Count the number of pressed pointers. -            val pressed = mutableSetOf<PointerId>() -            currentEvent.changes.fastForEach { change -> -                if (change.pressed) { -                    pressed.add(change.id) -                } -            } - -            val controller = onDragStart(drag.position, overSlop, pressed.size) +            val controller = +                onDragStart( +                    // The startedPosition is the starting position when a gesture begins (when the +                    // first pointer touches the screen), not the point where we begin dragging. +                    // For example, this could be different if one of our children intercepts the +                    // gesture first and then we do. +                    requireNotNull(startedPosition), +                    overSlop, +                    pointersDown, +                )              val successful: Boolean              try { @@ -364,12 +415,10 @@ internal class MultiPointerDraggableNode(          fun canBeConsumed(changes: List<PointerInputChange>): Boolean {              // At least one pointer down AND              return changes.fastAny { it.pressed } && -                // All pointers must be: +                // All pointers must be either:                  changes.fastAll { -                    // A) recently pressed: even if the event has already been consumed, we can -                    // still use the recently added finger event to determine whether to initiate -                    // dragging the scene. -                    it.changedToDownIgnoreConsumed() || +                    // A) unconsumed AND recently pressed +                    it.changedToDown() ||                          // B) unconsumed AND in a new position (on the current axis)                          it.positionChange().toFloat() != 0f                  } @@ -461,4 +510,15 @@ internal class MultiPointerDraggableNode(              }          }      } + +    private fun List<PointerInputChange>.countDown() = fastSumBy { if (it.pressed) 1 else 0 }  } + +internal fun interface PointersInfoOwner { +    fun pointersInfo(): PointersInfo +} + +internal data class PointersInfo( +    val startedPosition: Offset?, +    val pointersDown: Int, +) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt index 1fa6b3f7d6c0..ddff2f709082 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt @@ -128,6 +128,7 @@ private class NestedScrollToSceneNode(      bottomOrRightBehavior: NestedScrollBehavior,      isExternalOverscrollGesture: () -> Boolean,  ) : DelegatingNode() { +    lateinit var pointersInfoOwner: PointersInfoOwner      private var priorityNestedScrollConnection: PriorityNestedScrollConnection =          scenePriorityNestedScrollConnection(              layoutImpl = layoutImpl, @@ -135,6 +136,7 @@ private class NestedScrollToSceneNode(              topOrLeftBehavior = topOrLeftBehavior,              bottomOrRightBehavior = bottomOrRightBehavior,              isExternalOverscrollGesture = isExternalOverscrollGesture, +            pointersInfoOwner = { pointersInfoOwner.pointersInfo() }          )      private var nestedScrollNode: DelegatableNode = @@ -144,6 +146,7 @@ private class NestedScrollToSceneNode(          )      override fun onAttach() { +        pointersInfoOwner = requireAncestorPointersInfoOwner()          delegate(nestedScrollNode)      } @@ -171,6 +174,7 @@ private class NestedScrollToSceneNode(                  topOrLeftBehavior = topOrLeftBehavior,                  bottomOrRightBehavior = bottomOrRightBehavior,                  isExternalOverscrollGesture = isExternalOverscrollGesture, +                pointersInfoOwner = pointersInfoOwner,              )          nestedScrollNode =              nestedScrollModifierNode( @@ -187,6 +191,7 @@ private fun scenePriorityNestedScrollConnection(      topOrLeftBehavior: NestedScrollBehavior,      bottomOrRightBehavior: NestedScrollBehavior,      isExternalOverscrollGesture: () -> Boolean, +    pointersInfoOwner: PointersInfoOwner,  ) =      NestedScrollHandlerImpl(              layoutImpl = layoutImpl, @@ -194,5 +199,6 @@ private fun scenePriorityNestedScrollConnection(              topOrLeftBehavior = topOrLeftBehavior,              bottomOrRightBehavior = bottomOrRightBehavior,              isExternalOverscrollGesture = isExternalOverscrollGesture, +            pointersInfoOwner = pointersInfoOwner,          )          .connection diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index 65b388f66a0d..ff83d4b93af8 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -113,7 +113,10 @@ class DraggableHandlerTest {                      orientation = draggableHandler.orientation,                      topOrLeftBehavior = nestedScrollBehavior,                      bottomOrRightBehavior = nestedScrollBehavior, -                    isExternalOverscrollGesture = { isExternalOverscrollGesture } +                    isExternalOverscrollGesture = { isExternalOverscrollGesture }, +                    pointersInfoOwner = { +                        PointersInfo(startedPosition = Offset.Zero, pointersDown = 1) +                    }                  )                  .connection diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index 2de6faaccca2..1ae999282afa 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -839,6 +839,80 @@ class ElementTest {      }      @Test +    fun elementTransitionDuringNestedScrollWith2Pointers() { +        // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is +        // detected as a drag event. +        var touchSlop = 0f +        val translateY = 10.dp +        val layoutWidth = 200.dp +        val layoutHeight = 400.dp + +        val state = +            rule.runOnUiThread { +                MutableSceneTransitionLayoutState( +                    initialScene = SceneA, +                    transitions = +                        transitions { +                            from(SceneA, to = SceneB) { +                                translate(TestElements.Foo, y = translateY) +                            } +                        }, +                ) +                    as MutableSceneTransitionLayoutStateImpl +            } + +        rule.setContent { +            touchSlop = LocalViewConfiguration.current.touchSlop +            SceneTransitionLayout( +                state = state, +                modifier = Modifier.size(layoutWidth, layoutHeight) +            ) { +                scene( +                    SceneA, +                    userActions = mapOf(Swipe(SwipeDirection.Down, pointerCount = 2) to SceneB) +                ) { +                    Box( +                        Modifier +                            // Unconsumed scroll gesture will be intercepted by STL +                            .verticalNestedScrollToScene() +                            // A scrollable that does not consume the scroll gesture +                            .scrollable( +                                rememberScrollableState(consumeScrollDelta = { 0f }), +                                Orientation.Vertical +                            ) +                            .fillMaxSize() +                    ) { +                        Spacer(Modifier.element(TestElements.Foo).fillMaxSize()) +                    } +                } +                scene(SceneB) { Spacer(Modifier.fillMaxSize()) } +            } +        } + +        assertThat(state.transitionState).isIdle() +        val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag) +        fooElement.assertTopPositionInRootIsEqualTo(0.dp) + +        // Swipe down with 2 pointers by half of verticalSwipeDistance. +        rule.onRoot().performTouchInput { +            val middleTop = Offset((layoutWidth / 2).toPx(), 0f) +            repeat(2) { i -> down(pointerId = i, middleTop) } +            repeat(2) { i -> +                // Scroll 50% +                moveBy( +                    pointerId = i, +                    delta = Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), +                    delayMillis = 1_000, +                ) +            } +        } + +        val transition = assertThat(state.transitionState).isTransition() +        assertThat(transition).hasProgress(0.5f) +        fooElement.assertTopPositionInRootIsEqualTo(translateY * 0.5f) +    } + +    @Test      fun elementTransitionWithDistanceDuringOverscroll() {          val layoutWidth = 200.dp          val layoutHeight = 400.dp diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt index 460b640f0f5f..ecafb170535d 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt @@ -128,7 +128,7 @@ class MultiPointerDraggableTest {          var started = false          var dragged = false          var stopped = false -        var consumeBeforeMultiPointerDraggable = false +        var consumedByDescendant = false          var touchSlop = 0f          rule.setContent { @@ -138,6 +138,7 @@ class MultiPointerDraggableTest {                      .multiPointerDraggable(                          orientation = Orientation.Vertical,                          enabled = { true }, +                        // We want to start a drag gesture immediately                          startDragImmediately = { true },                          onDragStarted = { _, _, _ ->                              started = true @@ -157,7 +158,7 @@ class MultiPointerDraggableTest {                              awaitPointerEventScope {                                  while (isActive) {                                      val change = awaitPointerEvent().changes.first() -                                    if (consumeBeforeMultiPointerDraggable) { +                                    if (consumedByDescendant) {                                          change.consume()                                      }                                  } @@ -168,18 +169,19 @@ class MultiPointerDraggableTest {          }          // The first part of the gesture is consumed by our descendant -        consumeBeforeMultiPointerDraggable = true +        consumedByDescendant = true          rule.onRoot().performTouchInput {              down(middle)              moveBy(Offset(0f, touchSlop))          } -        started = false -        dragged = false -        stopped = false +        // The events were consumed by our descendant, we should not start a drag gesture. +        assertThat(started).isFalse() +        assertThat(dragged).isFalse() +        assertThat(stopped).isFalse()          // The next events could be consumed by us -        consumeBeforeMultiPointerDraggable = false +        consumedByDescendant = false          rule.onRoot().performTouchInput {              // The pointer is moved to a new position without reporting it              updatePointerBy(0, Offset(0f, touchSlop)) @@ -188,7 +190,7 @@ class MultiPointerDraggableTest {              up()          } -        // This event should not be used to start a drag gesture +        // The "up" event should not be used to start a drag gesture          assertThat(started).isFalse()          assertThat(dragged).isFalse()          assertThat(stopped).isFalse() diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index de6f1cc518f4..41bf630e9ae4 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -314,7 +314,6 @@ class SceneTransitionLayoutStateTest {                          }                      },              ) -                as MutableSceneTransitionLayoutStateImpl          // Default transition from A to B.          assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index 86a99e0de5bf..6412276ba34b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -32,11 +32,9 @@ import android.app.DreamManager;  import android.content.res.Resources;  import android.graphics.Region;  import android.os.Handler; -import android.platform.test.annotations.DisableFlags;  import android.platform.test.annotations.EnableFlags;  import android.testing.TestableLooper.RunWithLooper;  import android.view.AttachedSurfaceControl; -import android.view.View;  import android.view.ViewGroup;  import android.view.ViewRootImpl;  import android.view.ViewTreeObserver; @@ -46,7 +44,6 @@ import androidx.test.filters.SmallTest;  import com.android.dream.lowlight.LowLightTransitionCoordinator;  import com.android.keyguard.BouncerPanelExpansionCalculator; -import com.android.systemui.Flags;  import com.android.systemui.SysuiTestCase;  import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;  import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController; @@ -101,9 +98,6 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {      ViewGroup mDreamOverlayContentView;      @Mock -    View mHubGestureIndicatorView; - -    @Mock      Handler mHandler;      @Mock @@ -158,7 +152,6 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {                  mDreamOverlayContainerView,                  mComplicationHostViewController,                  mDreamOverlayContentView, -                mHubGestureIndicatorView,                  mAmbientStatusBarViewController,                  mLowLightTransitionCoordinator,                  mTouchInsetSession, @@ -179,18 +172,6 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {                  mDreamManager);      } -    @DisableFlags(Flags.FLAG_COMMUNAL_HUB) -    @Test -    public void testHubGestureIndicatorGoneWhenFlagOff() { -        verify(mHubGestureIndicatorView, never()).setVisibility(View.VISIBLE); -    } - -    @EnableFlags({Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_GLANCEABLE_HUB_GESTURE_HANDLE}) -    @Test -    public void testHubGestureIndicatorVisibleWhenFlagOn() { -        verify(mHubGestureIndicatorView).setVisibility(View.VISIBLE); -    } -      @Test      public void testRootSurfaceControlInsetSetOnAttach() {          mController.onViewAttached(); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt index 0f5e45814608..8914c80cdd5e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt @@ -41,6 +41,7 @@ import com.android.systemui.authentication.data.repository.fakeAuthenticationRep  import com.android.systemui.authentication.shared.model.AuthenticationMethodModel  import com.android.systemui.coroutines.collectLastValue  import com.android.systemui.coroutines.collectValues +import com.android.systemui.flags.DisableSceneContainer  import com.android.systemui.flags.EnableSceneContainer  import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository  import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository @@ -54,6 +55,9 @@ import com.android.systemui.power.domain.interactor.PowerInteractor  import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest  import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest  import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.scene.data.repository.Transition +import com.android.systemui.scene.data.repository.setSceneTransition +import com.android.systemui.scene.shared.model.Scenes  import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor  import com.android.systemui.testKosmos  import com.google.common.truth.Truth.assertThat @@ -208,6 +212,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {          }      @Test +    @DisableSceneContainer      fun showWhenLockedActivityLaunchedFromPowerGesture_falseIfReturningToGone() =          testScope.runTest {              val values by collectValues(underTest.showWhenLockedActivityLaunchedFromPowerGesture) @@ -245,6 +250,42 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {      @Test      @EnableSceneContainer +    fun showWhenLockedActivityLaunchedFromPowerGesture_falseIfReturningToGone_scene_container() = +        testScope.runTest { +            val values by collectValues(underTest.showWhenLockedActivityLaunchedFromPowerGesture) +            powerInteractor.setAwakeForTest() +            kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone)) + +            powerInteractor.setAsleepForTest() + +            kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen)) +            transitionRepository.sendTransitionSteps( +                from = KeyguardState.UNDEFINED, +                to = KeyguardState.AOD, +                testScope = testScope, +                throughTransitionState = TransitionState.RUNNING +            ) + +            powerInteractor.onCameraLaunchGestureDetected() +            kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) +            powerInteractor.setAwakeForTest() +            runCurrent() + +            kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone)) +            transitionRepository.sendTransitionSteps( +                from = KeyguardState.AOD, +                to = KeyguardState.UNDEFINED, +                testScope = testScope, +            ) + +            assertThat(values) +                .containsExactly( +                    false, +                ) +        } + +    @Test +    @EnableSceneContainer      fun occludingActivityWillDismissKeyguard() =          testScope.runTest {              val occludingActivityWillDismissKeyguard by diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt new file mode 100644 index 000000000000..8a9720ea3cb0 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt @@ -0,0 +1,104 @@ +/* + * 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.statusbar.notification.collection.coordinator + +import android.app.NotificationChannel +import android.app.NotificationChannel.NEWS_ID +import android.app.NotificationChannel.PROMOTIONS_ID +import android.app.NotificationChannel.RECS_ID +import android.app.NotificationChannel.SOCIAL_MEDIA_ID +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.collection.render.NodeController +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper +class BundleCoordinatorTest : SysuiTestCase() { +    @Mock private lateinit var newsController: NodeController +    @Mock private lateinit var socialController: NodeController +    @Mock private lateinit var recsController: NodeController +    @Mock private lateinit var promoController: NodeController + +    private lateinit var coordinator: BundleCoordinator + +    @Before +    fun setUp() { +        MockitoAnnotations.initMocks(this) +        coordinator = +            BundleCoordinator( +                newsController, +                socialController, +                recsController, +                promoController +            ) +    } + +    @Test +    fun newsSectioner() { +        assertThat(coordinator.newsSectioner.isInSection(makeEntryOfChannelType(NEWS_ID))).isTrue() +        assertThat(coordinator.newsSectioner.isInSection(makeEntryOfChannelType("news"))).isFalse() +    } + +    @Test +    fun socialSectioner() { +        assertThat(coordinator.socialSectioner.isInSection(makeEntryOfChannelType(SOCIAL_MEDIA_ID))) +            .isTrue() +        assertThat(coordinator.socialSectioner.isInSection(makeEntryOfChannelType("social"))) +            .isFalse() +    } + +    @Test +    fun recsSectioner() { +        assertThat(coordinator.recsSectioner.isInSection(makeEntryOfChannelType(RECS_ID))).isTrue() +        assertThat(coordinator.recsSectioner.isInSection(makeEntryOfChannelType("recommendations"))) +            .isFalse() +    } + +    @Test +    fun promoSectioner() { +        assertThat(coordinator.promoSectioner.isInSection(makeEntryOfChannelType(PROMOTIONS_ID))) +            .isTrue() +        assertThat(coordinator.promoSectioner.isInSection(makeEntryOfChannelType("promo"))). +        isFalse() +    } + +    private fun makeEntryOfChannelType( +        type: String, +        buildBlock: NotificationEntryBuilder.() -> Unit = {} +    ): NotificationEntry { +        val channel: NotificationChannel = NotificationChannel(type, type, 2) +        val entry = +            NotificationEntryBuilder() +                .updateRanking { +                    it.setChannel(channel) +                } +                .also(buildBlock) +                .build() +        return entry +    } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt index 6b5d07282a08..495ab61600f9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt @@ -16,6 +16,7 @@  package com.android.systemui.statusbar.policy  import android.app.Notification +import android.os.Handler  import android.platform.test.annotations.EnableFlags  import android.testing.TestableLooper.RunWithLooper  import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -59,6 +60,9 @@ class AvalancheControllerTest : SysuiTestCase() {      // For creating TestableHeadsUpManager      @Mock private val mAccessibilityMgr: AccessibilityManagerWrapper? = null      private val mUiEventLoggerFake = UiEventLoggerFake() + +    @Mock private lateinit var mBgHandler: Handler +      private val mLogger = Mockito.spy(HeadsUpManagerLogger(logcatLogBuffer()))      private val mGlobalSettings = FakeGlobalSettings()      private val mSystemClock = FakeSystemClock() @@ -78,7 +82,7 @@ class AvalancheControllerTest : SysuiTestCase() {          // Initialize AvalancheController and TestableHeadsUpManager during setUp instead of          // declaration, where mocks are null -        mAvalancheController = AvalancheController(dumpManager, mUiEventLoggerFake) +        mAvalancheController = AvalancheController(dumpManager, mUiEventLoggerFake, mBgHandler)          testableHeadsUpManager =              TestableHeadsUpManager( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java index 206b39c28da3..df07b446667a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.when;  import android.app.Notification;  import android.app.PendingIntent;  import android.app.Person; +import android.os.Handler;  import android.platform.test.annotations.DisableFlags;  import android.platform.test.annotations.EnableFlags;  import android.platform.test.flag.junit.FlagsParameterization; @@ -81,7 +82,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {      private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();      private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer())); - +    @Mock private Handler mBgHandler;      @Mock private DumpManager dumpManager;      private AvalancheController mAvalancheController; @@ -148,7 +149,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {      @Override      public void SysuiSetup() throws Exception {          super.SysuiSetup(); -        mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake); +        mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake, mBgHandler);      }      @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java index 7346323f733d..3d3438eab109 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java @@ -104,8 +104,7 @@ public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {                  UiEventLogger uiEventLogger,                  JavaAdapter javaAdapter,                  ShadeInteractor shadeInteractor, -                AvalancheController avalancheController, -                Handler bgHandler +                AvalancheController avalancheController          ) {              super(                      context, @@ -123,8 +122,7 @@ public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {                      uiEventLogger,                      javaAdapter,                      shadeInteractor, -                    avalancheController, -                    bgHandler +                    avalancheController              );              mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;              mAutoDismissTime = TEST_AUTO_DISMISS_TIME; @@ -147,8 +145,7 @@ public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {                  mUiEventLogger,                  mJavaAdapter,                  mShadeInteractor, -                mAvalancheController, -                mBgHandler +                mAvalancheController          );      } @@ -173,7 +170,7 @@ public class HeadsUpManagerPhoneTest extends BaseHeadsUpManagerTest {          mContext.getOrCreateTestableResources().addOverride(                  R.integer.ambient_notification_extension_time, 500); -        mAvalancheController = new AvalancheController(dumpManager, mUiEventLogger); +        mAvalancheController = new AvalancheController(dumpManager, mUiEventLogger, mBgHandler);      }      @Test diff --git a/packages/SystemUI/res/drawable/hub_handle.xml b/packages/SystemUI/res/drawable/hub_handle.xml deleted file mode 100644 index 8bc276ffed9e..000000000000 --- a/packages/SystemUI/res/drawable/hub_handle.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- -  ~ Copyright (C) 2024 The Android Open Source Project -  ~ -  ~ Licensed under the Apache License, Version 2.0 (the "License"); -  ~ you may not use this file except in compliance with the License. -  ~ You may obtain a copy of the License at -  ~ -  ~      http://www.apache.org/licenses/LICENSE-2.0 -  ~ -  ~ Unless required by applicable law or agreed to in writing, software -  ~ distributed under the License is distributed on an "AS IS" BASIS, -  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -  ~ See the License for the specific language governing permissions and -  ~ limitations under the License. -  --> - -<shape xmlns:android="http://schemas.android.com/apk/res/android"> -    <corners android:radius="4dp" /> -    <solid android:color="#FFFFFF" /> -</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/app_clips_screenshot.xml b/packages/SystemUI/res/layout/app_clips_screenshot.xml index 6d4e410b3397..b09d35d46ca0 100644 --- a/packages/SystemUI/res/layout/app_clips_screenshot.xml +++ b/packages/SystemUI/res/layout/app_clips_screenshot.xml @@ -16,6 +16,7 @@    -->  <androidx.constraintlayout.widget.ConstraintLayout      xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"      xmlns:app="http://schemas.android.com/apk/res-auto"      xmlns:tools="http://schemas.android.com/tools"      android:background="@null" @@ -30,9 +31,10 @@          android:layout_height="48dp"          android:layout_marginStart="8dp"          android:background="@drawable/overlay_button_background" +        android:backgroundTint="?androidprv:attr/materialColorPrimary"          android:paddingHorizontal="24dp"          android:text="@string/app_clips_save_add_to_note" -        android:textColor="?android:textColorSecondary" +        android:textColor="?androidprv:attr/materialColorOnPrimary"          app:layout_constraintBottom_toTopOf="@id/preview"          app:layout_constraintStart_toStartOf="parent"          app:layout_constraintTop_toTopOf="parent" /> diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml index dcd3fa66ef04..be1652b60250 100644 --- a/packages/SystemUI/res/layout/dream_overlay_container.xml +++ b/packages/SystemUI/res/layout/dream_overlay_container.xml @@ -21,19 +21,6 @@      android:layout_width="match_parent"      android:layout_height="match_parent"> -    <ImageView -        android:id="@+id/glanceable_hub_handle" -        android:layout_width="4dp" -        android:layout_height="220dp" -        android:layout_centerVertical="true" -        android:layout_marginEnd="12dp" -        android:background="@drawable/hub_handle" -        android:visibility="gone" -        android:contentDescription="UI indicator for swiping open the glanceable hub" -        app:layout_constraintBottom_toBottomOf="parent" -        app:layout_constraintEnd_toEndOf="parent" -        app:layout_constraintTop_toTopOf="parent" /> -      <androidx.constraintlayout.widget.ConstraintLayout          android:id="@+id/dream_overlay_content"          android:layout_width="match_parent" diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt index 6032f0b7b618..b33924ca0b70 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt @@ -18,10 +18,10 @@ package com.android.systemui.accessibility.data.repository  import android.view.accessibility.AccessibilityManager  import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener -import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import com.android.app.tracing.FlowTracing.tracedAwaitClose +import com.android.app.tracing.FlowTracing.tracedConflatedCallbackFlow  import dagger.Module  import dagger.Provides -import kotlinx.coroutines.channels.awaitClose  import kotlinx.coroutines.flow.Flow  import kotlinx.coroutines.flow.distinctUntilChanged @@ -38,24 +38,28 @@ interface AccessibilityRepository {      }  } +private const val TAG = "AccessibilityRepository" +  private class AccessibilityRepositoryImpl(      manager: AccessibilityManager,  ) : AccessibilityRepository {      override val isTouchExplorationEnabled: Flow<Boolean> = -        conflatedCallbackFlow { +        tracedConflatedCallbackFlow(TAG) {                  val listener = TouchExplorationStateChangeListener(::trySend)                  manager.addTouchExplorationStateChangeListener(listener)                  trySend(manager.isTouchExplorationEnabled) -                awaitClose { manager.removeTouchExplorationStateChangeListener(listener) } +                tracedAwaitClose(TAG) { +                    manager.removeTouchExplorationStateChangeListener(listener) +                }              }              .distinctUntilChanged()      override val isEnabled: Flow<Boolean> = -        conflatedCallbackFlow { +        tracedConflatedCallbackFlow(TAG) {                  val listener = AccessibilityManager.AccessibilityStateChangeListener(::trySend)                  manager.addAccessibilityStateChangeListener(listener)                  trySend(manager.isEnabled) -                awaitClose { manager.removeAccessibilityStateChangeListener(listener) } +                tracedAwaitClose(TAG) { manager.removeAccessibilityStateChangeListener(listener) }              }              .distinctUntilChanged()  } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index d6d40f28d288..b466f31cc509 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -995,6 +995,16 @@ public class AuthController implements      }      /** +     * @return true if ultrasonic udfps HW is supported on this device. Can return true even if +     * the user has not enrolled udfps. This may be false if called before +     * onAllAuthenticatorsRegistered. +     */ +    public boolean isUltrasonicUdfpsSupported() { +        return getUdfpsProps() != null && !getUdfpsProps().isEmpty() && getUdfpsProps() +                .get(0).isUltrasonicUdfps(); +    } + +    /**       * @return true if sfps HW is supported on this device. Can return true even if the user has       * not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.       */ diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 9d3c6a49a616..3dd375846499 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -270,6 +270,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {          @Override          public void showUdfpsOverlay(long requestId, int sensorId, int reason,                  @NonNull IUdfpsOverlayControllerCallback callback) { +            mUdfpsOverlayInteractor.setRequestId(requestId);              mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(                      new UdfpsControllerOverlay(                          mContext, @@ -404,6 +405,15 @@ public class UdfpsController implements DozeReceiver, Dumpable {                      handler::post,                      authenticationCallback);          } + +        /** +         * Debug to run setIgnoreDisplayTouches +         */ +        public void debugSetIgnoreDisplayTouches(boolean ignoreTouch) { +            final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L; +            UdfpsController.this.mFingerprintManager.setIgnoreDisplayTouches( +                    requestId, mSensorProps.sensorId, ignoreTouch); +        }      }      /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt index f5e3d29cb878..97ece11b8fbb 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt @@ -75,6 +75,8 @@ class UdfpsShell @Inject constructor(commandRegistry: CommandRegistry) : Command              simFingerUp()          } else if (args.size == 1 && args[0] == "biometricPrompt") {              launchBiometricPrompt() +        } else if (args.size == 2 && args[0] == "setIgnoreDisplayTouches") { +            setIgnoreDisplayTouches(args[1].toBoolean())          } else {              invalidCommand(pw)          } @@ -186,6 +188,11 @@ class UdfpsShell @Inject constructor(commandRegistry: CommandRegistry) : Command          upEvent?.recycle()      } +    @VisibleForTesting +    fun setIgnoreDisplayTouches(ignoreTouches: Boolean) { +        udfpsOverlayController?.debugSetIgnoreDisplayTouches(ignoreTouches) +    } +      private fun obtainMotionEvent(          action: Int,          x: Float, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt index a77cc1fea6a6..bb450c0b6d90 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt @@ -17,6 +17,7 @@  package com.android.systemui.biometrics.domain.interactor  import android.content.Context +import android.hardware.fingerprint.FingerprintManager  import android.util.Log  import android.view.MotionEvent  import com.android.systemui.biometrics.AuthController @@ -46,6 +47,7 @@ constructor(      @Application private val context: Context,      private val authController: AuthController,      private val selectedUserInteractor: SelectedUserInteractor, +    private val fingerprintManager: FingerprintManager?,      @Application scope: CoroutineScope  ) {      private fun calculateIconSize(): Int { @@ -70,8 +72,25 @@ constructor(          return isUdfpsEnrolled && isWithinOverlayBounds      } +    private var _requestId = MutableStateFlow(0L) + +    /** RequestId of current AcquisitionClient */ +    val requestId: StateFlow<Long> = _requestId.asStateFlow() + +    fun setRequestId(requestId: Long) { +        _requestId.value = requestId +    } +      /** Sets whether Udfps overlay should handle touches */      fun setHandleTouches(shouldHandle: Boolean = true) { +        if (authController.isUltrasonicUdfpsSupported +                && shouldHandle != _shouldHandleTouches.value) { +            fingerprintManager?.setIgnoreDisplayTouches( +                requestId.value, +                authController.udfpsProps!!.get(0).sensorId, +                !shouldHandle +            ) +        }          _shouldHandleTouches.value = shouldHandle      } diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt index f991d5b8405f..8270db1e89a8 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt @@ -16,7 +16,7 @@  package com.android.systemui.brightness.ui.compose -import androidx.compose.animation.core.animateIntAsState +import androidx.compose.animation.core.animateFloatAsState  import androidx.compose.foundation.clickable  import androidx.compose.foundation.layout.fillMaxWidth  import androidx.compose.foundation.layout.size @@ -57,12 +57,12 @@ private fun BrightnessSlider(  ) {      var value by remember(gammaValue) { mutableIntStateOf(gammaValue) }      val animatedValue by -        animateIntAsState(targetValue = value, label = "BrightnessSliderAnimatedValue") +        animateFloatAsState(targetValue = value.toFloat(), label = "BrightnessSliderAnimatedValue")      val floatValueRange = valueRange.first.toFloat()..valueRange.last.toFloat() -    val isRestricted = restriction is PolicyRestriction.Restricted +    val isRestricted = remember(restriction) { restriction is PolicyRestriction.Restricted }      PlatformSlider( -        value = animatedValue.toFloat(), +        value = animatedValue,          valueRange = floatValueRange,          enabled = !isRestricted,          onValueChange = { diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index f9f01f70e2dd..780bf70f3f7b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -322,14 +322,6 @@ constructor(          not(shadeInteractor.isAnyFullyExpanded)              .stateIn(bgScope, SharingStarted.Eagerly, initialValue = false) -    // TODO(b/339667383): remove this temporary swipe gesture handle -    /** -     * The dream overlay has its own gesture handle as the SysUI window is not visible above the -     * dream. This flow will be false when dreaming so that we don't show a duplicate handle when -     * opening the hub over the dream. -     */ -    val showGestureIndicator: Flow<Boolean> = not(keyguardInteractor.isDreaming) -      /** The type of background to use for the hub. */      val communalBackground: Flow<CommunalBackgroundType> =          communalSettingsInteractor.communalBackground diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index 4fd1c24dfa67..76c7d2383751 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -21,8 +21,6 @@ import static android.service.dreams.Flags.dreamHandlesBeingObscured;  import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;  import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamAlphaScaledExpansion;  import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamYPositionScaledExpansion; -import static com.android.systemui.Flags.communalHub; -import static com.android.systemui.Flags.glanceableHubGestureHandle;  import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM;  import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP;  import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; @@ -192,7 +190,6 @@ public class DreamOverlayContainerViewController extends              DreamOverlayContainerView containerView,              ComplicationHostViewController complicationHostViewController,              @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView, -            @Named(DreamOverlayModule.HUB_GESTURE_INDICATOR_VIEW) View hubGestureIndicatorView,              AmbientStatusBarViewController statusBarViewController,              LowLightTransitionCoordinator lowLightTransitionCoordinator,              TouchInsetManager.TouchInsetSession touchInsetSession, @@ -230,12 +227,6 @@ public class DreamOverlayContainerViewController extends          mComplicationHostViewController = complicationHostViewController;          mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize(                  R.dimen.dream_overlay_y_offset); - -        if (communalHub() && glanceableHubGestureHandle()) { -            // TODO(b/339667383): remove this temporary swipe gesture handle -            hubGestureIndicatorView.setVisibility(View.VISIBLE); -        } -          final View view = mComplicationHostViewController.getView();          mDreamOverlayContentView.addView(view, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java index 76fcabd635d8..12984efb52c2 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java @@ -18,7 +18,6 @@ package com.android.systemui.dreams.dagger;  import android.content.res.Resources;  import android.view.LayoutInflater; -import android.view.View;  import android.view.ViewGroup;  import androidx.lifecycle.Lifecycle; @@ -42,7 +41,6 @@ import javax.inject.Named;  @Module  public abstract class DreamOverlayModule {      public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view"; -    public static final String HUB_GESTURE_INDICATOR_VIEW = "hub_gesture_indicator_view";      public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset";      public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =              "burn_in_protection_update_interval"; @@ -75,18 +73,6 @@ public abstract class DreamOverlayModule {                  "R.id.dream_overlay_content must not be null");      } -    /** -     * Gesture indicator bar on the right edge of the screen to indicate to users that they can -     * swipe to see their widgets on lock screen. -     */ -    @Provides -    @DreamOverlayComponent.DreamOverlayScope -    @Named(HUB_GESTURE_INDICATOR_VIEW) -    public static View providesHubGestureIndicatorView(DreamOverlayContainerView view) { -        return Preconditions.checkNotNull(view.findViewById(R.id.glanceable_hub_handle), -                "R.id.glanceable_hub_handle must not be null"); -    } -      /** */      @Provides      public static TouchInsetManager.TouchInsetSession providesTouchInsetSession( diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt index f54fd2269679..b47fb65ae9e8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt @@ -16,12 +16,18 @@  package com.android.systemui.keyboard.shortcut.data.repository +import android.content.Context +import android.hardware.input.InputManager +import android.util.Log +import android.view.InputDevice +import android.view.KeyCharacterMap  import android.view.KeyEvent  import android.view.KeyboardShortcutGroup  import android.view.KeyboardShortcutInfo  import android.view.WindowManager  import android.view.WindowManager.KeyboardShortcutsReceiver  import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background  import com.android.systemui.keyboard.shortcut.data.source.KeyboardShortcutGroupsSource  import com.android.systemui.keyboard.shortcut.qualifiers.MultitaskingShortcuts  import com.android.systemui.keyboard.shortcut.qualifiers.SystemShortcuts @@ -30,29 +36,46 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory  import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType  import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.IME  import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MULTI_TASKING +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.SYSTEM  import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand  import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey  import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory  import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher  import kotlinx.coroutines.flow.map  import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext  @SysUISingleton  class ShortcutHelperCategoriesRepository  @Inject  constructor( +    private val context: Context, +    @Background private val backgroundDispatcher: CoroutineDispatcher,      @SystemShortcuts private val systemShortcutsSource: KeyboardShortcutGroupsSource,      @MultitaskingShortcuts private val multitaskingShortcutsSource: KeyboardShortcutGroupsSource,      private val windowManager: WindowManager, -    shortcutHelperStateRepository: ShortcutHelperStateRepository +    private val inputManager: InputManager, +    stateRepository: ShortcutHelperStateRepository  ) { -    val systemShortcutsCategory = -        shortcutHelperStateRepository.state.map { +    private val activeInputDevice = +        stateRepository.state.map {              if (it is Active) { +                withContext(backgroundDispatcher) { inputManager.getInputDevice(it.deviceId) } +            } else { +                null +            } +        } + +    val systemShortcutsCategory = +        activeInputDevice.map { +            if (it != null) {                  toShortcutCategory( -                    systemShortcutsSource.shortcutGroups(), -                    ShortcutCategoryType.SYSTEM +                    it.keyCharacterMap, +                    SYSTEM, +                    systemShortcutsSource.shortcutGroups()                  )              } else {                  null @@ -60,76 +83,128 @@ constructor(          }      val multitaskingShortcutsCategory = -        shortcutHelperStateRepository.state.map { -            if (it is Active) { -                toShortcutCategory(multitaskingShortcutsSource.shortcutGroups(), MULTI_TASKING) +        activeInputDevice.map { +            if (it != null) { +                toShortcutCategory( +                    it.keyCharacterMap, +                    MULTI_TASKING, +                    multitaskingShortcutsSource.shortcutGroups() +                )              } else {                  null              }          }      val imeShortcutsCategory = -        shortcutHelperStateRepository.state.map { -            if (it is Active) retrieveImeShortcuts(it.deviceId) else null -        } +        activeInputDevice.map { if (it != null) retrieveImeShortcuts(it) else null } -    private suspend fun retrieveImeShortcuts(deviceId: Int): ShortcutCategory? { +    private suspend fun retrieveImeShortcuts( +        inputDevice: InputDevice, +    ): ShortcutCategory? {          return suspendCancellableCoroutine { continuation ->              val shortcutsReceiver = KeyboardShortcutsReceiver { shortcutGroups -> -                continuation.resumeWith(Result.success(toShortcutCategory(shortcutGroups, IME))) +                continuation.resumeWith( +                    Result.success( +                        toShortcutCategory(inputDevice.keyCharacterMap, IME, shortcutGroups) +                    ) +                )              } -            windowManager.requestImeKeyboardShortcuts(shortcutsReceiver, deviceId) +            windowManager.requestImeKeyboardShortcuts(shortcutsReceiver, inputDevice.id)          }      }      private fun toShortcutCategory( -        shortcutGroups: List<KeyboardShortcutGroup>, +        keyCharacterMap: KeyCharacterMap,          type: ShortcutCategoryType, +        shortcutGroups: List<KeyboardShortcutGroup>,      ): ShortcutCategory? {          val subCategories =              shortcutGroups                  .map { shortcutGroup ->                      ShortcutSubCategory( -                        label = shortcutGroup.label.toString(), -                        shortcuts = toShortcuts(shortcutGroup.items) +                        shortcutGroup.label.toString(), +                        toShortcuts(keyCharacterMap, shortcutGroup.items)                      )                  }                  .filter { it.shortcuts.isNotEmpty() }          return if (subCategories.isEmpty()) { +            Log.wtf(TAG, "Empty sub categories after converting $shortcutGroups")              null          } else {              ShortcutCategory(type, subCategories)          }      } -    private fun toShortcuts(infoList: List<KeyboardShortcutInfo>) = -        infoList.mapNotNull { toShortcut(it) } +    private fun toShortcuts( +        keyCharacterMap: KeyCharacterMap, +        infoList: List<KeyboardShortcutInfo> +    ) = infoList.mapNotNull { toShortcut(keyCharacterMap, it) } -    private fun toShortcut(shortcutInfo: KeyboardShortcutInfo): Shortcut? { -        val shortcutCommand = toShortcutCommand(shortcutInfo) +    private fun toShortcut( +        keyCharacterMap: KeyCharacterMap, +        shortcutInfo: KeyboardShortcutInfo +    ): Shortcut? { +        val shortcutCommand = toShortcutCommand(keyCharacterMap, shortcutInfo)          return if (shortcutCommand == null) null          else Shortcut(label = shortcutInfo.label!!.toString(), commands = listOf(shortcutCommand))      } -    private fun toShortcutCommand(info: KeyboardShortcutInfo): ShortcutCommand? { -        val keyCodes = mutableListOf<Int>() +    private fun toShortcutCommand( +        keyCharacterMap: KeyCharacterMap, +        info: KeyboardShortcutInfo +    ): ShortcutCommand? { +        val keys = mutableListOf<ShortcutKey>()          var remainingModifiers = info.modifiers          SUPPORTED_MODIFIERS.forEach { supportedModifier ->              if ((supportedModifier and remainingModifiers) != 0) { -                keyCodes += supportedModifier +                keys += toShortcutKey(keyCharacterMap, supportedModifier) ?: return null                  // "Remove" the modifier from the remaining modifiers                  remainingModifiers = remainingModifiers and supportedModifier.inv()              }          }          if (remainingModifiers != 0) {              // There is a remaining modifier we don't support +            Log.wtf(TAG, "Unsupported modifiers remaining: $remainingModifiers")              return null          } -        keyCodes += info.keycode -        return ShortcutCommand(keyCodes) +        if (info.keycode != 0) { +            keys += toShortcutKey(keyCharacterMap, info.keycode, info.baseCharacter) ?: return null +        } +        if (keys.isEmpty()) { +            Log.wtf(TAG, "No keys for $info") +            return null +        } +        return ShortcutCommand(keys) +    } + +    private fun toShortcutKey( +        keyCharacterMap: KeyCharacterMap, +        keyCode: Int, +        baseCharacter: Char = Char.MIN_VALUE, +    ): ShortcutKey? { +        val iconResId = ShortcutHelperKeys.keyIcons[keyCode] +        if (iconResId != null) { +            return ShortcutKey.Icon(iconResId) +        } +        if (baseCharacter > Char.MIN_VALUE) { +            return ShortcutKey.Text(baseCharacter.toString()) +        } +        val specialKeyLabel = ShortcutHelperKeys.specialKeyLabels[keyCode] +        if (specialKeyLabel != null) { +            val label = specialKeyLabel(context) +            return ShortcutKey.Text(label) +        } +        val displayLabelCharacter = keyCharacterMap.getDisplayLabel(keyCode) +        if (displayLabelCharacter.code != 0) { +            return ShortcutKey.Text(displayLabelCharacter.toString()) +        } +        Log.wtf(TAG, "Couldn't find label or icon for key: $keyCode") +        return null      }      companion object { +        private const val TAG = "SHCategoriesRepo" +          private val SUPPORTED_MODIFIERS =              listOf(                  KeyEvent.META_META_ON, diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt index e5b870a983c6..adc6d952f2c3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt @@ -21,8 +21,8 @@ data class Shortcut(val label: String, val commands: List<ShortcutCommand>)  class ShortcutBuilder(private val label: String) {      val commands = mutableListOf<ShortcutCommand>() -    fun command(vararg keyCodes: Int) { -        commands += ShortcutCommand(keyCodes.toList()) +    fun command(builder: ShortcutCommandBuilder.() -> Unit) { +        commands += ShortcutCommandBuilder().apply(builder).build()      }      fun build() = Shortcut(label, commands) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt index 3ac7fa8f8ece..5d0535905540 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt @@ -30,8 +30,8 @@ data class ShortcutCategory(  class ShortcutCategoryBuilder(val type: ShortcutCategoryType) {      private val subCategories = mutableListOf<ShortcutSubCategory>() -    fun subCategory(label: String, shortcuts: List<Shortcut>) { -        subCategories += ShortcutSubCategory(label, shortcuts) +    fun subCategory(label: String, builder: ShortcutSubCategoryBuilder.() -> Unit) { +        subCategories += ShortcutSubCategoryBuilder(label).apply(builder).build()      }      fun build() = ShortcutCategory(type, subCategories) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt index a98a8ffb7004..e5b8096f2403 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt @@ -16,4 +16,23 @@  package com.android.systemui.keyboard.shortcut.shared.model -data class ShortcutCommand(val keyCodes: List<Int>) +import androidx.annotation.DrawableRes + +data class ShortcutCommand(val keys: List<ShortcutKey>) + +class ShortcutCommandBuilder { +    private val keys = mutableListOf<ShortcutKey>() + +    fun key(text: String) { +        keys += ShortcutKey.Text(text) +    } + +    fun key(@DrawableRes drawableResId: Int) { +        keys += ShortcutKey.Icon(drawableResId) +    } + +    fun build() = ShortcutCommand(keys) +} + +fun shortcutCommand(block: ShortcutCommandBuilder.() -> Unit) = +    ShortcutCommandBuilder().apply(block).build() diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutKey.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutKey.kt new file mode 100644 index 000000000000..1abb78c54b99 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutKey.kt @@ -0,0 +1,25 @@ +/* + * 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.keyboard.shortcut.shared.model + +import androidx.annotation.DrawableRes + +sealed interface ShortcutKey { +    data class Text(val value: String) : ShortcutKey + +    data class Icon(@DrawableRes val drawableResId: Int) : ShortcutKey +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt index 4545b4c5dd28..14016783a478 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt @@ -17,3 +17,13 @@  package com.android.systemui.keyboard.shortcut.shared.model  data class ShortcutSubCategory(val label: String, val shortcuts: List<Shortcut>) + +class ShortcutSubCategoryBuilder(val label: String) { +    private val shortcuts = mutableListOf<Shortcut>() + +    fun shortcut(label: String, builder: ShortcutBuilder.() -> Unit) { +        shortcuts += ShortcutBuilder(label).apply(builder).build() +    } + +    fun build() = ShortcutSubCategory(label, shortcuts) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 5f6fe1c2130d..b32d0950e0d2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -27,6 +27,7 @@ import android.os.DeadObjectException  import android.os.Handler  import android.os.PowerManager  import android.os.RemoteException +import android.os.Trace  import android.util.Log  import android.view.RemoteAnimationTarget  import android.view.SurfaceControl @@ -385,10 +386,15 @@ class KeyguardUnlockAnimationController @Inject constructor(                          valueAnimator.animatedValue as Float, openingWallpaperTargets)              }              addListener(object : AnimatorListenerAdapter() { +                override fun onAnimationStart(animation: Animator) { +                    super.onAnimationStart(animation) +                    Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0) +                }                  override fun onAnimationEnd(animation: Animator) {                      Log.d(TAG, "wallpaperCannedUnlockAnimator#onAnimationEnd")                      keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(                          false /* cancelled */) +                    Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0)                  }              })          } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt index e9bd88932c52..e6bab4c7edcf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt @@ -19,8 +19,9 @@ package com.android.systemui.keyguard.data.quickaffordance  import android.content.Context  import android.os.UserHandle +import com.android.app.tracing.FlowTracing.tracedAwaitClose +import com.android.app.tracing.FlowTracing.tracedConflatedCallbackFlow  import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging -import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow  import com.android.systemui.dagger.SysUISingleton  import com.android.systemui.dagger.qualifiers.Application  import com.android.systemui.settings.UserTracker @@ -28,7 +29,6 @@ import com.android.systemui.shared.customization.data.content.CustomizationProvi  import javax.inject.Inject  import kotlinx.coroutines.CoroutineScope  import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.awaitClose  import kotlinx.coroutines.flow.Flow  import kotlinx.coroutines.flow.SharingStarted  import kotlinx.coroutines.flow.StateFlow @@ -55,19 +55,20 @@ constructor(      private val userHandle: UserHandle,  ) : KeyguardQuickAffordanceSelectionManager { -    private val userId: Flow<Int> = conflatedCallbackFlow { -        val callback = -            object : UserTracker.Callback { -                override fun onUserChanged(newUser: Int, userContext: Context) { -                    trySendWithFailureLogging(newUser, TAG) +    private val userId: Flow<Int> = +        tracedConflatedCallbackFlow("userId") { +            val callback = +                object : UserTracker.Callback { +                    override fun onUserChanged(newUser: Int, userContext: Context) { +                        trySendWithFailureLogging(newUser, TAG) +                    }                  } -            } -        userTracker.addCallback(callback) { it.run() } -        trySendWithFailureLogging(userTracker.userId, TAG) +            userTracker.addCallback(callback) { it.run() } +            trySendWithFailureLogging(userTracker.userId, TAG) -        awaitClose { userTracker.removeCallback(callback) } -    } +            tracedAwaitClose("userId") { userTracker.removeCallback(callback) } +        }      private val clientOrNull: StateFlow<CustomizationProviderClient?> =          userId diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index 63dd255c5de4..f837d8efdcc5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -62,6 +62,7 @@ import kotlinx.coroutines.flow.flowOn  import kotlinx.coroutines.flow.mapLatest  import kotlinx.coroutines.flow.onStart  import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch  /** Defines interface for classes that encapsulate application state for the keyguard. */  interface KeyguardRepository { @@ -362,30 +363,8 @@ constructor(      override val topClippingBounds = MutableStateFlow<Int?>(null) -    override val isKeyguardShowing: Flow<Boolean> = -        conflatedCallbackFlow { -                val callback = -                    object : KeyguardStateController.Callback { -                        override fun onKeyguardShowingChanged() { -                            trySendWithFailureLogging( -                                keyguardStateController.isShowing, -                                TAG, -                                "updated isKeyguardShowing" -                            ) -                        } -                    } - -                keyguardStateController.addCallback(callback) -                // Adding the callback does not send an initial update. -                trySendWithFailureLogging( -                    keyguardStateController.isShowing, -                    TAG, -                    "initial isKeyguardShowing" -                ) - -                awaitClose { keyguardStateController.removeCallback(callback) } -            } -            .distinctUntilChanged() +    override val isKeyguardShowing: MutableStateFlow<Boolean> = +        MutableStateFlow(keyguardStateController.isShowing)      private val _isAodAvailable = MutableStateFlow(false)      override val isAodAvailable: StateFlow<Boolean> = _isAodAvailable.asStateFlow() @@ -394,91 +373,14 @@ constructor(          _isAodAvailable.value = value      } -    override val isKeyguardOccluded: Flow<Boolean> = -        conflatedCallbackFlow { -                val callback = -                    object : KeyguardStateController.Callback { -                        override fun onKeyguardShowingChanged() { -                            trySendWithFailureLogging( -                                keyguardStateController.isOccluded, -                                TAG, -                                "updated isKeyguardOccluded" -                            ) -                        } -                    } - -                keyguardStateController.addCallback(callback) -                // Adding the callback does not send an initial update. -                trySendWithFailureLogging( -                    keyguardStateController.isOccluded, -                    TAG, -                    "initial isKeyguardOccluded" -                ) - -                awaitClose { keyguardStateController.removeCallback(callback) } -            } -            .distinctUntilChanged() - -    override val isKeyguardDismissible: StateFlow<Boolean> = -        conflatedCallbackFlow { -                val callback = -                    object : KeyguardStateController.Callback { -                        override fun onUnlockedChanged() { -                            trySendWithFailureLogging( -                                keyguardStateController.isUnlocked, -                                TAG, -                                "updated isKeyguardDismissible due to onUnlockedChanged" -                            ) -                        } - -                        override fun onKeyguardShowingChanged() { -                            trySendWithFailureLogging( -                                keyguardStateController.isUnlocked, -                                TAG, -                                "updated isKeyguardDismissible due to onKeyguardShowingChanged" -                            ) -                        } -                    } - -                keyguardStateController.addCallback(callback) -                // Adding the callback does not send an initial update. -                trySendWithFailureLogging( -                    keyguardStateController.isUnlocked, -                    TAG, -                    "initial isKeyguardUnlocked" -                ) +    override val isKeyguardOccluded: MutableStateFlow<Boolean> = +        MutableStateFlow(keyguardStateController.isOccluded) -                awaitClose { keyguardStateController.removeCallback(callback) } -            } -            .distinctUntilChanged() -            .stateIn( -                scope, -                SharingStarted.Eagerly, -                initialValue = false, -            ) +    override val isKeyguardDismissible: MutableStateFlow<Boolean> = +        MutableStateFlow(keyguardStateController.isUnlocked) -    override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow { -        val callback = -            object : KeyguardStateController.Callback { -                override fun onKeyguardGoingAwayChanged() { -                    trySendWithFailureLogging( -                        keyguardStateController.isKeyguardGoingAway, -                        TAG, -                        "updated isKeyguardGoingAway" -                    ) -                } -            } - -        keyguardStateController.addCallback(callback) -        // Adding the callback does not send an initial update. -        trySendWithFailureLogging( -            keyguardStateController.isKeyguardGoingAway, -            TAG, -            "initial isKeyguardGoingAway" -        ) - -        awaitClose { keyguardStateController.removeCallback(callback) } -    } +    override val isKeyguardGoingAway: MutableStateFlow<Boolean> = +        MutableStateFlow(keyguardStateController.isKeyguardGoingAway)      private val _isKeyguardEnabled =          MutableStateFlow(!lockPatternUtils.isLockScreenDisabled(userTracker.userId)) @@ -669,6 +571,35 @@ constructor(      private val _isActiveDreamLockscreenHosted = MutableStateFlow(false)      override val isActiveDreamLockscreenHosted = _isActiveDreamLockscreenHosted.asStateFlow() +    init { +        val callback = +            object : KeyguardStateController.Callback { +                override fun onKeyguardShowingChanged() { +                    isKeyguardShowing.value = keyguardStateController.isShowing +                    isKeyguardOccluded.value = keyguardStateController.isOccluded +                    isKeyguardDismissible.value = keyguardStateController.isUnlocked +                } + +                override fun onUnlockedChanged() { +                    isKeyguardDismissible.value = keyguardStateController.isUnlocked +                } + +                override fun onKeyguardGoingAwayChanged() { +                    isKeyguardGoingAway.value = keyguardStateController.isKeyguardGoingAway +                } +            } + +        keyguardStateController.addCallback(callback) + +        scope +            .launch { +                isKeyguardShowing.collect { +                    // no-op to allow for callback removal +                } +            } +            .invokeOnCompletion { keyguardStateController.removeCallback(callback) } +    } +      override fun setAnimateDozingTransitions(animate: Boolean) {          _animateBottomAreaDozingTransitions.value = animate      } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index 3877e7ae1e55..76e88a2a2cd6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -28,6 +28,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor  import com.android.systemui.keyguard.shared.model.BiometricUnlockMode.Companion.isWakeAndUnlock  import com.android.systemui.keyguard.shared.model.KeyguardState  import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlag  import com.android.systemui.util.kotlin.Utils.Companion.sample  import com.android.systemui.util.kotlin.sample  import javax.inject.Inject @@ -100,10 +101,14 @@ constructor(                          biometricUnlockState,                      ) ->                      if (isWakeAndUnlock(biometricUnlockState.mode)) { -                        startTransitionTo( -                            KeyguardState.GONE, -                            ownerReason = "biometric wake and unlock", -                        ) +                        if (SceneContainerFlag.isEnabled) { +                            // TODO(b/336576536): Check if adaptation for scene framework is needed +                        } else { +                            startTransitionTo( +                                KeyguardState.GONE, +                                ownerReason = "biometric wake and unlock", +                            ) +                        }                      }                  }          } @@ -131,21 +136,35 @@ constructor(                          isIdleOnCommunal,                          canTransitionToGoneOnWake,                          primaryBouncerShowing) -> -                    startTransitionTo( -                        if (!deviceEntryRepository.isLockscreenEnabled()) { -                            KeyguardState.GONE -                        } else if (canTransitionToGoneOnWake) { -                            KeyguardState.GONE -                        } else if (primaryBouncerShowing) { -                            KeyguardState.PRIMARY_BOUNCER -                        } else if (occluded) { -                            KeyguardState.OCCLUDED -                        } else if (isIdleOnCommunal) { -                            KeyguardState.GLANCEABLE_HUB +                    if (!deviceEntryRepository.isLockscreenEnabled()) { +                        if (SceneContainerFlag.isEnabled) { +                            // TODO(b/336576536): Check if adaptation for scene framework is needed +                        } else { +                            startTransitionTo(KeyguardState.GONE) +                        } +                    } else if (canTransitionToGoneOnWake) { +                        if (SceneContainerFlag.isEnabled) { +                            // TODO(b/336576536): Check if adaptation for scene framework is needed +                        } else { +                            startTransitionTo(KeyguardState.GONE) +                        } +                    } else if (primaryBouncerShowing) { +                        if (SceneContainerFlag.isEnabled) { +                            // TODO(b/336576536): Check if adaptation for scene framework is needed +                        } else { +                            startTransitionTo(KeyguardState.PRIMARY_BOUNCER) +                        } +                    } else if (occluded) { +                        startTransitionTo(KeyguardState.OCCLUDED) +                    } else if (isIdleOnCommunal) { +                        if (SceneContainerFlag.isEnabled) { +                            // TODO(b/336576536): Check if adaptation for scene framework is needed                          } else { -                            KeyguardState.LOCKSCREEN +                            startTransitionTo(KeyguardState.GLANCEABLE_HUB)                          } -                    ) +                    } else { +                        startTransitionTo(KeyguardState.LOCKSCREEN) +                    }                  }          }      } @@ -177,23 +196,55 @@ constructor(                              // Handled by dismissFromDozing().                              !isWakeAndUnlock(biometricUnlockState.mode)                      ) { -                        startTransitionTo( -                            if (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) { -                                KeyguardState.GONE -                            } else if ( -                                KeyguardWmStateRefactor.isEnabled && -                                    !deviceEntryRepository.isLockscreenEnabled() -                            ) { -                                KeyguardState.GONE -                            } else if (primaryBouncerShowing) { -                                KeyguardState.PRIMARY_BOUNCER -                            } else if (isIdleOnCommunal) { -                                KeyguardState.GLANCEABLE_HUB +                        if (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) { +                            if (SceneContainerFlag.isEnabled) { +                                // TODO(b/336576536): Check if adaptation for scene framework is +                                // needed +                            } else { +                                startTransitionTo( +                                    KeyguardState.GONE, +                                    ownerReason = "waking from dozing" +                                ) +                            } +                        } else if ( +                            KeyguardWmStateRefactor.isEnabled && +                                !deviceEntryRepository.isLockscreenEnabled() +                        ) { +                            if (SceneContainerFlag.isEnabled) { +                                // TODO(b/336576536): Check if adaptation for scene framework is +                                // needed +                            } else { +                                startTransitionTo( +                                    KeyguardState.GONE, +                                    ownerReason = "waking from dozing" +                                ) +                            } +                        } else if (primaryBouncerShowing) { +                            if (SceneContainerFlag.isEnabled) { +                                // TODO(b/336576536): Check if adaptation for scene framework is +                                // needed                              } else { -                                KeyguardState.LOCKSCREEN -                            }, -                            ownerReason = "waking from dozing" -                        ) +                                startTransitionTo( +                                    KeyguardState.PRIMARY_BOUNCER, +                                    ownerReason = "waking from dozing" +                                ) +                            } +                        } else if (isIdleOnCommunal) { +                            if (SceneContainerFlag.isEnabled) { +                                // TODO(b/336576536): Check if adaptation for scene framework is +                                // needed +                            } else { +                                startTransitionTo( +                                    KeyguardState.GLANCEABLE_HUB, +                                    ownerReason = "waking from dozing" +                                ) +                            } +                        } else { +                            startTransitionTo( +                                KeyguardState.LOCKSCREEN, +                                ownerReason = "waking from dozing" +                            ) +                        }                      }                  }          } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 97006486b6f1..0e764879d8f6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -91,8 +91,8 @@ constructor(      private fun listenForDreamingToGlanceableHub() {          if (!communalHub()) return -        if (SceneContainerFlag.isEnabled) -            return // TODO(b/336576536): Check if adaptation for scene framework is needed +        // TODO(b/336576536): Check if adaptation for scene framework is needed +        if (SceneContainerFlag.isEnabled) return          scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) {              glanceableHubTransitions.listenForGlanceableHubTransition(                  transitionOwnerName = TAG, @@ -103,6 +103,8 @@ constructor(      }      private fun listenForDreamingToPrimaryBouncer() { +        // TODO(b/336576536): Check if adaptation for scene framework is needed +        if (SceneContainerFlag.isEnabled) return          scope.launch {              keyguardInteractor.primaryBouncerShowing                  .sample(startedKeyguardTransitionStep, ::Pair) @@ -179,8 +181,8 @@ constructor(      }      private fun listenForDreamingToGoneWhenDismissable() { -        if (SceneContainerFlag.isEnabled) -            return // TODO(b/336576536): Check if adaptation for scene framework is needed +        // TODO(b/336576536): Check if adaptation for scene framework is needed +        if (SceneContainerFlag.isEnabled) return          scope.launch {              keyguardInteractor.isAbleToDream                  .sampleCombine( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 22ab82b383a7..859326a29b28 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -23,9 +23,10 @@ import android.app.StatusBarManager  import android.graphics.Point  import android.util.MathUtils  import com.android.app.animation.Interpolators +import com.android.app.tracing.FlowTracing.tracedAwaitClose +import com.android.app.tracing.FlowTracing.tracedConflatedCallbackFlow  import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository  import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging -import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow  import com.android.systemui.common.shared.model.NotificationContainerBounds  import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor  import com.android.systemui.dagger.SysUISingleton @@ -51,13 +52,13 @@ import com.android.systemui.statusbar.CommandQueue  import com.android.systemui.statusbar.notification.NotificationUtils.interpolate  import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor  import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine +import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter  import com.android.systemui.util.kotlin.pairwise  import com.android.systemui.util.kotlin.sample  import javax.inject.Inject  import javax.inject.Provider  import kotlinx.coroutines.CoroutineScope  import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.channels.awaitClose  import kotlinx.coroutines.delay  import kotlinx.coroutines.flow.Flow  import kotlinx.coroutines.flow.MutableStateFlow @@ -197,22 +198,22 @@ constructor(      val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted      /** Event for when the camera gesture is detected */ -    val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow { -        val callback = -            object : CommandQueue.Callbacks { -                override fun onCameraLaunchGestureDetected(source: Int) { -                    trySendWithFailureLogging( -                        cameraLaunchSourceIntToModel(source), -                        TAG, -                        "updated onCameraLaunchGestureDetected" -                    ) +    val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = +        tracedConflatedCallbackFlow("KeyguardInteractor#onCameraLaunchDetected") { +            val callback = +                object : CommandQueue.Callbacks { +                    override fun onCameraLaunchGestureDetected(source: Int) { +                        trySendWithFailureLogging( +                            cameraLaunchSourceIntToModel(source), +                            TAG, +                            "updated onCameraLaunchGestureDetected") +                    }                  } -            } -        commandQueue.addCallback(callback) +            commandQueue.addCallback(callback) -        awaitClose { commandQueue.removeCallback(callback) } -    } +            tracedAwaitClose("onCameraLaunchDetected") { commandQueue.removeCallback(callback) } +        }      /**       * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means @@ -250,17 +251,13 @@ constructor(      /** Keyguard can be clipped at the top as the shade is dragged */      val topClippingBounds: Flow<Int?> by lazy { -        combineTransform( +        repository.topClippingBounds +            .sampleFilter(                  keyguardTransitionInteractor                      .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) -                    .map { it == 1f } -                    .onStart { emit(false) } -                    .distinctUntilChanged(), -                repository.topClippingBounds -            ) { isGone, topClippingBounds -> -                if (!isGone) { -                    emit(topClippingBounds) -                } +                    .onStart { emit(0f) } +            ) { goneValue -> +                goneValue != 1f              }              .distinctUntilChanged()      } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt index 41ccea735f5f..5af38ba49b85 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt @@ -24,6 +24,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardOcclusionRepository  import com.android.systemui.keyguard.shared.model.KeyguardState  import com.android.systemui.power.domain.interactor.PowerInteractor  import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes  import com.android.systemui.util.kotlin.sample  import dagger.Lazy  import javax.inject.Inject @@ -94,10 +95,15 @@ constructor(                  // currently                  // GONE, in which case we're going back to GONE and launching the insecure camera).                  powerInteractor.detailedWakefulness -                    .sample(transitionInteractor.currentKeyguardState, ::Pair) -                    .map { (wakefulness, currentKeyguardState) -> -                        wakefulness.powerButtonLaunchGestureTriggered && -                            currentKeyguardState != KeyguardState.GONE +                    .sample( +                        transitionInteractor.isFinishedIn( +                            Scenes.Gone, +                            stateWithoutSceneContainer = KeyguardState.GONE +                        ), +                        ::Pair +                    ) +                    .map { (wakefulness, isOnGone) -> +                        wakefulness.powerButtonLaunchGestureTriggered && !isOnGone                      },                  // Emit false once that activity goes away.                  isShowWhenLockedActivityOnTop.filter { !it }.map { false } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt index 82255a0c0d54..bcbdc9cca83b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt @@ -19,9 +19,10 @@ package com.android.systemui.keyguard.domain.interactor  import android.content.Context  import com.android.systemui.dagger.SysUISingleton  import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository -import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.isSurfaceVisible +import com.android.systemui.keyguard.shared.model.Edge  import com.android.systemui.keyguard.shared.model.KeyguardState  import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel +import com.android.systemui.scene.shared.model.Scenes  import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor  import com.android.systemui.util.kotlin.sample  import com.android.systemui.util.kotlin.toPx @@ -56,12 +57,18 @@ constructor(       */      val viewParams: Flow<KeyguardSurfaceBehindModel> =          combine( -                transitionInteractor.startedKeyguardTransitionStep, -                transitionInteractor.currentKeyguardState, +                transitionInteractor.isInTransition( +                    Edge.create(to = Scenes.Gone), +                    edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE) +                ), +                transitionInteractor.isFinishedIn( +                    Scenes.Gone, +                    stateWithoutSceneContainer = KeyguardState.GONE +                ),                  notificationLaunchInteractor.isLaunchAnimationRunning, -            ) { startedStep, currentState, notifAnimationRunning -> +            ) { transitioningToGone, isOnGone, notifAnimationRunning ->                  // If we're in transition to GONE, special unlock animation params apply. -                if (startedStep.to == KeyguardState.GONE && currentState != KeyguardState.GONE) { +                if (transitioningToGone) {                      if (notifAnimationRunning) {                          // If the notification launch animation is running, leave the alpha at 0f.                          // The ActivityLaunchAnimator will morph it from the notification at the @@ -87,14 +94,14 @@ constructor(                              animateFromTranslationY =                                  SURFACE_TRANSLATION_Y_DISTANCE_DP.toPx(context).toFloat(),                              translationY = 0f, -                            startVelocity = swipeToDismissInteractor.dismissFling.value?.velocity -                                    ?: 0f, +                            startVelocity = +                                swipeToDismissInteractor.dismissFling.value?.velocity ?: 0f,                          )                      }                  }                  // Default to the visibility of the current state, with no animations. -                KeyguardSurfaceBehindModel(alpha = if (isSurfaceVisible(currentState)) 1f else 0f) +                KeyguardSurfaceBehindModel(alpha = if (isOnGone) 1f else 0f)              }              .distinctUntilChanged() @@ -103,10 +110,14 @@ constructor(       */      private val isNotificationLaunchAnimationRunningOnKeyguard =          notificationLaunchInteractor.isLaunchAnimationRunning -            .sample(transitionInteractor.finishedKeyguardState, ::Pair) -            .map { (animationRunning, finishedState) -> -                animationRunning && finishedState != KeyguardState.GONE -            } +            .sample( +                transitionInteractor.isFinishedIn( +                    Scenes.Gone, +                    stateWithoutSceneContainer = KeyguardState.GONE +                ), +                ::Pair +            ) +            .map { (animationRunning, isOnGone) -> animationRunning && !isOnGone }              .onStart { emit(false) }      /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt index b3c9591f5668..805dbb08d1ac 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt @@ -22,7 +22,10 @@ import com.android.systemui.dagger.SysUISingleton  import com.android.systemui.dagger.qualifiers.Application  import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor  import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN +import com.android.systemui.keyguard.shared.model.KeyguardState.OFF +import com.android.systemui.scene.shared.flag.SceneContainerFlag  import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor  import javax.inject.Inject  import kotlinx.coroutines.CoroutineScope @@ -57,17 +60,7 @@ constructor(      override fun start() {          scope.launch { -            val state = -                if (showLockscreenOnBoot.first()) { -                    KeyguardState.LOCKSCREEN -                } else { -                    KeyguardState.GONE -                } - -            if ( -                internalTransitionInteractor.currentTransitionInfoInternal.value.from != -                    KeyguardState.OFF -            ) { +            if (internalTransitionInteractor.currentTransitionInfoInternal.value.from != OFF) {                  Log.e(                      "KeyguardTransitionInteractor",                      "showLockscreenOnBoot emitted, but we've already " + @@ -75,7 +68,23 @@ constructor(                          "transition, but this should not happen."                  )              } else { -                repository.emitInitialStepsFromOff(state) +                if (SceneContainerFlag.isEnabled) { +                    // TODO(b/336576536): Some part of the transition implemented for flag off is +                    //  missing here. There are two things achieved with this: +                    //  1. Keyguard is hidden when the setup wizard is shown. This part is already +                    //     implemented in scene container by disabling visibility instead of going +                    //     to Gone. See [SceneContainerStartable.hydrateVisibility]. We might want +                    //     to unify this logic here. +                    //  2. When the auth method is set to NONE device boots into Gone (Launcher). +                    //     For this we would just need to call changeScene(Scene.Gone). +                    //     Unfortunately STL doesn't seem to be initialized at this point, therefore +                    //     it needs a different solution. +                    repository.emitInitialStepsFromOff(LOCKSCREEN) +                } else { +                    repository.emitInitialStepsFromOff( +                        if (showLockscreenOnBoot.first()) LOCKSCREEN else GONE +                    ) +                }              }          }      } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index 973e8989b82a..bbc3d7699dee 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -25,6 +25,7 @@ import com.android.systemui.keyguard.shared.model.TransitionInfo  import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled  import com.android.systemui.keyguard.shared.model.TransitionStep  import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlag  import com.android.systemui.util.kotlin.sample  import java.util.UUID  import kotlinx.coroutines.CoroutineDispatcher @@ -81,6 +82,7 @@ sealed class TransitionInteractor(          // a bugreport.          ownerReason: String = "",      ): UUID? { +        toState.checkValidState()          if (fromState != internalTransitionInteractor.currentTransitionInfoInternal.value.to) {              Log.e(                  name, @@ -166,6 +168,8 @@ sealed class TransitionInteractor(       */      @Deprecated("Will be merged into maybeStartTransitionToOccludedOrInsecureCamera")      suspend fun maybeHandleInsecurePowerGesture(): Boolean { +        // TODO(b/336576536): Check if adaptation for scene framework is needed +        if (SceneContainerFlag.isEnabled) return true          if (keyguardOcclusionInteractor.shouldTransitionFromPowerButtonGesture()) {              if (keyguardInteractor.isKeyguardDismissible.value) {                  startTransitionTo( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt index 1c7b4d996b36..76f7749754a2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -38,7 +38,9 @@ import com.android.systemui.lifecycle.repeatWhenAttached  import com.android.systemui.plugins.FalsingManager  import com.android.systemui.res.R  import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.util.kotlin.DisposableHandles  import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DisposableHandle  import kotlinx.coroutines.ExperimentalCoroutinesApi  import kotlinx.coroutines.launch @@ -64,8 +66,9 @@ object DeviceEntryIconViewBinder {          falsingManager: FalsingManager,          vibratorHelper: VibratorHelper,          overrideColor: Color? = null, -    ) { +    ): DisposableHandle {          DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode() +        val disposables = DisposableHandles()          val longPressHandlingView = view.longPressHandlingView          val fgIconView = view.iconView          val bgView = view.bgView @@ -83,118 +86,125 @@ object DeviceEntryIconViewBinder {                  }              } -        view.repeatWhenAttached { -            // Repeat on CREATED so that the view will always observe the entire -            // GONE => AOD transition (even though the view may not be visible until the middle -            // of the transition. -            repeatOnLifecycle(Lifecycle.State.CREATED) { -                launch("$TAG#viewModel.isVisible") { -                    viewModel.isVisible.collect { isVisible -> -                        longPressHandlingView.isInvisible = !isVisible +        disposables += +            view.repeatWhenAttached { +                // Repeat on CREATED so that the view will always observe the entire +                // GONE => AOD transition (even though the view may not be visible until the middle +                // of the transition. +                repeatOnLifecycle(Lifecycle.State.CREATED) { +                    launch("$TAG#viewModel.isVisible") { +                        viewModel.isVisible.collect { isVisible -> +                            longPressHandlingView.isInvisible = !isVisible +                        }                      } -                } -                launch("$TAG#viewModel.isLongPressEnabled") { -                    viewModel.isLongPressEnabled.collect { isEnabled -> -                        longPressHandlingView.setLongPressHandlingEnabled(isEnabled) +                    launch("$TAG#viewModel.isLongPressEnabled") { +                        viewModel.isLongPressEnabled.collect { isEnabled -> +                            longPressHandlingView.setLongPressHandlingEnabled(isEnabled) +                        }                      } -                } -                launch("$TAG#viewModel.isUdfpsSupported") { -                    viewModel.isUdfpsSupported.collect { udfpsSupported -> -                        longPressHandlingView.longPressDuration = -                            if (udfpsSupported) { -                                { -                                    view.resources -                                        .getInteger(R.integer.config_udfpsDeviceEntryIconLongPress) -                                        .toLong() +                    launch("$TAG#viewModel.isUdfpsSupported") { +                        viewModel.isUdfpsSupported.collect { udfpsSupported -> +                            longPressHandlingView.longPressDuration = +                                if (udfpsSupported) { +                                    { +                                        view.resources +                                            .getInteger( +                                                R.integer.config_udfpsDeviceEntryIconLongPress +                                            ) +                                            .toLong() +                                    } +                                } else { +                                    { +                                        view.resources +                                            .getInteger(R.integer.config_lockIconLongPress) +                                            .toLong() +                                    }                                  } -                            } else { -                                { -                                    view.resources -                                        .getInteger(R.integer.config_lockIconLongPress) -                                        .toLong() +                        } +                    } +                    launch("$TAG#viewModel.accessibilityDelegateHint") { +                        viewModel.accessibilityDelegateHint.collect { hint -> +                            view.accessibilityHintType = hint +                            if (hint != DeviceEntryIconView.AccessibilityHintType.NONE) { +                                view.setOnClickListener { +                                    vibratorHelper.performHapticFeedback( +                                        view, +                                        HapticFeedbackConstants.CONFIRM, +                                    ) +                                    applicationScope.launch { viewModel.onUserInteraction() }                                  } +                            } else { +                                view.setOnClickListener(null)                              } +                        }                      } -                } -                launch("$TAG#viewModel.accessibilityDelegateHint") { -                    viewModel.accessibilityDelegateHint.collect { hint -> -                        view.accessibilityHintType = hint -                        if (hint != DeviceEntryIconView.AccessibilityHintType.NONE) { -                            view.setOnClickListener { -                                vibratorHelper.performHapticFeedback( -                                    view, -                                    HapticFeedbackConstants.CONFIRM, -                                ) -                                applicationScope.launch { viewModel.onUserInteraction() } +                    launch("$TAG#viewModel.useBackgroundProtection") { +                        viewModel.useBackgroundProtection.collect { useBackgroundProtection -> +                            if (useBackgroundProtection) { +                                bgView.visibility = View.VISIBLE +                            } else { +                                bgView.visibility = View.GONE                              } -                        } else { -                            view.setOnClickListener(null)                          }                      } -                } -                launch("$TAG#viewModel.useBackgroundProtection") { -                    viewModel.useBackgroundProtection.collect { useBackgroundProtection -> -                        if (useBackgroundProtection) { -                            bgView.visibility = View.VISIBLE -                        } else { -                            bgView.visibility = View.GONE +                    launch("$TAG#viewModel.burnInOffsets") { +                        viewModel.burnInOffsets.collect { burnInOffsets -> +                            view.translationX = burnInOffsets.x.toFloat() +                            view.translationY = burnInOffsets.y.toFloat() +                            view.aodFpDrawable.progress = burnInOffsets.progress                          }                      } -                } -                launch("$TAG#viewModel.burnInOffsets") { -                    viewModel.burnInOffsets.collect { burnInOffsets -> -                        view.translationX = burnInOffsets.x.toFloat() -                        view.translationY = burnInOffsets.y.toFloat() -                        view.aodFpDrawable.progress = burnInOffsets.progress -                    } -                } -                launch("$TAG#viewModel.deviceEntryViewAlpha") { -                    viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha } +                    launch("$TAG#viewModel.deviceEntryViewAlpha") { +                        viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha } +                    }                  }              } -        } -        fgIconView.repeatWhenAttached { -            repeatOnLifecycle(Lifecycle.State.STARTED) { -                // Start with an empty state -                fgIconView.setImageState(StateSet.NOTHING, /* merge */ false) -                launch("$TAG#fpIconView.viewModel") { -                    fgViewModel.viewModel.collect { viewModel -> -                        fgIconView.setImageState( -                            view.getIconState(viewModel.type, viewModel.useAodVariant), -                            /* merge */ false -                        ) -                        if (viewModel.type.contentDescriptionResId != -1) { -                            fgIconView.contentDescription = -                                fgIconView.resources.getString( -                                    viewModel.type.contentDescriptionResId -                                ) +        disposables += +            fgIconView.repeatWhenAttached { +                repeatOnLifecycle(Lifecycle.State.STARTED) { +                    // Start with an empty state +                    fgIconView.setImageState(StateSet.NOTHING, /* merge */ false) +                    launch("$TAG#fpIconView.viewModel") { +                        fgViewModel.viewModel.collect { viewModel -> +                            fgIconView.setImageState( +                                view.getIconState(viewModel.type, viewModel.useAodVariant), +                                /* merge */ false +                            ) +                            if (viewModel.type.contentDescriptionResId != -1) { +                                fgIconView.contentDescription = +                                    fgIconView.resources.getString( +                                        viewModel.type.contentDescriptionResId +                                    ) +                            } +                            fgIconView.imageTintList = +                                ColorStateList.valueOf(overrideColor?.toArgb() ?: viewModel.tint) +                            fgIconView.setPadding( +                                viewModel.padding, +                                viewModel.padding, +                                viewModel.padding, +                                viewModel.padding, +                            )                          } -                        fgIconView.imageTintList = -                            ColorStateList.valueOf(overrideColor?.toArgb() ?: viewModel.tint) -                        fgIconView.setPadding( -                            viewModel.padding, -                            viewModel.padding, -                            viewModel.padding, -                            viewModel.padding, -                        )                      }                  }              } -        } -        bgView.repeatWhenAttached { -            repeatOnLifecycle(Lifecycle.State.CREATED) { -                launch("$TAG#bgViewModel.alpha") { -                    bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha } -                } -                launch("$TAG#bgViewModel.color") { -                    bgViewModel.color.collect { color -> -                        bgView.imageTintList = ColorStateList.valueOf(color) +        disposables += +            bgView.repeatWhenAttached { +                repeatOnLifecycle(Lifecycle.State.CREATED) { +                    launch("$TAG#bgViewModel.alpha") { +                        bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha } +                    } +                    launch("$TAG#bgViewModel.color") { +                        bgViewModel.color.collect { color -> +                            bgView.imageTintList = ColorStateList.valueOf(color) +                        }                      }                  }              } -        } + +        return disposables      }  } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt index e0633807db88..ba9f01862e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt @@ -37,7 +37,9 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel  import com.android.systemui.lifecycle.repeatWhenAttached  import com.android.systemui.plugins.clocks.AodClockBurnInModel  import com.android.systemui.plugins.clocks.ClockController +import com.android.systemui.util.kotlin.DisposableHandles  import com.android.systemui.util.ui.value +import kotlinx.coroutines.DisposableHandle  import kotlinx.coroutines.flow.combine  import kotlinx.coroutines.flow.distinctUntilChanged  import kotlinx.coroutines.flow.map @@ -56,85 +58,94 @@ object KeyguardClockViewBinder {          keyguardClockInteractor: KeyguardClockInteractor,          blueprintInteractor: KeyguardBlueprintInteractor,          rootViewModel: KeyguardRootViewModel, -    ) { -        keyguardRootView.repeatWhenAttached { -            repeatOnLifecycle(Lifecycle.State.CREATED) { -                keyguardClockInteractor.clockEventController.registerListeners(keyguardRootView) -            } -        } - -        keyguardRootView.repeatWhenAttached { -            repeatOnLifecycle(Lifecycle.State.CREATED) { -                launch { -                    if (!MigrateClocksToBlueprint.isEnabled) return@launch -                    viewModel.currentClock.collect { currentClock -> -                        cleanupClockViews(currentClock, keyguardRootView, viewModel.burnInLayer) -                        addClockViews(currentClock, keyguardRootView) -                        updateBurnInLayer(keyguardRootView, viewModel, viewModel.clockSize.value) -                        applyConstraints(clockSection, keyguardRootView, true) -                    } +    ): DisposableHandle { +        val disposables = DisposableHandles() +        disposables += +            keyguardRootView.repeatWhenAttached { +                repeatOnLifecycle(Lifecycle.State.CREATED) { +                    keyguardClockInteractor.clockEventController.registerListeners(keyguardRootView)                  } +            } -                launch { -                    if (!MigrateClocksToBlueprint.isEnabled) return@launch -                    viewModel.clockSize.collect { clockSize -> -                        updateBurnInLayer(keyguardRootView, viewModel, clockSize) -                        blueprintInteractor.refreshBlueprint(Type.ClockSize) +        disposables += +            keyguardRootView.repeatWhenAttached { +                repeatOnLifecycle(Lifecycle.State.CREATED) { +                    launch { +                        if (!MigrateClocksToBlueprint.isEnabled) return@launch +                        viewModel.currentClock.collect { currentClock -> +                            cleanupClockViews(currentClock, keyguardRootView, viewModel.burnInLayer) +                            addClockViews(currentClock, keyguardRootView) +                            updateBurnInLayer( +                                keyguardRootView, +                                viewModel, +                                viewModel.clockSize.value +                            ) +                            applyConstraints(clockSection, keyguardRootView, true) +                        }                      } -                } -                launch { -                    if (!MigrateClocksToBlueprint.isEnabled) return@launch -                    viewModel.clockShouldBeCentered.collect { -                        viewModel.currentClock.value?.let { -                            // TODO(b/301502635): remove "!it.config.useCustomClockScene" when -                            // migrate clocks to blueprint is fully rolled out -                            if ( -                                it.largeClock.config.hasCustomPositionUpdatedAnimation && -                                    !it.config.useCustomClockScene -                            ) { -                                blueprintInteractor.refreshBlueprint(Type.DefaultClockStepping) -                            } else { -                                blueprintInteractor.refreshBlueprint(Type.DefaultTransition) -                            } +                    launch { +                        if (!MigrateClocksToBlueprint.isEnabled) return@launch +                        viewModel.clockSize.collect { clockSize -> +                            updateBurnInLayer(keyguardRootView, viewModel, clockSize) +                            blueprintInteractor.refreshBlueprint(Type.ClockSize)                          }                      } -                } -                launch { -                    if (!MigrateClocksToBlueprint.isEnabled) return@launch -                    combine( -                            viewModel.hasAodIcons, -                            rootViewModel.isNotifIconContainerVisible.map { it.value } -                        ) { hasIcon, isVisible -> -                            hasIcon && isVisible -                        } -                        .distinctUntilChanged() -                        .collect { _ -> +                    launch { +                        if (!MigrateClocksToBlueprint.isEnabled) return@launch +                        viewModel.clockShouldBeCentered.collect {                              viewModel.currentClock.value?.let { -                                if (it.config.useCustomClockScene) { +                                // TODO(b/301502635): remove "!it.config.useCustomClockScene" when +                                // migrate clocks to blueprint is fully rolled out +                                if ( +                                    it.largeClock.config.hasCustomPositionUpdatedAnimation && +                                        !it.config.useCustomClockScene +                                ) { +                                    blueprintInteractor.refreshBlueprint(Type.DefaultClockStepping) +                                } else {                                      blueprintInteractor.refreshBlueprint(Type.DefaultTransition)                                  }                              }                          } -                } +                    } + +                    launch { +                        if (!MigrateClocksToBlueprint.isEnabled) return@launch +                        combine( +                                viewModel.hasAodIcons, +                                rootViewModel.isNotifIconContainerVisible.map { it.value } +                            ) { hasIcon, isVisible -> +                                hasIcon && isVisible +                            } +                            .distinctUntilChanged() +                            .collect { _ -> +                                viewModel.currentClock.value?.let { +                                    if (it.config.useCustomClockScene) { +                                        blueprintInteractor.refreshBlueprint(Type.DefaultTransition) +                                    } +                                } +                            } +                    } -                launch { -                    if (!MigrateClocksToBlueprint.isEnabled) return@launch -                    rootViewModel.burnInModel.collect { burnInModel -> -                        viewModel.currentClock.value?.let { -                            it.largeClock.layout.applyAodBurnIn( -                                AodClockBurnInModel( -                                    translationX = burnInModel.translationX.toFloat(), -                                    translationY = burnInModel.translationY.toFloat(), -                                    scale = burnInModel.scale +                    launch { +                        if (!MigrateClocksToBlueprint.isEnabled) return@launch +                        rootViewModel.burnInModel.collect { burnInModel -> +                            viewModel.currentClock.value?.let { +                                it.largeClock.layout.applyAodBurnIn( +                                    AodClockBurnInModel( +                                        translationX = burnInModel.translationX.toFloat(), +                                        translationY = burnInModel.translationY.toFloat(), +                                        scale = burnInModel.scale +                                    )                                  ) -                            ) +                            }                          }                      }                  }              } -        } + +        return disposables      }      @VisibleForTesting 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 fc92afe17eff..8f149fbe99de 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 @@ -267,6 +267,23 @@ object KeyguardRootViewBinder {                              }                          } +                        launch { +                            blueprintViewModel.currentTransition.collect { currentTransition -> +                                // When blueprint/clock transitions end (null), make sure NSSL is in +                                // the right place +                                if (currentTransition == null) { +                                    childViews[nsslPlaceholderId]?.let { notificationListPlaceholder +                                        -> +                                        viewModel.onNotificationContainerBoundsChanged( +                                            notificationListPlaceholder.top.toFloat(), +                                            notificationListPlaceholder.bottom.toFloat(), +                                            animate = true, +                                        ) +                                    } +                                } +                            } +                        } +                          if (NotificationIconContainerRefactor.isEnabled) {                              launch {                                  val iconsAppearTranslationPx = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt index 191056c5578a..8b74f5dc791d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt @@ -31,6 +31,7 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel  import com.android.systemui.lifecycle.repeatWhenAttached  import com.android.systemui.res.R  import com.android.systemui.shared.R as sharedR +import kotlinx.coroutines.DisposableHandle  object KeyguardSmartspaceViewBinder {      @JvmStatic @@ -39,8 +40,8 @@ object KeyguardSmartspaceViewBinder {          clockViewModel: KeyguardClockViewModel,          smartspaceViewModel: KeyguardSmartspaceViewModel,          blueprintInteractor: KeyguardBlueprintInteractor, -    ) { -        keyguardRootView.repeatWhenAttached { +    ): DisposableHandle { +        return keyguardRootView.repeatWhenAttached {              repeatOnLifecycle(Lifecycle.State.CREATED) {                  launch("$TAG#clockViewModel.hasCustomWeatherDataDisplay") {                      if (!MigrateClocksToBlueprint.isEnabled) return@launch diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt index 0637bba9f1de..91e48b56b4f5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt @@ -48,6 +48,7 @@ import com.android.systemui.shared.R as sharedR  import com.android.systemui.util.ui.value  import dagger.Lazy  import javax.inject.Inject +import kotlinx.coroutines.DisposableHandle  internal fun ConstraintSet.setVisibility(      views: Iterable<View>, @@ -70,21 +71,24 @@ constructor(      val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>,      private val rootViewModel: KeyguardRootViewModel,  ) : KeyguardSection() { +    private var disposableHandle: DisposableHandle? = null +      override fun addViews(constraintLayout: ConstraintLayout) {}      override fun bindData(constraintLayout: ConstraintLayout) {          if (!MigrateClocksToBlueprint.isEnabled) {              return          } - -        KeyguardClockViewBinder.bind( -            this, -            constraintLayout, -            keyguardClockViewModel, -            clockInteractor, -            blueprintInteractor.get(), -            rootViewModel, -        ) +        disposableHandle?.dispose() +        disposableHandle = +            KeyguardClockViewBinder.bind( +                this, +                constraintLayout, +                keyguardClockViewModel, +                clockInteractor, +                blueprintInteractor.get(), +                rootViewModel, +            )      }      override fun applyConstraints(constraintSet: ConstraintSet) { @@ -97,7 +101,13 @@ constructor(          }      } -    override fun removeViews(constraintLayout: ConstraintLayout) {} +    override fun removeViews(constraintLayout: ConstraintLayout) { +        if (!MigrateClocksToBlueprint.isEnabled) { +            return +        } + +        disposableHandle?.dispose() +    }      private fun buildConstraints(          clock: ClockController, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt index e01f0a152b37..51230dd0a47c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt @@ -49,6 +49,7 @@ import com.android.systemui.statusbar.VibratorHelper  import dagger.Lazy  import javax.inject.Inject  import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DisposableHandle  import kotlinx.coroutines.ExperimentalCoroutinesApi  /** Includes the device entry icon. */ @@ -70,6 +71,7 @@ constructor(      private val vibratorHelper: Lazy<VibratorHelper>,  ) : KeyguardSection() {      private val deviceEntryIconViewId = R.id.device_entry_icon_view +    private var disposableHandle: DisposableHandle? = null      override fun addViews(constraintLayout: ConstraintLayout) {          if ( @@ -97,15 +99,17 @@ constructor(      override fun bindData(constraintLayout: ConstraintLayout) {          if (DeviceEntryUdfpsRefactor.isEnabled) {              constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let { -                DeviceEntryIconViewBinder.bind( -                    applicationScope, -                    it, -                    deviceEntryIconViewModel.get(), -                    deviceEntryForegroundViewModel.get(), -                    deviceEntryBackgroundViewModel.get(), -                    falsingManager.get(), -                    vibratorHelper.get(), -                ) +                disposableHandle?.dispose() +                disposableHandle = +                    DeviceEntryIconViewBinder.bind( +                        applicationScope, +                        it, +                        deviceEntryIconViewModel.get(), +                        deviceEntryForegroundViewModel.get(), +                        deviceEntryBackgroundViewModel.get(), +                        falsingManager.get(), +                        vibratorHelper.get(), +                    )              }          } else {              constraintLayout.findViewById<LockIconView?>(R.id.lock_icon_view)?.let { @@ -178,6 +182,7 @@ constructor(      override fun removeViews(constraintLayout: ConstraintLayout) {          if (DeviceEntryUdfpsRefactor.isEnabled) {              constraintLayout.removeView(deviceEntryIconViewId) +            disposableHandle?.dispose()          } else {              constraintLayout.removeView(R.id.lock_icon_view)          } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt index 8a751f05e102..55fc71823685 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt @@ -37,6 +37,7 @@ import com.android.systemui.shared.R as sharedR  import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController  import dagger.Lazy  import javax.inject.Inject +import kotlinx.coroutines.DisposableHandle  @SysUISingleton  open class SmartspaceSection @@ -56,6 +57,7 @@ constructor(      private var smartspaceVisibilityListener: OnGlobalLayoutListener? = null      private var pastVisibility: Int = -1 +    private var disposableHandle: DisposableHandle? = null      override fun onRebuildBegin() {          smartspaceController.suppressDisconnects = true @@ -96,12 +98,14 @@ constructor(      override fun bindData(constraintLayout: ConstraintLayout) {          if (!MigrateClocksToBlueprint.isEnabled) return          if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return -        KeyguardSmartspaceViewBinder.bind( -            constraintLayout, -            keyguardClockViewModel, -            keyguardSmartspaceViewModel, -            blueprintInteractor.get(), -        ) +        disposableHandle?.dispose() +        disposableHandle = +            KeyguardSmartspaceViewBinder.bind( +                constraintLayout, +                keyguardClockViewModel, +                keyguardSmartspaceViewModel, +                blueprintInteractor.get(), +            )      }      override fun applyConstraints(constraintSet: ConstraintSet) { @@ -188,6 +192,8 @@ constructor(          }          smartspaceView?.viewTreeObserver?.removeOnGlobalLayoutListener(smartspaceVisibilityListener)          smartspaceVisibilityListener = null + +        disposableHandle?.dispose()      }      private fun updateVisibility(constraintSet: ConstraintSet) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt index c4383fc0857d..680f966708be 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt @@ -18,6 +18,7 @@  package com.android.systemui.keyguard.ui.viewmodel  import androidx.annotation.VisibleForTesting +import com.android.app.tracing.FlowTracing.traceEmissionCount  import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor  import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor  import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -229,7 +230,7 @@ constructor(                      )                  }                  .distinctUntilChanged() -        } +        }.traceEmissionCount({"QuickAfforcances#button${position.toSlotId()}"})      }      private fun KeyguardQuickAffordanceModel.toViewModel( diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java index 988fe640de2d..18a04ec60d56 100644 --- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java @@ -158,7 +158,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener                  }                  if (mp != null) {                      if (DEBUG) { -                        Log.d(mTag, "mPlayer.pause+release piid:" + player.getPlayerIId()); +                        Log.d(mTag, "mp.pause+release piid:" + mp.getPlayerIId());                      }                      mp.pause();                      try { diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt index de490a58a498..311cbfb7e632 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt @@ -96,12 +96,27 @@ constructor(                  logDebug { "lockScreenState:isConfigSelected=$isConfigSelected" }                  logDebug { "lockScreenState:isDefaultNotesAppSet=$isDefaultNotesAppSet" } +                val isCustomLockScreenShortcutEnabled = +                    context.resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled) +                val isShortcutSelectedOrDefaultEnabled = +                    if (isCustomLockScreenShortcutEnabled) { +                        isConfigSelected +                    } else { +                        isStylusEverUsed +                    } +                logDebug { +                    "lockScreenState:isCustomLockScreenShortcutEnabled=" + +                        isCustomLockScreenShortcutEnabled +                } +                logDebug { +                    "lockScreenState:isShortcutSelectedOrDefaultEnabled=" + +                        isShortcutSelectedOrDefaultEnabled +                }                  if (                      isEnabled &&                          isUserUnlocked &&                          isDefaultNotesAppSet && -                        isConfigSelected && -                        isStylusEverUsed +                        isShortcutSelectedOrDefaultEnabled                  ) {                      val contentDescription = ContentDescription.Resource(pickerNameResourceId)                      val icon = Icon.Resource(pickerIconResourceId, contentDescription) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt index f62b24aa96a3..3dcaff3de35a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt @@ -21,6 +21,7 @@ import android.provider.DeviceConfig  import com.android.internal.annotations.VisibleForTesting  import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING  import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag  import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype  import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection  import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING @@ -51,7 +52,8 @@ constructor(val proxy: DeviceConfigProxy, val context: Context) {      }      fun getNotificationBuckets(): IntArray { -        if (PriorityPeopleSection.isEnabled || NotificationMinimalismPrototype.V2.isEnabled) { +        if (PriorityPeopleSection.isEnabled || NotificationMinimalismPrototype.V2.isEnabled +            || NotificationClassificationFlag.isEnabled) {              // We don't need this list to be adaptive, it can be the superset of all features.              return PriorityBucket.getAllInOrder()          } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationClassificationFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationClassificationFlag.kt new file mode 100644 index 000000000000..139347c86d54 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationClassificationFlag.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.collection + +import android.service.notification.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** + * Helper for android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION + */ +@Suppress("NOTHING_TO_INLINE") +object NotificationClassificationFlag { +    const val FLAG_NAME = Flags.FLAG_NOTIFICATION_CLASSIFICATION + +    /** A token used for dependency declaration */ +    val token: FlagToken +        get() = FlagToken(FLAG_NAME, isEnabled) + +    /** Are sections sorted by time? */ +    @JvmStatic +    inline val isEnabled +        get() = Flags.notificationClassification() + +    /** +     * Called to ensure code is only run when the flag is enabled. This protects users from the +     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng +     * build to ensure that the refactor author catches issues in testing. +     */ +    @JvmStatic +    inline fun isUnexpectedlyInLegacyMode() = +            RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + +    /** +     * Called to ensure code is only run when the flag is disabled. This will throw an exception if +     * the flag is enabled to ensure that the refactor author catches issues in testing. +     */ +    @JvmStatic +    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt new file mode 100644 index 000000000000..244c5946c21e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt @@ -0,0 +1,95 @@ +/* + * 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.statusbar.notification.collection.coordinator + +import android.app.NotificationChannel.NEWS_ID +import android.app.NotificationChannel.PROMOTIONS_ID +import android.app.NotificationChannel.RECS_ID +import android.app.NotificationChannel.SOCIAL_MEDIA_ID +import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner +import com.android.systemui.statusbar.notification.collection.render.NodeController +import com.android.systemui.statusbar.notification.dagger.NewsHeader +import com.android.systemui.statusbar.notification.dagger.PromoHeader +import com.android.systemui.statusbar.notification.dagger.RecsHeader +import com.android.systemui.statusbar.notification.dagger.SocialHeader +import com.android.systemui.statusbar.notification.stack.BUCKET_NEWS +import com.android.systemui.statusbar.notification.stack.BUCKET_PROMO +import com.android.systemui.statusbar.notification.stack.BUCKET_RECS +import com.android.systemui.statusbar.notification.stack.BUCKET_SOCIAL +import javax.inject.Inject + +/** + * Coordinator for sections derived from NotificationAssistantService classification. + */ +@CoordinatorScope +class BundleCoordinator @Inject constructor( +    @NewsHeader private val newsHeaderController: NodeController, +    @SocialHeader private val socialHeaderController: NodeController, +    @RecsHeader private val recsHeaderController: NodeController, +    @PromoHeader private val promoHeaderController: NodeController, +) : Coordinator { + +    val newsSectioner = +            object : NotifSectioner("News", BUCKET_NEWS) { +                override fun isInSection(entry: ListEntry): Boolean { +                    return entry.representativeEntry?.channel?.id == NEWS_ID +                } + +                override fun getHeaderNodeController(): NodeController? { +                    return newsHeaderController +                } +            } + +    val socialSectioner = +        object : NotifSectioner("Social", BUCKET_SOCIAL) { +            override fun isInSection(entry: ListEntry): Boolean { +                return entry.representativeEntry?.channel?.id == SOCIAL_MEDIA_ID +            } + +            override fun getHeaderNodeController(): NodeController? { +                return socialHeaderController +            } +        } + +    val recsSectioner = +        object : NotifSectioner("Recommendations", BUCKET_RECS) { +            override fun isInSection(entry: ListEntry): Boolean { +                return entry.representativeEntry?.channel?.id == RECS_ID +            } + +            override fun getHeaderNodeController(): NodeController? { +                return recsHeaderController +            } +        } + +    val promoSectioner = +        object : NotifSectioner("Promotions", BUCKET_PROMO) { +            override fun isInSection(entry: ListEntry): Boolean { +                return entry.representativeEntry?.channel?.id == PROMOTIONS_ID +            } + +            override fun getHeaderNodeController(): NodeController? { +                return promoHeaderController +            } +        } + +    override fun attach(pipeline: NotifPipeline) { +    } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt index e41352254bac..e0389820aedf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt @@ -17,10 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator  import com.android.systemui.flags.FeatureFlags  import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED -import com.android.systemui.statusbar.notification.collection.NotifPipeline -import com.android.systemui.statusbar.notification.collection.PipelineDumpable -import com.android.systemui.statusbar.notification.collection.PipelineDumper -import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag +import com.android.systemui.statusbar.notification.collection.*  import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope  import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner  import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider @@ -69,6 +66,7 @@ constructor(      dismissibilityCoordinator: DismissibilityCoordinator,      dreamCoordinator: DreamCoordinator,      statsLoggerCoordinator: NotificationStatsLoggerCoordinator, +    bundleCoordinator: BundleCoordinator,  ) : NotifCoordinators {      private val mCoreCoordinators: MutableList<CoreCoordinator> = ArrayList() @@ -132,6 +130,12 @@ constructor(              mOrderedSections.add(conversationCoordinator.peopleSilentSectioner) // People Silent          }          mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting +        if (NotificationClassificationFlag.isEnabled) { +            mOrderedSections.add(bundleCoordinator.newsSectioner); +            mOrderedSections.add(bundleCoordinator.socialSectioner); +            mOrderedSections.add(bundleCoordinator.recsSectioner); +            mOrderedSections.add(bundleCoordinator.promoSectioner); +        }          mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent          mOrderedSections.add(rankingCoordinator.minimizedSectioner) // Minimized diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt index ca43591e0776..e661090011c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt @@ -80,6 +80,50 @@ object NotificationSectionHeadersModule {              .build()      @Provides +    @NewsHeader +    @SysUISingleton +    @JvmStatic fun providesNewsHeaderSubcomponent( +        builder: Provider<SectionHeaderControllerSubcomponent.Builder> +    ) = builder.get() +        .nodeLabel("news header") +        .headerText(com.android.internal.R.string.news_notification_channel_label) +        .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS) +        .build() + +    @Provides +    @SocialHeader +    @SysUISingleton +    @JvmStatic fun providesSocialHeaderSubcomponent( +        builder: Provider<SectionHeaderControllerSubcomponent.Builder> +    ) = builder.get() +        .nodeLabel("social header") +        .headerText(com.android.internal.R.string.social_notification_channel_label) +        .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS) +        .build() + +    @Provides +    @RecsHeader +    @SysUISingleton +    @JvmStatic fun providesRecsHeaderSubcomponent( +        builder: Provider<SectionHeaderControllerSubcomponent.Builder> +    ) = builder.get() +        .nodeLabel("recs header") +        .headerText(com.android.internal.R.string.recs_notification_channel_label) +        .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS) +        .build() + +    @Provides +    @PromoHeader +    @SysUISingleton +    @JvmStatic fun providesPromoHeaderSubcomponent( +        builder: Provider<SectionHeaderControllerSubcomponent.Builder> +    ) = builder.get() +        .nodeLabel("promo header") +        .headerText(com.android.internal.R.string.promotional_notification_channel_label) +        .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS) +        .build() + +    @Provides      @SilentHeader      @JvmStatic fun providesSilentHeaderNodeController(          @SilentHeader subcomponent: SectionHeaderControllerSubcomponent @@ -126,6 +170,54 @@ object NotificationSectionHeadersModule {      @JvmStatic fun providesIncomingHeaderController(          @IncomingHeader subcomponent: SectionHeaderControllerSubcomponent      ) = subcomponent.headerController + +    @Provides +    @NewsHeader +    @JvmStatic fun providesNewsHeaderNodeController( +        @NewsHeader subcomponent: SectionHeaderControllerSubcomponent +    ) = subcomponent.nodeController + +    @Provides +    @NewsHeader +    @JvmStatic fun providesNewsHeaderController( +        @NewsHeader subcomponent: SectionHeaderControllerSubcomponent +    ) = subcomponent.headerController + +    @Provides +    @SocialHeader +    @JvmStatic fun providesSocialHeaderNodeController( +        @SocialHeader subcomponent: SectionHeaderControllerSubcomponent +    ) = subcomponent.nodeController + +    @Provides +    @SocialHeader +    @JvmStatic fun providesSocialHeaderController( +        @SocialHeader subcomponent: SectionHeaderControllerSubcomponent +    ) = subcomponent.headerController + +    @Provides +    @RecsHeader +    @JvmStatic fun providesRecsHeaderNodeController( +        @RecsHeader subcomponent: SectionHeaderControllerSubcomponent +    ) = subcomponent.nodeController + +    @Provides +    @RecsHeader +    @JvmStatic fun providesRecsHeaderController( +        @RecsHeader subcomponent: SectionHeaderControllerSubcomponent +    ) = subcomponent.headerController + +    @Provides +    @PromoHeader +    @JvmStatic fun providesPromoHeaderNodeController( +        @PromoHeader subcomponent: SectionHeaderControllerSubcomponent +    ) = subcomponent.nodeController + +    @Provides +    @PromoHeader +    @JvmStatic fun providesPromoHeaderController( +        @PromoHeader subcomponent: SectionHeaderControllerSubcomponent +    ) = subcomponent.headerController  }  @Subcomponent(modules = [ SectionHeaderBindingModule::class ]) @@ -183,3 +275,19 @@ annotation class HeaderClickAction  @Scope  @Retention(AnnotationRetention.BINARY)  annotation class SectionHeaderScope + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class NewsHeader + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class SocialHeader + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class RecsHeader + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class PromoHeader
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java index 9e0dd8fc4d92..175512336b8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java @@ -20,9 +20,13 @@ import static com.android.systemui.statusbar.notification.stack.NotificationPrio  import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_FOREGROUND_SERVICE;  import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_HEADS_UP;  import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_MEDIA_CONTROLS; +import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_NEWS;  import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PEOPLE;  import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PRIORITY_PEOPLE; +import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PROMO; +import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_RECS;  import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT; +import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SOCIAL;  import android.annotation.Nullable;  import android.service.notification.StatusBarNotification; @@ -135,6 +139,10 @@ public interface NotificationPanelLogger {                  return Notifications.Notification.SECTION_PEOPLE;              case BUCKET_ALERTING: return Notifications.Notification.SECTION_ALERTING;              case BUCKET_SILENT: return Notifications.Notification.SECTION_SILENT; +            case BUCKET_NEWS: return Notifications.Notification.SECTION_NEWS; +            case BUCKET_SOCIAL: return Notifications.Notification.SECTION_SOCIAL; +            case BUCKET_RECS: return Notifications.Notification.SECTION_RECS; +            case BUCKET_PROMO: return Notifications.Notification.SECTION_PROMO;          }          return Notifications.Notification.SECTION_UNKNOWN;      } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto index c2ab2758dd74..ce4356ae6c2b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto @@ -43,6 +43,13 @@ message Notification {          SECTION_ALERTING = 4;          SECTION_SILENT = 5;          SECTION_FOREGROUND_SERVICE = 6; +        SECTION_PRIORITY_PEOPLE = 7; +        SECTION_TOP_ONGOING = 8; +        SECTION_TOP_UNSEEN = 9; +        SECTION_NEWS = 10; +        SECTION_SOCIAL = 11; +        SECTION_RECS = 12; +        SECTION_PROMO = 13;      }      optional NotificationSection section = 6;  } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt index fabb696d9182..f4a452784267 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt @@ -20,6 +20,10 @@ import android.annotation.IntDef              BUCKET_PRIORITY_PEOPLE,              BUCKET_PEOPLE,              BUCKET_ALERTING, +            BUCKET_NEWS, +            BUCKET_SOCIAL, +            BUCKET_RECS, +            BUCKET_PROMO,              BUCKET_SILENT          ]  ) @@ -35,6 +39,10 @@ annotation class PriorityBucket {                  BUCKET_PRIORITY_PEOPLE,                  BUCKET_PEOPLE,                  BUCKET_ALERTING, +                BUCKET_NEWS, +                BUCKET_SOCIAL, +                BUCKET_RECS, +                BUCKET_PROMO,                  BUCKET_SILENT,              )      } @@ -49,4 +57,9 @@ const val BUCKET_FOREGROUND_SERVICE = 3  const val BUCKET_PRIORITY_PEOPLE = 7  const val BUCKET_PEOPLE = 4  const val BUCKET_ALERTING = 5 +const val BUCKET_NEWS = 10 +const val BUCKET_SOCIAL = 11 +const val BUCKET_RECS = 12 +const val BUCKET_PROMO = 13  const val BUCKET_SILENT = 6 + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index 3400ad107133..7441c7058d7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -22,12 +22,10 @@ import com.android.internal.annotations.VisibleForTesting  import com.android.systemui.media.controls.ui.controller.KeyguardMediaController  import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager  import com.android.systemui.statusbar.notification.SourceType +import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag  import com.android.systemui.statusbar.notification.collection.render.MediaContainerController  import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController -import com.android.systemui.statusbar.notification.dagger.AlertingHeader -import com.android.systemui.statusbar.notification.dagger.IncomingHeader -import com.android.systemui.statusbar.notification.dagger.PeopleHeader -import com.android.systemui.statusbar.notification.dagger.SilentHeader +import com.android.systemui.statusbar.notification.dagger.*  import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow  import com.android.systemui.statusbar.notification.row.ExpandableView  import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider @@ -51,7 +49,11 @@ internal constructor(      @IncomingHeader private val incomingHeaderController: SectionHeaderController,      @PeopleHeader private val peopleHeaderController: SectionHeaderController,      @AlertingHeader private val alertingHeaderController: SectionHeaderController, -    @SilentHeader private val silentHeaderController: SectionHeaderController +    @SilentHeader private val silentHeaderController: SectionHeaderController, +    @NewsHeader private val newsHeaderController: SectionHeaderController, +    @SocialHeader private val socialHeaderController: SectionHeaderController, +    @RecsHeader private val recsHeaderController: SectionHeaderController, +    @PromoHeader private val promoHeaderController: SectionHeaderController  ) : SectionProvider {      private val configurationListener = @@ -84,6 +86,22 @@ internal constructor(      val mediaControlsView: MediaContainerView?          get() = mediaContainerController.mediaContainerView +    @VisibleForTesting +    val newsHeaderView: SectionHeaderView? +        get() = newsHeaderController.headerView + +    @VisibleForTesting +    val socialHeaderView: SectionHeaderView? +        get() = socialHeaderController.headerView + +    @VisibleForTesting +    val recsHeaderView: SectionHeaderView? +        get() = recsHeaderController.headerView + +    @VisibleForTesting +    val promoHeaderView: SectionHeaderView? +        get() = promoHeaderController.headerView +      /** Must be called before use. */      fun initialize(parent: NotificationStackScrollLayout) {          check(!initialized) { "NotificationSectionsManager already initialized" } @@ -107,15 +125,24 @@ internal constructor(          incomingHeaderController.reinflateView(parent)          mediaContainerController.reinflateView(parent)          keyguardMediaController.attachSinglePaneContainer(mediaControlsView) +        if (NotificationClassificationFlag.isEnabled) { +            newsHeaderController.reinflateView(parent) +            socialHeaderController.reinflateView(parent) +            recsHeaderController.reinflateView(parent) +            promoHeaderController.reinflateView(parent) +        }      }      override fun beginsSection(view: View, previous: View?): Boolean =          view === silentHeaderView || -            view === mediaControlsView || -            view === peopleHeaderView || -            view === alertingHeaderView || -            view === incomingHeaderView || -            getBucket(view) != getBucket(previous) +                view === mediaControlsView || +                view === peopleHeaderView || +                view === alertingHeaderView || +                view === incomingHeaderView || +                (NotificationClassificationFlag.isEnabled && (view === newsHeaderView +                        || view === socialHeaderView || view === recsHeaderView +                        || view === promoHeaderView)) || +                getBucket(view) != getBucket(previous)      private fun getBucket(view: View?): Int? =          when { @@ -124,6 +151,10 @@ internal constructor(              view === mediaControlsView -> BUCKET_MEDIA_CONTROLS              view === peopleHeaderView -> BUCKET_PEOPLE              view === alertingHeaderView -> BUCKET_ALERTING +            view === newsHeaderView -> BUCKET_NEWS +            view === socialHeaderView -> BUCKET_SOCIAL +            view === recsHeaderView -> BUCKET_RECS +            view === promoHeaderView -> BUCKET_PROMO              view is ExpandableNotificationRow -> view.entry.bucket              else -> null          } @@ -255,6 +286,12 @@ internal constructor(          peopleHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)          silentHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)          alertingHeaderView?.setForegroundColors(onSurface, onSurfaceVariant) +        if (NotificationClassificationFlag.isEnabled) { +            newsHeaderView?.setForegroundColors(onSurface, onSurfaceVariant) +            socialHeaderView?.setForegroundColors(onSurface, onSurfaceVariant) +            recsHeaderView?.setForegroundColors(onSurface, onSurfaceVariant) +            promoHeaderView?.setForegroundColors(onSurface, onSurfaceVariant) +        }      }      companion object { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 4ce901030e57..0623bb2c274a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -107,7 +107,6 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements      private int mStatusBarState;      private AnimationStateHandler mAnimationStateHandler; -    private Handler mBgHandler;      private int mHeadsUpInset;      // Used for determining the region for touch interaction @@ -152,8 +151,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements              UiEventLogger uiEventLogger,              JavaAdapter javaAdapter,              ShadeInteractor shadeInteractor, -            AvalancheController avalancheController, -            @Background Handler bgHandler) { +            AvalancheController avalancheController) {          super(context, logger, handler, globalSettings, systemClock, executor,                  accessibilityManagerWrapper, uiEventLogger, avalancheController);          Resources resources = mContext.getResources(); @@ -163,7 +161,6 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements          mGroupMembershipManager = groupMembershipManager;          mVisualStabilityProvider = visualStabilityProvider;          mAvalancheController = avalancheController; -        mBgHandler = bgHandler;          updateResources();          configurationController.addCallback(new ConfigurationController.ConfigurationListener() {              @Override @@ -405,11 +402,8 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements              // Waiting HUNs in AvalancheController are still promoted to the HUN section and thus              // seen in open shade; clear them so we don't show them again when the shade closes and              // reordering is allowed again. -            int waitingKeysSize = mAvalancheController.getWaitingKeys().size(); -            mBgHandler.post(() -> { -                // Do this in the background to avoid missing frames when closing the shade -                mAvalancheController.logDroppedHuns(waitingKeysSize); -            }); +            final int numDropped = mAvalancheController.getWaitingKeys().size(); +            mAvalancheController.logDroppedHunsInBackground(numDropped);              mAvalancheController.clearNext();              // In open shade the first HUN is pinned, and visual stability logic prevents us from diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt index dbe54f346072..8aabdf2a40f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt @@ -15,12 +15,14 @@   */  package com.android.systemui.statusbar.policy +import android.os.Handler  import android.util.Log  import androidx.annotation.VisibleForTesting  import com.android.internal.logging.UiEvent  import com.android.internal.logging.UiEventLogger  import com.android.systemui.Dumpable  import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background  import com.android.systemui.dump.DumpManager  import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun  import com.android.systemui.statusbar.policy.BaseHeadsUpManager.HeadsUpEntry @@ -35,7 +37,10 @@ import javax.inject.Inject  @SysUISingleton  class AvalancheController  @Inject -constructor(dumpManager: DumpManager, private val uiEventLogger: UiEventLogger) : Dumpable { +constructor(dumpManager: DumpManager, +            private val uiEventLogger: UiEventLogger, +            @Background private val bgHandler: Handler +) : Dumpable {      private val tag = "AvalancheController"      private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG) @@ -315,7 +320,7 @@ constructor(dumpManager: DumpManager, private val uiEventLogger: UiEventLogger)          // Remove runnable labels for dropped huns          val listToDrop = nextList.subList(1, nextList.size) -        logDroppedHuns(listToDrop.size) +        logDroppedHunsInBackground(listToDrop.size)          if (debug) {              // Clear runnable labels @@ -332,10 +337,13 @@ constructor(dumpManager: DumpManager, private val uiEventLogger: UiEventLogger)          showNow(headsUpEntryShowing!!, headsUpEntryShowingRunnableList)      } -    fun logDroppedHuns(numDropped: Int) { -        for (n in 1..numDropped) { -            uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_DROPPED) -        } +    fun logDroppedHunsInBackground(numDropped: Int) { +        bgHandler.post(Runnable { +            // Do this in the background to avoid missing frames when closing the shade +            for (n in 1..numDropped) { +                uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_DROPPED) +            } +        })      }      fun clearNext() { diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/SettingsSingleThreadBackground.java b/packages/SystemUI/src/com/android/systemui/util/kotlin/SettingsSingleThreadBackground.java new file mode 100644 index 000000000000..e13981dfe5c7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SettingsSingleThreadBackground.java @@ -0,0 +1,34 @@ +/* + * 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.util.kotlin; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +/** + * This is a background executor/dispatcher which guarantees that **within that instance**, + * operations will execute in the same order as they were dispatched. + * This is useful for background operations like register/unregister where ordering is important. + */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface SettingsSingleThreadBackground { } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt index b836016eb2ef..a03221e03467 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt @@ -16,6 +16,7 @@  package com.android.systemui.util.kotlin +import android.os.Handler  import com.android.systemui.dagger.SysUISingleton  import com.android.systemui.dagger.qualifiers.Application  import com.android.systemui.dagger.qualifiers.Background @@ -23,15 +24,16 @@ import com.android.systemui.dagger.qualifiers.Tracing  import com.android.systemui.dagger.qualifiers.UiBackground  import dagger.Module  import dagger.Provides -import java.util.concurrent.Executor -import kotlin.coroutines.CoroutineContext  import kotlinx.coroutines.CoroutineDispatcher  import kotlinx.coroutines.CoroutineScope  import kotlinx.coroutines.DelicateCoroutinesApi  import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.android.asCoroutineDispatcher  import kotlinx.coroutines.asCoroutineDispatcher  import kotlinx.coroutines.newFixedThreadPoolContext  import kotlinx.coroutines.plus +import java.util.concurrent.Executor +import kotlin.coroutines.CoroutineContext  private const val LIMIT_BACKGROUND_DISPATCHER_THREADS = true @@ -78,6 +80,14 @@ class SysUICoroutinesModule {      }      @Provides +    @SysUISingleton +    @SettingsSingleThreadBackground +    fun settingsBgDispatcher(@Background bgHandler: Handler): CoroutineDispatcher { +        // Handlers are guaranteed to be sequential so we use that one for now. +        return bgHandler.asCoroutineDispatcher("SettingsBg") +    } + +    @Provides      @Background      @SysUISingleton      fun bgCoroutineContext( diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java index 4438763aa765..816f55db65a0 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java @@ -23,7 +23,7 @@ import android.content.ContentResolver;  import android.net.Uri;  import android.provider.Settings; -import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.util.kotlin.SettingsSingleThreadBackground;  import kotlinx.coroutines.CoroutineDispatcher; @@ -37,7 +37,7 @@ class GlobalSettingsImpl implements GlobalSettings {      @Inject      GlobalSettingsImpl(ContentResolver contentResolver, -            @Background CoroutineDispatcher bgDispatcher) { +            @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {          mContentResolver = contentResolver;          mBgDispatcher = bgDispatcher;      } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java index 38ad5d0d0cab..9c98f43e2501 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java @@ -22,13 +22,13 @@ import android.provider.Settings;  import androidx.annotation.NonNull; -import com.android.systemui.dagger.qualifiers.Background;  import com.android.systemui.settings.UserTracker; - -import kotlinx.coroutines.CoroutineDispatcher; +import com.android.systemui.util.kotlin.SettingsSingleThreadBackground;  import javax.inject.Inject; +import kotlinx.coroutines.CoroutineDispatcher; +  class SecureSettingsImpl implements SecureSettings {      private final ContentResolver mContentResolver;      private final UserTracker mUserTracker; @@ -36,7 +36,7 @@ class SecureSettingsImpl implements SecureSettings {      @Inject      SecureSettingsImpl(ContentResolver contentResolver, UserTracker userTracker, -            @Background CoroutineDispatcher bgDispatcher) { +            @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {          mContentResolver = contentResolver;          mUserTracker = userTracker;          mBgDispatcher = bgDispatcher; diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java index 68cc753bc48a..406d95bd8fd1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java @@ -22,13 +22,13 @@ import android.provider.Settings;  import androidx.annotation.NonNull; -import com.android.systemui.dagger.qualifiers.Background;  import com.android.systemui.settings.UserTracker; - -import kotlinx.coroutines.CoroutineDispatcher; +import com.android.systemui.util.kotlin.SettingsSingleThreadBackground;  import javax.inject.Inject; +import kotlinx.coroutines.CoroutineDispatcher; +  class SystemSettingsImpl implements SystemSettings {      private final ContentResolver mContentResolver;      private final UserTracker mUserTracker; @@ -36,7 +36,7 @@ class SystemSettingsImpl implements SystemSettings {      @Inject      SystemSettingsImpl(ContentResolver contentResolver, UserTracker userTracker, -            @Background CoroutineDispatcher bgDispatcher) { +            @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {          mContentResolver = contentResolver;          mUserTracker = userTracker;          mBgCoroutineDispatcher = bgDispatcher; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 9a99ed7bb512..1e3ee280204b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -27,6 +27,7 @@ import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton  import android.hardware.biometrics.PromptInfo  import android.hardware.biometrics.PromptVerticalListContentView  import android.hardware.face.FaceSensorPropertiesInternal +import android.hardware.fingerprint.FingerprintManager  import android.hardware.fingerprint.FingerprintSensorPropertiesInternal  import android.os.Handler  import android.os.IBinder @@ -88,9 +89,8 @@ import org.mockito.Mockito.eq  import org.mockito.Mockito.never  import org.mockito.Mockito.times  import org.mockito.Mockito.verify -import org.mockito.junit.MockitoJUnit  import org.mockito.Mockito.`when` as whenever - +import org.mockito.junit.MockitoJUnit  private const val OP_PACKAGE_NAME = "biometric.testapp" @@ -99,33 +99,21 @@ private const val OP_PACKAGE_NAME = "biometric.testapp"  @SmallTest  open class AuthContainerViewTest : SysuiTestCase() { -    @JvmField @Rule -    var mockitoRule = MockitoJUnit.rule() - -    @Mock -    lateinit var callback: AuthDialogCallback -    @Mock -    lateinit var userManager: UserManager -    @Mock -    lateinit var lockPatternUtils: LockPatternUtils -    @Mock -    lateinit var wakefulnessLifecycle: WakefulnessLifecycle -    @Mock -    lateinit var panelInteractionDetector: AuthDialogPanelInteractionDetector -    @Mock -    lateinit var windowToken: IBinder -    @Mock -    lateinit var interactionJankMonitor: InteractionJankMonitor -    @Mock -    lateinit var vibrator: VibratorHelper -    @Mock -    lateinit var udfpsUtils: UdfpsUtils -    @Mock -    lateinit var authController: AuthController -    @Mock -    lateinit var selectedUserInteractor: SelectedUserInteractor -    @Mock -    private lateinit var packageManager: PackageManager +    @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + +    @Mock lateinit var callback: AuthDialogCallback +    @Mock lateinit var userManager: UserManager +    @Mock lateinit var fingerprintManager: FingerprintManager +    @Mock lateinit var lockPatternUtils: LockPatternUtils +    @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle +    @Mock lateinit var panelInteractionDetector: AuthDialogPanelInteractionDetector +    @Mock lateinit var windowToken: IBinder +    @Mock lateinit var interactionJankMonitor: InteractionJankMonitor +    @Mock lateinit var vibrator: VibratorHelper +    @Mock lateinit var udfpsUtils: UdfpsUtils +    @Mock lateinit var authController: AuthController +    @Mock lateinit var selectedUserInteractor: SelectedUserInteractor +    @Mock private lateinit var packageManager: PackageManager      @Mock private lateinit var activityTaskManager: ActivityTaskManager      private lateinit var displayRepository: FakeDisplayRepository @@ -141,11 +129,12 @@ open class AuthContainerViewTest : SysuiTestCase() {      private val fingerprintRepository = FakeFingerprintPropertyRepository()      private val displayStateRepository = FakeDisplayStateRepository()      private val credentialInteractor = FakeCredentialInteractor() -    private val bpCredentialInteractor = PromptCredentialInteractor( -        Dispatchers.Main.immediate, -        biometricPromptRepository, -        credentialInteractor, -    ) +    private val bpCredentialInteractor = +        PromptCredentialInteractor( +            Dispatchers.Main.immediate, +            biometricPromptRepository, +            credentialInteractor, +        )      private val promptSelectorInteractor by lazy {          PromptSelectorInteractorImpl(              fingerprintRepository, @@ -166,22 +155,26 @@ open class AuthContainerViewTest : SysuiTestCase() {          displayStateInteractor =              DisplayStateInteractorImpl( -                    testScope.backgroundScope, -                    mContext, -                    fakeExecutor, -                    displayStateRepository, -                    displayRepository, +                testScope.backgroundScope, +                mContext, +                fakeExecutor, +                displayStateRepository, +                displayRepository,              )          udfpsOverlayInteractor = -                UdfpsOverlayInteractor( -                        context, -                        authController, -                        selectedUserInteractor, -                        testScope.backgroundScope, -                ) +            UdfpsOverlayInteractor( +                context, +                authController, +                selectedUserInteractor, +                fingerprintManager, +                testScope.backgroundScope, +            )          biometricStatusInteractor = -                BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository, -                    fingerprintRepository) +            BiometricStatusInteractorImpl( +                activityTaskManager, +                biometricStatusRepository, +                fingerprintRepository +            )          iconProvider = IconProvider(context)          // Set up default logo icon          whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon) @@ -198,10 +191,8 @@ open class AuthContainerViewTest : SysuiTestCase() {      @Test      fun testNotifiesAnimatedIn() {          initializeFingerprintContainer() -        verify(callback).onDialogAnimatedIn( -            authContainer?.requestId ?: 0L, -            true /* startFingerprintNow */ -        ) +        verify(callback) +            .onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)      }      @Test @@ -246,10 +237,8 @@ open class AuthContainerViewTest : SysuiTestCase() {          waitForIdleSync()          // attaching the view resets the state and allows this to happen again -        verify(callback).onDialogAnimatedIn( -            authContainer?.requestId ?: 0L, -            true /* startFingerprintNow */ -        ) +        verify(callback) +            .onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)      }      @Test @@ -274,10 +263,8 @@ open class AuthContainerViewTest : SysuiTestCase() {          // the first time is triggered by initializeFingerprintContainer()          // the second time was triggered by dismissWithoutCallback() -        verify(callback, times(2)).onDialogAnimatedIn( -            authContainer?.requestId ?: 0L, -            true /* startFingerprintNow */ -        ) +        verify(callback, times(2)) +            .onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)      }      @Test @@ -288,18 +275,18 @@ open class AuthContainerViewTest : SysuiTestCase() {          verify(panelInteractionDetector).disable()      } -      @Test      fun testActionAuthenticated_sendsDismissedAuthenticated() {          val container = initializeFingerprintContainer()          container.mBiometricCallback.onAuthenticated()          waitForIdleSync() -        verify(callback).onDismissed( +        verify(callback) +            .onDismissed(                  eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),                  eq<ByteArray?>(null), /* credentialAttestation */                  eq(authContainer?.requestId ?: 0L) -        ) +            )          assertThat(container.parent).isNull()      } @@ -309,15 +296,17 @@ open class AuthContainerViewTest : SysuiTestCase() {          container.mBiometricCallback.onUserCanceled()          waitForIdleSync() -        verify(callback).onSystemEvent( +        verify(callback) +            .onSystemEvent(                  eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL),                  eq(authContainer?.requestId ?: 0L) -        ) -        verify(callback).onDismissed( +            ) +        verify(callback) +            .onDismissed(                  eq(AuthDialogCallback.DISMISSED_USER_CANCELED),                  eq<ByteArray?>(null), /* credentialAttestation */                  eq(authContainer?.requestId ?: 0L) -        ) +            )          assertThat(container.parent).isNull()      } @@ -327,19 +316,21 @@ open class AuthContainerViewTest : SysuiTestCase() {          container.mBiometricCallback.onButtonNegative()          waitForIdleSync() -        verify(callback).onDismissed( +        verify(callback) +            .onDismissed(                  eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE),                  eq<ByteArray?>(null), /* credentialAttestation */                  eq(authContainer?.requestId ?: 0L) -        ) +            )          assertThat(container.parent).isNull()      }      @Test      fun testActionTryAgain_sendsTryAgain() { -        val container = initializeFingerprintContainer( -            authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK -        ) +        val container = +            initializeFingerprintContainer( +                authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK +            )          container.mBiometricCallback.onButtonTryAgain()          waitForIdleSync() @@ -352,21 +343,24 @@ open class AuthContainerViewTest : SysuiTestCase() {          container.mBiometricCallback.onError()          waitForIdleSync() -        verify(callback).onDismissed( +        verify(callback) +            .onDismissed(                  eq(AuthDialogCallback.DISMISSED_ERROR),                  eq<ByteArray?>(null), /* credentialAttestation */                  eq(authContainer?.requestId ?: 0L) -        ) +            )          assertThat(authContainer!!.parent).isNull()      }      @Ignore("b/279650412")      @Test      fun testActionUseDeviceCredential_sendsOnDeviceCredentialPressed() { -        val container = initializeFingerprintContainer( -            authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or -                    BiometricManager.Authenticators.DEVICE_CREDENTIAL -        ) +        val container = +            initializeFingerprintContainer( +                authenticators = +                    BiometricManager.Authenticators.BIOMETRIC_WEAK or +                        BiometricManager.Authenticators.DEVICE_CREDENTIAL +            )          container.mBiometricCallback.onUseDeviceCredential()          waitForIdleSync() @@ -376,10 +370,12 @@ open class AuthContainerViewTest : SysuiTestCase() {      @Test      fun testAnimateToCredentialUI_invokesStartTransitionToCredentialUI() { -        val container = initializeFingerprintContainer( -            authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or -                    BiometricManager.Authenticators.DEVICE_CREDENTIAL -        ) +        val container = +            initializeFingerprintContainer( +                authenticators = +                    BiometricManager.Authenticators.BIOMETRIC_WEAK or +                        BiometricManager.Authenticators.DEVICE_CREDENTIAL +            )          container.animateToCredentialUI(false)          waitForIdleSync() @@ -395,10 +391,12 @@ open class AuthContainerViewTest : SysuiTestCase() {      @Test      fun testAnimateToCredentialUI_rotateCredentialUI() { -        val container = initializeFingerprintContainer( -            authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or -                    BiometricManager.Authenticators.DEVICE_CREDENTIAL -        ) +        val container = +            initializeFingerprintContainer( +                authenticators = +                    BiometricManager.Authenticators.BIOMETRIC_WEAK or +                        BiometricManager.Authenticators.DEVICE_CREDENTIAL +            )          container.animateToCredentialUI(false)          waitForIdleSync() @@ -437,13 +435,12 @@ open class AuthContainerViewTest : SysuiTestCase() {          mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)          var isButtonClicked = false          val contentView = -                PromptContentViewWithMoreOptionsButton.Builder() -                        .setMoreOptionsButtonListener( -                                fakeExecutor) { _, _ -> isButtonClicked = true } -                        .build() +            PromptContentViewWithMoreOptionsButton.Builder() +                .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> isButtonClicked = true } +                .build()          val container = -                initializeFingerprintContainer(contentViewWithMoreOptionsButton = contentView) +            initializeFingerprintContainer(contentViewWithMoreOptionsButton = contentView)          waitForIdleSync() @@ -461,9 +458,9 @@ open class AuthContainerViewTest : SysuiTestCase() {      @Test      fun testShowCredentialUI_withDescription() {          val container = -                initializeFingerprintContainer( -                        authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL -                ) +            initializeFingerprintContainer( +                authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL +            )          waitForIdleSync()          assertThat(container.hasCredentialView()).isTrue() @@ -475,10 +472,10 @@ open class AuthContainerViewTest : SysuiTestCase() {          mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)          mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)          val container = -                initializeFingerprintContainer( -                        authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL, -                        verticalListContentView = PromptVerticalListContentView.Builder().build() -                ) +            initializeFingerprintContainer( +                authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL, +                verticalListContentView = PromptVerticalListContentView.Builder().build() +            )          // Two-step credential view should show -          // 1. biometric prompt without sensor 2. credential view ui          waitForIdleSync() @@ -497,14 +494,14 @@ open class AuthContainerViewTest : SysuiTestCase() {          mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)          mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)          val contentView = -                PromptContentViewWithMoreOptionsButton.Builder() -                        .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> } -                        .build() +            PromptContentViewWithMoreOptionsButton.Builder() +                .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> } +                .build()          val container = -                initializeFingerprintContainer( -                        authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL, -                        contentViewWithMoreOptionsButton = contentView -                ) +            initializeFingerprintContainer( +                authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL, +                contentViewWithMoreOptionsButton = contentView +            )          waitForIdleSync()          assertThat(container.hasCredentialView()).isTrue() @@ -514,13 +511,13 @@ open class AuthContainerViewTest : SysuiTestCase() {      @Test      fun testCredentialViewUsesEffectiveUserId() {          whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(200) -        whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(200))).thenReturn( -            DevicePolicyManager.PASSWORD_QUALITY_SOMETHING -        ) +        whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(200))) +            .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) -        val container = initializeFingerprintContainer( -            authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL -        ) +        val container = +            initializeFingerprintContainer( +                authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL +            )          waitForIdleSync()          assertThat(container.hasCredentialPatternView()).isTrue() @@ -531,9 +528,8 @@ open class AuthContainerViewTest : SysuiTestCase() {      fun testCredentialUI_disablesClickingOnBackground() {          val container = initializeCredentialPasswordContainer()          assertThat(container.hasBiometricPrompt()).isFalse() -        assertThat( -            container.findViewById<View>(R.id.background)?.isImportantForAccessibility -        ).isFalse() +        assertThat(container.findViewById<View>(R.id.background)?.isImportantForAccessibility) +            .isFalse()          container.findViewById<View>(R.id.background)?.performClick()          waitForIdleSync() @@ -552,7 +548,7 @@ open class AuthContainerViewTest : SysuiTestCase() {      fun testLayoutParams_hasShowWhenLockedFlag() {          val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")          assertThat((layoutParams.flags and WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) != 0) -                .isTrue() +            .isTrue()      }      @Test @@ -590,20 +586,20 @@ open class AuthContainerViewTest : SysuiTestCase() {      }      private fun initializeCredentialPasswordContainer( -            addToView: Boolean = true, +        addToView: Boolean = true,      ): TestAuthContainerView {          whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20) -        whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))).thenReturn( -            DevicePolicyManager.PASSWORD_QUALITY_NUMERIC -        ) +        whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))) +            .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC)          // In the credential view, clicking on the background (to cancel authentication) is not          // valid. Thus, the listener should be null, and it should not be in the accessibility          // hierarchy. -        val container = initializeFingerprintContainer( +        val container = +            initializeFingerprintContainer(                  authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,                  addToView = addToView, -        ) +            )          waitForIdleSync()          assertThat(container.hasCredentialPasswordView()).isTrue() @@ -615,26 +611,28 @@ open class AuthContainerViewTest : SysuiTestCase() {          addToView: Boolean = true,          verticalListContentView: PromptVerticalListContentView? = null,          contentViewWithMoreOptionsButton: PromptContentViewWithMoreOptionsButton? = null, -    ) = initializeContainer( -        TestAuthContainerView( -            authenticators = authenticators, -            fingerprintProps = fingerprintSensorPropertiesInternal(), +    ) = +        initializeContainer( +            TestAuthContainerView( +                authenticators = authenticators, +                fingerprintProps = fingerprintSensorPropertiesInternal(),                  verticalListContentView = verticalListContentView, -        ), -        addToView -    ) +            ), +            addToView +        )      private fun initializeCoexContainer(          authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,          addToView: Boolean = true -    ) = initializeContainer( -        TestAuthContainerView( -            authenticators = authenticators, -            fingerprintProps = fingerprintSensorPropertiesInternal(), -            faceProps = faceSensorPropertiesInternal() -        ), -        addToView -    ) +    ) = +        initializeContainer( +            TestAuthContainerView( +                authenticators = authenticators, +                fingerprintProps = fingerprintSensorPropertiesInternal(), +                faceProps = faceSensorPropertiesInternal() +            ), +            addToView +        )      private fun initializeContainer(          view: TestAuthContainerView, @@ -655,47 +653,50 @@ open class AuthContainerViewTest : SysuiTestCase() {          faceProps: List<FaceSensorPropertiesInternal> = listOf(),          verticalListContentView: PromptVerticalListContentView? = null,          contentViewWithMoreOptionsButton: PromptContentViewWithMoreOptionsButton? = null, -    ) : AuthContainerView( -        Config().apply { -            mContext = this@AuthContainerViewTest.context -            mCallback = callback -            mSensorIds = (fingerprintProps.map { it.sensorId } + -                faceProps.map { it.sensorId }).toIntArray() -            mSkipAnimation = true -            mPromptInfo = PromptInfo().apply { -                this.authenticators = authenticators -                if (verticalListContentView != null) { -                    this.contentView = verticalListContentView -                } else if (contentViewWithMoreOptionsButton != null) { -                    this.contentView = contentViewWithMoreOptionsButton -                } -            } -            mOpPackageName = OP_PACKAGE_NAME -        }, -        testScope.backgroundScope, -        fingerprintProps, -        faceProps, -        wakefulnessLifecycle, -        panelInteractionDetector, -        userManager, -        lockPatternUtils, -        interactionJankMonitor, -        { promptSelectorInteractor }, -        PromptViewModel( -            displayStateInteractor, -            promptSelectorInteractor, -            context, -            udfpsOverlayInteractor, -            biometricStatusInteractor, -            udfpsUtils, -            iconProvider, -            activityTaskManager -        ), -        { credentialViewModel }, -        Handler(TestableLooper.get(this).looper), -        fakeExecutor, -        vibrator -    ) { +    ) : +        AuthContainerView( +            Config().apply { +                mContext = this@AuthContainerViewTest.context +                mCallback = callback +                mSensorIds = +                    (fingerprintProps.map { it.sensorId } + faceProps.map { it.sensorId }) +                        .toIntArray() +                mSkipAnimation = true +                mPromptInfo = +                    PromptInfo().apply { +                        this.authenticators = authenticators +                        if (verticalListContentView != null) { +                            this.contentView = verticalListContentView +                        } else if (contentViewWithMoreOptionsButton != null) { +                            this.contentView = contentViewWithMoreOptionsButton +                        } +                    } +                mOpPackageName = OP_PACKAGE_NAME +            }, +            testScope.backgroundScope, +            fingerprintProps, +            faceProps, +            wakefulnessLifecycle, +            panelInteractionDetector, +            userManager, +            lockPatternUtils, +            interactionJankMonitor, +            { promptSelectorInteractor }, +            PromptViewModel( +                displayStateInteractor, +                promptSelectorInteractor, +                context, +                udfpsOverlayInteractor, +                biometricStatusInteractor, +                udfpsUtils, +                iconProvider, +                activityTaskManager +            ), +            { credentialViewModel }, +            Handler(TestableLooper.get(this).looper), +            fakeExecutor, +            vibrator +        ) {          override fun postOnAnimation(runnable: Runnable) {              runnable.run()          } @@ -717,8 +718,10 @@ open class AuthContainerViewTest : SysuiTestCase() {          val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")          val lpFlags = layoutParams.flags -        assertThat((lpFlags and WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) -                != 0).isTrue() +        assertThat( +                (lpFlags and WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) != 0 +            ) +            .isTrue()      }      @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt index 3d63c5b6d0f8..13f2c7212e36 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt @@ -17,6 +17,7 @@  package com.android.systemui.biometrics.domain.interactor  import android.graphics.Rect +import android.hardware.fingerprint.FingerprintManager  import android.view.MotionEvent  import android.view.Surface  import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -51,6 +52,7 @@ class UdfpsOverlayInteractorTest : SysuiTestCase() {      private lateinit var testScope: TestScope +    @Mock private lateinit var fingerprintManager: FingerprintManager      @Mock private lateinit var authController: AuthController      @Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback> @@ -111,6 +113,7 @@ class UdfpsOverlayInteractorTest : SysuiTestCase() {                  context,                  authController,                  selectedUserInteractor, +                fingerprintManager,                  testScope.backgroundScope              )          testScope.runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt deleted file mode 100644 index b986c52309f8..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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.keyboard.shortcut.data.repository - -import android.view.KeyEvent -import android.view.KeyboardShortcutGroup -import android.view.KeyboardShortcutInfo -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.keyboard.shortcut.shared.model.Shortcut -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand -import com.android.systemui.keyboard.shortcut.shared.model.shortcutCategory -import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesRepository -import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper -import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.testScope -import com.android.systemui.testKosmos -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class ShortcutHelperCategoriesRepositoryTest : SysuiTestCase() { -    @OptIn(ExperimentalCoroutinesApi::class) -    private val kosmos = testKosmos().also { it.testDispatcher = UnconfinedTestDispatcher() } -    private val repo = kosmos.shortcutHelperCategoriesRepository -    private val helper = kosmos.shortcutHelperTestHelper -    private val testScope = kosmos.testScope - -    @Test -    fun stateActive_imeShortcuts_shortcutInfoCorrectlyConverted() = -        testScope.runTest { -            helper.setImeShortcuts(imeShortcutsGroupWithPreviousLanguageSwitchShortcut) -            val imeShortcutCategory by collectLastValue(repo.imeShortcutsCategory) - -            helper.showFromActivity() - -            assertThat(imeShortcutCategory) -                .isEqualTo(expectedImeShortcutCategoryWithPreviousLanguageSwitchShortcut) -        } - -    @Test -    fun stateActive_imeShortcuts_onlyUnsupportedShortcuts_discardsAll() = -        testScope.runTest { -            helper.setImeShortcuts(imeShortcutsGroupWithUnsupportedShortcutModifiers) -            val imeShortcutCategory by collectLastValue(repo.imeShortcutsCategory) - -            helper.showFromActivity() - -            assertThat(imeShortcutCategory).isEqualTo(null) -        } - -    private val switchToPreviousLanguageCommand = -        ShortcutCommand( -            listOf(KeyEvent.META_CTRL_ON, KeyEvent.META_SHIFT_ON, KeyEvent.KEYCODE_SPACE) -        ) - -    private val expectedImeShortcutCategoryWithDiscardedUnsupportedShortcuts = -        shortcutCategory(ShortcutCategoryType.IME) { subCategory("input", emptyList()) } - -    private val switchToPreviousLanguageKeyboardShortcutInfo = -        KeyboardShortcutInfo( -            /* label = */ "switch to previous language", -            /* keycode = */ switchToPreviousLanguageCommand.keyCodes[2], -            /* modifiers = */ switchToPreviousLanguageCommand.keyCodes[0] or -                switchToPreviousLanguageCommand.keyCodes[1], -        ) - -    private val expectedImeShortcutCategoryWithPreviousLanguageSwitchShortcut = -        shortcutCategory(ShortcutCategoryType.IME) { -            subCategory( -                "input", -                listOf( -                    Shortcut( -                        switchToPreviousLanguageKeyboardShortcutInfo.label!!.toString(), -                        listOf(switchToPreviousLanguageCommand) -                    ) -                ) -            ) -        } - -    private val imeShortcutsGroupWithPreviousLanguageSwitchShortcut = -        listOf( -            KeyboardShortcutGroup( -                "input", -                listOf( -                    switchToPreviousLanguageKeyboardShortcutInfo, -                ) -            ) -        ) - -    private val shortcutInfoWithUnsupportedModifier = -        KeyboardShortcutInfo( -            /* label = */ "unsupported shortcut", -            /* keycode = */ KeyEvent.KEYCODE_SPACE, -            /* modifiers = */ 32 -        ) - -    private val imeShortcutsGroupWithUnsupportedShortcutModifiers = -        listOf( -            KeyboardShortcutGroup( -                "input", -                listOf( -                    shortcutInfoWithUnsupportedModifier, -                ) -            ) -        ) -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt index 3caa8f6cd138..0b4e6a289f0e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt @@ -65,17 +65,6 @@ class ShortcutHelperStateRepositoryTest : SysuiTestCase() {          }      @Test -    fun state_activeThroughActivity_virtualKeyboardActive_emitsActiveWithVirtualDeviceId() = -        testScope.runTest { -            val state by collectLastValue(repo.state) - -            fakeInputManager.addVirtualKeyboard() -            helper.showFromActivity() - -            assertThat(state).isEqualTo(ShortcutHelperState.Active(VIRTUAL_KEYBOARD)) -        } - -    @Test      fun state_activeThroughActivity_physicalKeyboardActive_emitsActiveWithDeviceId() =          testScope.runTest {              val deviceId = 456 diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt new file mode 100644 index 000000000000..c2814bd45e86 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt @@ -0,0 +1,235 @@ +/* + * 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.keyboard.shortcut.data.source + +import android.view.KeyEvent +import android.view.KeyboardShortcutGroup +import android.view.KeyboardShortcutInfo +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory +import com.android.systemui.keyboard.shortcut.shared.model.shortcut +import com.android.systemui.res.R + +object TestShortcuts { + +    private val shortcutInfoWithRepeatedLabel = +        KeyboardShortcutInfo( +            /* label = */ "Shortcut with repeated label", +            /* keycode = */ KeyEvent.KEYCODE_H, +            /* modifiers = */ KeyEvent.META_META_ON, +        ) + +    private val shortcutInfoWithRepeatedLabelAlternate = +        KeyboardShortcutInfo( +            /* label = */ shortcutInfoWithRepeatedLabel.label, +            /* keycode = */ KeyEvent.KEYCODE_L, +            /* modifiers = */ KeyEvent.META_META_ON, +        ) + +    private val shortcutInfoWithRepeatedLabelSecondAlternate = +        KeyboardShortcutInfo( +            /* label = */ shortcutInfoWithRepeatedLabel.label, +            /* keycode = */ KeyEvent.KEYCODE_M, +            /* modifiers = */ KeyEvent.META_SHIFT_ON, +        ) + +    private val shortcutWithGroupedRepeatedLabel = +        shortcut(shortcutInfoWithRepeatedLabel.label!!.toString()) { +            command { +                key(R.drawable.ic_ksh_key_meta) +                key("H") +            } +            command { +                key(R.drawable.ic_ksh_key_meta) +                key("L") +            } +            command { +                key("Shift") +                key("M") +            } +        } + +    private val standardShortcutInfo1 = +        KeyboardShortcutInfo( +            /* label = */ "Standard shortcut 1", +            /* keycode = */ KeyEvent.KEYCODE_N, +            /* modifiers = */ KeyEvent.META_META_ON, +        ) + +    private val standardShortcut1 = +        shortcut(standardShortcutInfo1.label!!.toString()) { +            command { +                key(R.drawable.ic_ksh_key_meta) +                key("N") +            } +        } + +    private val standardShortcutInfo2 = +        KeyboardShortcutInfo( +            /* label = */ "Standard shortcut 2", +            /* keycode = */ KeyEvent.KEYCODE_Z, +            /* modifiers = */ KeyEvent.META_ALT_ON or KeyEvent.META_SHIFT_ON, +        ) + +    private val standardShortcut2 = +        shortcut(standardShortcutInfo2.label!!.toString()) { +            command { +                key("Alt") +                key("Shift") +                key("Z") +            } +        } + +    private val standardShortcutInfo3 = +        KeyboardShortcutInfo( +            /* label = */ "Standard shortcut 3", +            /* keycode = */ KeyEvent.KEYCODE_J, +            /* modifiers = */ KeyEvent.META_CTRL_ON, +        ) + +    private val standardShortcut3 = +        shortcut(standardShortcutInfo3.label!!.toString()) { +            command { +                key("Ctrl") +                key("J") +            } +        } + +    private val shortcutInfoWithUnsupportedModifiers = +        KeyboardShortcutInfo( +            /* label = */ "Shortcut with unsupported modifiers", +            /* keycode = */ KeyEvent.KEYCODE_A, +            /* modifiers = */ KeyEvent.META_META_ON or KeyEvent.KEYCODE_SPACE, +        ) + +    private val groupWithRepeatedShortcutLabels = +        KeyboardShortcutGroup( +            "Group with duplicate labels", +            listOf( +                shortcutInfoWithRepeatedLabel, +                shortcutInfoWithRepeatedLabelAlternate, +                shortcutInfoWithRepeatedLabelSecondAlternate +            ) +        ) + +    private val subCategoryWithGroupedRepeatedShortcutLabels = +        ShortcutSubCategory( +            label = groupWithRepeatedShortcutLabels.label!!.toString(), +            shortcuts = listOf(shortcutWithGroupedRepeatedLabel) +        ) + +    private val groupWithStandardShortcutInfo = +        KeyboardShortcutGroup("Standard group", listOf(standardShortcutInfo1)) + +    private val subCategoryWithStandardShortcut = +        ShortcutSubCategory( +            label = groupWithStandardShortcutInfo.label!!.toString(), +            shortcuts = listOf(standardShortcut1) +        ) + +    private val groupWithOnlyUnsupportedModifierShortcut = +        KeyboardShortcutGroup( +            "Group with unsupported modifiers", +            listOf(shortcutInfoWithUnsupportedModifiers) +        ) + +    private val groupWithSupportedAndUnsupportedModifierShortcut = +        KeyboardShortcutGroup( +            "Group with mix of supported and not supported modifiers", +            listOf(standardShortcutInfo3, shortcutInfoWithUnsupportedModifiers) +        ) + +    private val subCategoryWithUnsupportedShortcutsRemoved = +        ShortcutSubCategory( +            groupWithSupportedAndUnsupportedModifierShortcut.label!!.toString(), +            listOf(standardShortcut3) +        ) + +    private val standardGroup1 = +        KeyboardShortcutGroup( +            "Standard group 1", +            listOf(standardShortcutInfo1, standardShortcutInfo2, standardShortcutInfo3) +        ) + +    private val standardSubCategory1 = +        ShortcutSubCategory( +            standardGroup1.label!!.toString(), +            listOf(standardShortcut1, standardShortcut2, standardShortcut3) +        ) + +    private val standardGroup2 = +        KeyboardShortcutGroup( +            "Standard group 2", +            listOf(standardShortcutInfo3, standardShortcutInfo2, standardShortcutInfo1) +        ) + +    private val standardSubCategory2 = +        ShortcutSubCategory( +            standardGroup2.label!!.toString(), +            listOf(standardShortcut3, standardShortcut2, standardShortcut1) +        ) +    private val standardGroup3 = +        KeyboardShortcutGroup( +            "Standard group 3", +            listOf(standardShortcutInfo2, standardShortcutInfo1) +        ) + +    private val standardSubCategory3 = +        ShortcutSubCategory( +            standardGroup3.label!!.toString(), +            listOf(standardShortcut2, standardShortcut1) +        ) +    val imeGroups = listOf(standardGroup1, standardGroup2, standardGroup3) +    val imeCategory = +        ShortcutCategory( +            type = ShortcutCategoryType.IME, +            subCategories = listOf(standardSubCategory1, standardSubCategory2, standardSubCategory3) +        ) + +    val systemGroups = listOf(standardGroup3, standardGroup2, standardGroup1) +    val systemCategory = +        ShortcutCategory( +            type = ShortcutCategoryType.SYSTEM, +            subCategories = listOf(standardSubCategory3, standardSubCategory2, standardSubCategory1) +        ) + +    val multitaskingGroups = listOf(standardGroup2, standardGroup1) +    val multitaskingCategory = +        ShortcutCategory( +            type = ShortcutCategoryType.MULTI_TASKING, +            subCategories = listOf(standardSubCategory2, standardSubCategory1) +        ) + +    val groupsWithDuplicateShortcutLabels = +        listOf(groupWithRepeatedShortcutLabels, groupWithStandardShortcutInfo) + +    val subCategoriesWithGroupedDuplicatedShortcutLabels = +        listOf(subCategoryWithGroupedRepeatedShortcutLabels, subCategoryWithStandardShortcut) + +    val groupsWithUnsupportedModifier = +        listOf( +            groupWithStandardShortcutInfo, +            groupWithOnlyUnsupportedModifierShortcut, +            groupWithSupportedAndUnsupportedModifierShortcut +        ) + +    val subCategoriesWithUnsupportedModifiersRemoved = +        listOf(subCategoryWithStandardShortcut, subCategoryWithUnsupportedShortcutsRemoved) + +    val groupsWithOnlyUnsupportedModifiers = listOf(groupWithOnlyUnsupportedModifierShortcut) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt index c00e7e794d1c..c6a7565efcd7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt @@ -16,18 +16,16 @@  package com.android.systemui.keyboard.shortcut.domain.interactor -import android.view.KeyEvent -import android.view.KeyboardShortcutGroup -import android.view.KeyboardShortcutInfo  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.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts  import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory -import com.android.systemui.keyboard.shortcut.shared.model.shortcut +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.IME +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MULTI_TASKING +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.SYSTEM  import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesInteractor  import com.android.systemui.keyboard.shortcut.shortcutHelperMultiTaskingShortcutsSource  import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSource @@ -56,15 +54,16 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() {              it.shortcutHelperSystemShortcutsSource = systemShortcutsSource              it.shortcutHelperMultiTaskingShortcutsSource = multitaskingShortcutsSource          } +      private val testScope = kosmos.testScope      private val interactor = kosmos.shortcutHelperCategoriesInteractor      private val helper = kosmos.shortcutHelperTestHelper      @Before -    fun setUp() { -        // Setting these sources as empty temporarily. Will be populated in follow up CL. -        systemShortcutsSource.setGroups(emptyList()) -        multitaskingShortcutsSource.setGroups(emptyList()) +    fun setShortcuts() { +        helper.setImeShortcuts(TestShortcuts.imeGroups) +        systemShortcutsSource.setGroups(TestShortcuts.systemGroups) +        multitaskingShortcutsSource.setGroups(TestShortcuts.multitaskingGroups)      }      @Test @@ -78,12 +77,17 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() {      @Test      fun categories_stateActive_emitsAllCategoriesInOrder() =          testScope.runTest { -            helper.setImeShortcuts(imeShortcutGroups)              val categories by collectLastValue(interactor.shortcutCategories)              helper.showFromActivity() -            assertThat(categories).containsExactly(imeShortcutCategory).inOrder() +            assertThat(categories) +                .containsExactly( +                    TestShortcuts.systemCategory, +                    TestShortcuts.multitaskingCategory, +                    TestShortcuts.imeCategory, +                ) +                .inOrder()          }      @Test @@ -96,159 +100,177 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() {              assertThat(categories).isEmpty()          } -    fun categories_stateActive_emitsGroupedShortcuts() = +    @Test +    fun categories_stateActive_imeShortcutsWithDuplicateLabels_emitsGroupedShortcuts() =          testScope.runTest { -            helper.setImeShortcuts(imeShortcutsGroupsWithDuplicateLabels) +            helper.setImeShortcuts(TestShortcuts.groupsWithDuplicateShortcutLabels) +              val categories by collectLastValue(interactor.shortcutCategories)              helper.showFromActivity() -            assertThat(categories).containsExactly(expectedGroupedShortcutCategories) +            assertThat(categories) +                .containsExactly( +                    TestShortcuts.systemCategory, +                    TestShortcuts.multitaskingCategory, +                    ShortcutCategory( +                        type = IME, +                        subCategories = +                            TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels +                    ), +                ) +                .inOrder()          } -    private val switchToNextLanguageShortcut = -        shortcut(label = "switch to next language") { -            command(KeyEvent.META_CTRL_ON, KeyEvent.KEYCODE_SPACE) -        } +    @Test +    fun categories_stateActive_systemShortcutsWithDuplicateLabels_emitsGroupedShortcuts() = +        testScope.runTest { +            systemShortcutsSource.setGroups(TestShortcuts.groupsWithDuplicateShortcutLabels) -    private val switchToNextLanguageKeyboardShortcutInfo = -        KeyboardShortcutInfo( -            /* label = */ switchToNextLanguageShortcut.label, -            /* keycode = */ switchToNextLanguageShortcut.commands[0].keyCodes[1], -            /* modifiers = */ switchToNextLanguageShortcut.commands[0].keyCodes[0], -        ) +            val categories by collectLastValue(interactor.shortcutCategories) -    private val switchToNextLanguageShortcutAlternative = -        shortcut("switch to next language") { -            command(KeyEvent.META_CTRL_ON, KeyEvent.KEYCODE_SPACE) -        } +            helper.showFromActivity() -    private val switchToNextLanguageKeyboardShortcutInfoAlternative = -        KeyboardShortcutInfo( -            /* label = */ switchToNextLanguageShortcutAlternative.label, -            /* keycode = */ switchToNextLanguageShortcutAlternative.commands[0].keyCodes[1], -            /* modifiers = */ switchToNextLanguageShortcutAlternative.commands[0].keyCodes[0], -        ) - -    private val switchToPreviousLanguageShortcut = -        shortcut("switch to previous language") { -            command( -                KeyEvent.META_SHIFT_ON, -                KeyEvent.KEYCODE_SPACE, -            ) +            assertThat(categories) +                .containsExactly( +                    ShortcutCategory( +                        type = SYSTEM, +                        subCategories = +                            TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels +                    ), +                    TestShortcuts.multitaskingCategory, +                    TestShortcuts.imeCategory, +                ) +                .inOrder()          } -    private val switchToPreviousLanguageKeyboardShortcutInfo = -        KeyboardShortcutInfo( -            /* label = */ switchToPreviousLanguageShortcut.label, -            /* keycode = */ switchToPreviousLanguageShortcut.commands[0].keyCodes[1], -            /* modifiers = */ switchToPreviousLanguageShortcut.commands[0].keyCodes[0], -        ) - -    private val switchToPreviousLanguageShortcutAlternative = -        shortcut("switch to previous language") { -            command( -                KeyEvent.META_SHIFT_ON, -                KeyEvent.KEYCODE_SPACE, -            ) +    @Test +    fun categories_stateActive_multiTaskingShortcutsWithDuplicateLabels_emitsGroupedShortcuts() = +        testScope.runTest { +            multitaskingShortcutsSource.setGroups(TestShortcuts.groupsWithDuplicateShortcutLabels) + +            val categories by collectLastValue(interactor.shortcutCategories) + +            helper.showFromActivity() + +            assertThat(categories) +                .containsExactly( +                    TestShortcuts.systemCategory, +                    ShortcutCategory( +                        type = MULTI_TASKING, +                        subCategories = +                            TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels +                    ), +                    TestShortcuts.imeCategory, +                ) +                .inOrder()          } -    private val switchToPreviousLanguageKeyboardShortcutInfoAlternative = -        KeyboardShortcutInfo( -            /* label = */ switchToPreviousLanguageShortcutAlternative.label, -            /* keycode = */ switchToPreviousLanguageShortcutAlternative.commands[0].keyCodes[1], -            /* modifiers = */ switchToPreviousLanguageShortcutAlternative.commands[0].keyCodes[0], -        ) +    @Test +    fun categories_stateActive_imeShortcutsWithUnsupportedModifiers_discardUnsupported() = +        testScope.runTest { +            helper.setImeShortcuts(TestShortcuts.groupsWithUnsupportedModifier) +            val categories by collectLastValue(interactor.shortcutCategories) + +            helper.showFromActivity() -    private val showOnscreenKeyboardShortcut = -        shortcut(label = "Show on-screen keyboard") { -            command(KeyEvent.META_ALT_ON, KeyEvent.KEYCODE_K) +            assertThat(categories) +                .containsExactly( +                    TestShortcuts.systemCategory, +                    TestShortcuts.multitaskingCategory, +                    ShortcutCategory( +                        type = IME, +                        subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved +                    ), +                ) +                .inOrder()          } -    private val showOnScreenKeyboardShortcutInfo = -        KeyboardShortcutInfo( -            /* label = */ showOnscreenKeyboardShortcut.label, -            /* keycode = */ showOnscreenKeyboardShortcut.commands[0].keyCodes[1], -            /* modifiers = */ showOnscreenKeyboardShortcut.commands[0].keyCodes[0], -        ) - -    private val accessClipboardShortcut = -        shortcut(label = "Access clipboard") { command(KeyEvent.META_ALT_ON, KeyEvent.KEYCODE_V) } - -    private val accessClipboardShortcutInfo = -        KeyboardShortcutInfo( -            /* label = */ accessClipboardShortcut.label, -            /* keycode = */ accessClipboardShortcut.commands[0].keyCodes[1], -            /* modifiers = */ accessClipboardShortcut.commands[0].keyCodes[0], -        ) - -    private val imeShortcutGroups = -        listOf( -            KeyboardShortcutGroup( -                /* label = */ "input", -                /* shortcutInfoList = */ listOf( -                    switchToNextLanguageKeyboardShortcutInfo, -                    switchToPreviousLanguageKeyboardShortcutInfo +    @Test +    fun categories_stateActive_systemShortcutsWithUnsupportedModifiers_discardUnsupported() = +        testScope.runTest { +            systemShortcutsSource.setGroups(TestShortcuts.groupsWithUnsupportedModifier) +            val categories by collectLastValue(interactor.shortcutCategories) + +            helper.showFromActivity() + +            assertThat(categories) +                .containsExactly( +                    ShortcutCategory( +                        type = SYSTEM, +                        subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved +                    ), +                    TestShortcuts.multitaskingCategory, +                    TestShortcuts.imeCategory,                  ) -            ) -        ) - -    private val imeShortcutCategory = -        ShortcutCategory( -            type = ShortcutCategoryType.IME, -            subCategories = -                listOf( -                    ShortcutSubCategory( -                        imeShortcutGroups[0].label.toString(), -                        listOf(switchToNextLanguageShortcut, switchToPreviousLanguageShortcut) -                    ) +                .inOrder() +        } + +    @Test +    fun categories_stateActive_multitaskingShortcutsWithUnsupportedModifiers_discardUnsupported() = +        testScope.runTest { +            multitaskingShortcutsSource.setGroups(TestShortcuts.groupsWithUnsupportedModifier) +            val categories by collectLastValue(interactor.shortcutCategories) + +            helper.showFromActivity() + +            assertThat(categories) +                .containsExactly( +                    TestShortcuts.systemCategory, +                    ShortcutCategory( +                        type = MULTI_TASKING, +                        subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved +                    ), +                    TestShortcuts.imeCategory,                  ) -        ) - -    private val imeShortcutsGroupsWithDuplicateLabels = -        listOf( -            KeyboardShortcutGroup( -                "input", -                listOf( -                    switchToNextLanguageKeyboardShortcutInfo, -                    switchToNextLanguageKeyboardShortcutInfoAlternative, -                    switchToPreviousLanguageKeyboardShortcutInfo, -                    switchToPreviousLanguageKeyboardShortcutInfoAlternative +                .inOrder() +        } + +    @Test +    fun categories_stateActive_imeShortcutsWitOnlyUnsupportedModifiers_discardsCategory() = +        testScope.runTest { +            helper.setImeShortcuts(TestShortcuts.groupsWithOnlyUnsupportedModifiers) +            val categories by collectLastValue(interactor.shortcutCategories) + +            helper.showFromActivity() + +            assertThat(categories) +                .containsExactly( +                    TestShortcuts.systemCategory, +                    TestShortcuts.multitaskingCategory,                  ) -            ), -            KeyboardShortcutGroup( -                "Gboard", -                listOf( -                    showOnScreenKeyboardShortcutInfo, -                    accessClipboardShortcutInfo, +                .inOrder() +        } + +    @Test +    fun categories_stateActive_systemShortcutsWitOnlyUnsupportedModifiers_discardsCategory() = +        testScope.runTest { +            systemShortcutsSource.setGroups(TestShortcuts.groupsWithOnlyUnsupportedModifiers) +            val categories by collectLastValue(interactor.shortcutCategories) + +            helper.showFromActivity() + +            assertThat(categories) +                .containsExactly( +                    TestShortcuts.multitaskingCategory, +                    TestShortcuts.imeCategory,                  ) -            ) -        ) - -    private val expectedGroupedShortcutCategories = -        ShortcutCategory( -            type = ShortcutCategoryType.IME, -            subCategories = -                listOf( -                    ShortcutSubCategory( -                        imeShortcutsGroupsWithDuplicateLabels[0].label.toString(), -                        listOf( -                            switchToNextLanguageShortcut.copy( -                                commands = -                                    switchToNextLanguageShortcut.commands + -                                        switchToNextLanguageShortcutAlternative.commands -                            ), -                            switchToPreviousLanguageShortcut.copy( -                                commands = -                                    switchToPreviousLanguageShortcut.commands + -                                        switchToPreviousLanguageShortcutAlternative.commands -                            ) -                        ), -                    ), -                    ShortcutSubCategory( -                        imeShortcutsGroupsWithDuplicateLabels[1].label.toString(), -                        listOf(showOnscreenKeyboardShortcut, accessClipboardShortcut), -                    ) +                .inOrder() +        } + +    @Test +    fun categories_stateActive_multitaskingShortcutsWitOnlyUnsupportedModifiers_discardsCategory() = +        testScope.runTest { +            multitaskingShortcutsSource.setGroups(TestShortcuts.groupsWithOnlyUnsupportedModifiers) +            val categories by collectLastValue(interactor.shortcutCategories) + +            helper.showFromActivity() + +            assertThat(categories) +                .containsExactly( +                    TestShortcuts.systemCategory, +                    TestShortcuts.imeCategory,                  ) -        ) +                .inOrder() +        }  } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt index f32e7757328f..c9871f1e4e1e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt @@ -21,6 +21,9 @@ import androidx.test.filters.SmallTest  import com.android.systemui.SysuiTestCase  import com.android.systemui.coroutines.collectLastValue  import com.android.systemui.coroutines.collectValues +import com.android.systemui.flags.DisableSceneContainer +import com.android.systemui.flags.EnableSceneContainer +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository  import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository  import com.android.systemui.keyguard.shared.model.KeyguardState  import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel @@ -28,12 +31,18 @@ import com.android.systemui.keyguard.shared.model.TransitionState  import com.android.systemui.keyguard.shared.model.TransitionStep  import com.android.systemui.keyguard.util.mockTopActivityClassName  import com.android.systemui.kosmos.testScope +import com.android.systemui.scene.data.repository.Idle +import com.android.systemui.scene.data.repository.Transition +import com.android.systemui.scene.data.repository.setSceneTransition +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.shared.system.ActivityManagerWrapper  import com.android.systemui.shared.system.activityManagerWrapper  import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor  import com.android.systemui.testKosmos  import com.android.systemui.util.assertValuesMatch  import com.google.common.truth.Truth.assertThat  import kotlin.test.Test +import kotlinx.coroutines.test.TestScope  import kotlinx.coroutines.test.runCurrent  import kotlinx.coroutines.test.runTest  import org.junit.Before @@ -44,16 +53,22 @@ import org.junit.runner.RunWith  @kotlinx.coroutines.ExperimentalCoroutinesApi  class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {      private val kosmos = testKosmos() -    private val testScope = kosmos.testScope -    private val underTest = kosmos.keyguardSurfaceBehindInteractor -    private val transitionRepository = kosmos.fakeKeyguardTransitionRepository -    private val inWindowUnlockInteractor = kosmos.inWindowLauncherUnlockAnimationInteractor -    private val activityManagerWrapper = kosmos.activityManagerWrapper +    private lateinit var testScope: TestScope +    private lateinit var underTest: KeyguardSurfaceBehindInteractor +    private lateinit var transitionRepository: FakeKeyguardTransitionRepository +    private lateinit var inWindowUnlockInteractor: InWindowLauncherUnlockAnimationInteractor +    private lateinit var activityManagerWrapper: ActivityManagerWrapper      private val LAUNCHER_ACTIVITY_NAME = "launcher"      @Before      fun setUp() { +        testScope = kosmos.testScope +        underTest = kosmos.keyguardSurfaceBehindInteractor +        transitionRepository = kosmos.fakeKeyguardTransitionRepository +        inWindowUnlockInteractor = kosmos.inWindowLauncherUnlockAnimationInteractor +        activityManagerWrapper = kosmos.activityManagerWrapper +          inWindowUnlockInteractor.setLauncherActivityClass(LAUNCHER_ACTIVITY_NAME)          // Default to having something other than Launcher on top. @@ -61,6 +76,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {      }      @Test +    @DisableSceneContainer      fun testSurfaceBehindModel_toAppSurface() =          testScope.runTest {              val values by collectValues(underTest.viewParams) @@ -136,6 +152,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {          }      @Test +    @DisableSceneContainer      fun testSurfaceBehindModel_toLauncher() =          testScope.runTest {              val values by collectValues(underTest.viewParams) @@ -196,6 +213,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {          }      @Test +    @DisableSceneContainer      fun testSurfaceBehindModel_fromNotificationLaunch() =          testScope.runTest {              val values by collectValues(underTest.viewParams) @@ -230,6 +248,105 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {          }      @Test +    @EnableSceneContainer +    fun testSurfaceBehindModel_toAppSurface_scene_container() = +        testScope.runTest { +            val values by collectValues(underTest.viewParams) +            runCurrent() + +            assertThat(values) +                .containsExactly( +                    // We're initialized in LOCKSCREEN. +                    KeyguardSurfaceBehindModel(alpha = 0f), +                ) + +            kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone)) + +            values.assertValuesMatch( +                { it == KeyguardSurfaceBehindModel(alpha = 0f) }, +                // Once we start a transition to GONE, we should fade in and translate up. The exact +                // start value depends on screen density, so just look for != 0. +                { +                    it.animateFromAlpha == 0f && +                        it.alpha == 1f && +                        it.animateFromTranslationY != 0f && +                        it.translationY == 0f +                } +            ) + +            kosmos.setSceneTransition(Idle(Scenes.Gone)) + +            values.assertValuesMatch( +                { it == KeyguardSurfaceBehindModel(alpha = 0f) }, +                { +                    it.animateFromAlpha == 0f && +                        it.alpha == 1f && +                        it.animateFromTranslationY != 0f && +                        it.translationY == 0f +                }, +                // Once the current state is GONE, we should default to alpha = 1f. +                { it == KeyguardSurfaceBehindModel(alpha = 1f) } +            ) +        } + +    @Test +    @EnableSceneContainer +    fun testSurfaceBehindModel_toLauncher_scene_container() = +        testScope.runTest { +            val values by collectValues(underTest.viewParams) +            activityManagerWrapper.mockTopActivityClassName(LAUNCHER_ACTIVITY_NAME) +            runCurrent() + +            assertThat(values) +                .containsExactly( +                    // We're initialized in LOCKSCREEN. +                    KeyguardSurfaceBehindModel(alpha = 0f), +                ) +                .inOrder() + +            kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone)) + +            assertThat(values) +                .containsExactly( +                    KeyguardSurfaceBehindModel(alpha = 0f), +                    // We should instantly set alpha = 1, with no animations, when Launcher is +                    // behind +                    // the keyguard since we're playing in-window animations. +                    KeyguardSurfaceBehindModel(alpha = 1f), +                ) +                .inOrder() + +            kosmos.setSceneTransition(Idle(Scenes.Gone)) + +            assertThat(values) +                .containsExactly( +                    KeyguardSurfaceBehindModel(alpha = 0f), +                    // Should have remained at alpha = 1f through the entire animation. +                    KeyguardSurfaceBehindModel(alpha = 1f), +                ) +                .inOrder() +        } + +    @Test +    @EnableSceneContainer +    fun testSurfaceBehindModel_fromNotificationLaunch_scene_container() = +        testScope.runTest { +            val values by collectValues(underTest.viewParams) +            runCurrent() + +            kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true) +            runCurrent() + +            kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone)) + +            values.assertValuesMatch( +                // We should be at alpha = 0f during the animation. +                { it == KeyguardSurfaceBehindModel(alpha = 0f) }, +            ) +        } + +    @Test +    @DisableSceneContainer      fun notificationLaunchFromLockscreen_isAnimatingSurfaceTrue() =          testScope.runTest {              val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface) @@ -253,6 +370,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {          }      @Test +    @DisableSceneContainer      fun notificationLaunchFromGone_isAnimatingSurfaceFalse() =          testScope.runTest {              val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface) @@ -276,6 +394,7 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {          }      @Test +    @DisableSceneContainer      fun notificationLaunchFalse_isAnimatingSurfaceFalse() =          testScope.runTest {              val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface) @@ -297,4 +416,44 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {              runCurrent()              assertThat(isAnimatingSurface).isFalse()          } + +    @Test +    @EnableSceneContainer +    fun notificationLaunchFromLockscreen_isAnimatingSurfaceTrue_scene_container() = +        testScope.runTest { +            val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface) + +            kosmos.setSceneTransition(Idle(Scenes.Lockscreen)) +            kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true) +            runCurrent() + +            assertThat(isAnimatingSurface).isTrue() +        } + +    @Test +    @EnableSceneContainer +    fun notificationLaunchFromGone_isAnimatingSurfaceFalse_scene_container() = +        testScope.runTest { +            val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface) + +            kosmos.setSceneTransition(Idle(Scenes.Gone)) +            kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true) +            runCurrent() +            assertThat(isAnimatingSurface).isFalse() + +            kosmos.setSceneTransition(Transition(from = Scenes.Gone, to = Scenes.Lockscreen)) +            assertThat(isAnimatingSurface).isFalse() +        } + +    @Test +    @EnableSceneContainer +    fun notificationLaunchFalse_isAnimatingSurfaceFalse_scene_container() = +        testScope.runTest { +            val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface) + +            kosmos.setSceneTransition(Idle(Scenes.Lockscreen)) +            kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(false) +            runCurrent() +            assertThat(isAnimatingSurface).isFalse() +        }  } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 246cfbfbea68..824132adc0d5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -30,7 +30,6 @@ import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepositor  import com.android.systemui.communal.domain.interactor.communalInteractor  import com.android.systemui.communal.domain.interactor.setCommunalAvailable  import com.android.systemui.communal.shared.model.CommunalScenes -import com.android.systemui.dock.fakeDockManager  import com.android.systemui.flags.BrokenWithSceneContainer  import com.android.systemui.flags.DisableSceneContainer  import com.android.systemui.flags.FakeFeatureFlags @@ -124,7 +123,6 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest      private val powerInteractor by lazy { kosmos.powerInteractor }      private val communalInteractor by lazy { kosmos.communalInteractor } -    private val dockManager by lazy { kosmos.fakeDockManager }      companion object {          @JvmStatic @@ -583,6 +581,7 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest          }      @Test +    @DisableSceneContainer      fun dozingToPrimaryBouncer() =          testScope.runTest {              // GIVEN a prior transition has run to DOZING @@ -597,8 +596,8 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest              assertThat(transitionRepository)                  .startedTransition( -                    to = KeyguardState.PRIMARY_BOUNCER,                      from = KeyguardState.DOZING, +                    to = KeyguardState.PRIMARY_BOUNCER,                      animatorAssertion = { it.isNotNull() }                  ) @@ -633,6 +632,7 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest          }      @Test +    @DisableSceneContainer      fun dozingToGlanceableHub() =          testScope.runTest {              // GIVEN a prior transition has run to DOZING @@ -654,8 +654,8 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest              assertThat(transitionRepository)                  .startedTransition( -                    to = KeyguardState.GLANCEABLE_HUB,                      from = KeyguardState.DOZING, +                    to = KeyguardState.GLANCEABLE_HUB,                      animatorAssertion = { it.isNotNull() }                  ) @@ -1452,6 +1452,7 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest          }      @Test +    @DisableSceneContainer      fun dreamingToPrimaryBouncer() =          testScope.runTest {              // GIVEN a prior transition has run to DREAMING diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt index f624f2029b36..b7fb759eb138 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt @@ -57,7 +57,9 @@ import org.junit.Test  import org.junit.runner.RunWith  import org.mockito.Mock  import org.mockito.Mockito.anyString +import org.mockito.Mockito.spy  import org.mockito.Mockito.verify +import org.mockito.Mockito.`when`  import org.mockito.MockitoSession  import org.mockito.quality.Strictness @@ -75,6 +77,9 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {      private lateinit var mockitoSession: MockitoSession +    private val spiedContext = spy(context) +    private val spiedResources = spy(spiedContext.resources) +      @Before      fun setUp() {          mockitoSession = @@ -100,6 +105,8 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {                  )              )              .thenReturn(listOf("com.google.test.notes")) + +        `when`(spiedContext.resources).thenReturn(spiedResources)      }      @After @@ -109,7 +116,7 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {      private fun createUnderTest(isEnabled: Boolean = true): KeyguardQuickAffordanceConfig =          NoteTaskQuickAffordanceConfig( -            context = context, +            context = spiedContext,              controller = controller,              stylusManager = stylusManager,              userManager = userManager, @@ -132,126 +139,262 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {          )      // region lockScreenState +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_stylusUsed_userUnlocked_isSelected_shouldEmitVisible() = runTest { -        val underTest = createUnderTest() -        TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(underTest) +    fun lockScreenState_stylusUnused_userLocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(false) +                .setUserUnlocked(false) +                .setLockScreenCustomizationEnabled(false) +                .setConfigSelections() -        val actual by collectLastValue(underTest.lockScreenState) +            val actual by collectLastValue(underTest.lockScreenState) -        assertThat(actual).isEqualTo(createLockScreenStateVisible()) -    } +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_stylusUsed_userUnlocked_isSelected_noDefaultNotesAppSet_shouldEmitHidden() = +    fun lockScreenState_stylusUnused_userLocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHidden() =          runTest {              val underTest = createUnderTest()              TestConfig() -                .setStylusEverUsed(true) +                .setStylusEverUsed(false) +                .setUserUnlocked(false) +                .setLockScreenCustomizationEnabled(false) +                .setConfigSelections(underTest) + +            val actual by collectLastValue(underTest.lockScreenState) + +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } + +    @Suppress("ktlint:standard:max-line-length") +    @Test +    fun lockScreenState_stylusUnused_userLocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(false) +                .setUserUnlocked(false) +                .setLockScreenCustomizationEnabled(true) +                .setConfigSelections() + +            val actual by collectLastValue(underTest.lockScreenState) + +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } + +    @Suppress("ktlint:standard:max-line-length") +    @Test +    fun lockScreenState_stylusUnused_userLocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(false) +                .setUserUnlocked(false) +                .setLockScreenCustomizationEnabled(true) +                .setConfigSelections(underTest) + +            val actual by collectLastValue(underTest.lockScreenState) + +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } + +    @Suppress("ktlint:standard:max-line-length") +    @Test +    fun lockScreenState_stylusUnused_userUnlocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(false)                  .setUserUnlocked(true) +                .setLockScreenCustomizationEnabled(false) +                .setConfigSelections() + +            val actual by collectLastValue(underTest.lockScreenState) + +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } + +    @Suppress("ktlint:standard:max-line-length") +    @Test +    fun lockScreenState_stylusUnused_userUnlocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(false) +                .setUserUnlocked(true) +                .setLockScreenCustomizationEnabled(false)                  .setConfigSelections(underTest) -            whenever( -                    roleManager.getRoleHoldersAsUser( -                        eq(RoleManager.ROLE_NOTES), -                        any(UserHandle::class.java) -                    ) -                ) -                .thenReturn(emptyList())              val actual by collectLastValue(underTest.lockScreenState)              assertThat(actual).isEqualTo(LockScreenState.Hidden)          } +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_stylusUnused_userUnlocked_isSelected_shouldEmitHidden() = runTest { -        val underTest = createUnderTest() -        TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections(underTest) +    fun lockScreenState_stylusUnused_userUnlocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(false) +                .setUserUnlocked(true) +                .setLockScreenCustomizationEnabled(true) +                .setConfigSelections() -        val actual by collectLastValue(underTest.lockScreenState) +            val actual by collectLastValue(underTest.lockScreenState) -        assertThat(actual).isEqualTo(LockScreenState.Hidden) -    } +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_stylusUsed_userLocked_isSelected_shouldEmitHidden() = runTest { -        val underTest = createUnderTest() -        TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections(underTest) +    fun lockScreenState_stylusUnused_userUnlocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitVisible() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(false) +                .setUserUnlocked(true) +                .setLockScreenCustomizationEnabled(true) +                .setConfigSelections(underTest) -        val actual by collectLastValue(underTest.lockScreenState) +            val actual by collectLastValue(underTest.lockScreenState) -        assertThat(actual).isEqualTo(LockScreenState.Hidden) -    } +            assertThat(actual).isEqualTo(createLockScreenStateVisible()) +        } +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_stylusUsed_userUnlocked_noSelected_shouldEmitHidden() = runTest { -        TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections() +    fun lockScreenState_stylusUsed_userLocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(true) +                .setUserUnlocked(false) +                .setLockScreenCustomizationEnabled(false) +                .setConfigSelections() -        val underTest = createUnderTest() -        val actual by collectLastValue(underTest.lockScreenState) +            val actual by collectLastValue(underTest.lockScreenState) -        assertThat(actual).isEqualTo(LockScreenState.Hidden) -    } +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_stylusUnused_userUnlocked_noSelected_shouldEmitHidden() = runTest { -        TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections() +    fun lockScreenState_stylusUsed_userLocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(true) +                .setUserUnlocked(false) +                .setLockScreenCustomizationEnabled(false) +                .setConfigSelections(underTest) -        val underTest = createUnderTest() -        val actual by collectLastValue(underTest.lockScreenState) +            val actual by collectLastValue(underTest.lockScreenState) -        assertThat(actual).isEqualTo(LockScreenState.Hidden) -    } +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_stylusUsed_userLocked_noSelected_shouldEmitHidden() = runTest { -        TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections() +    fun lockScreenState_stylusUsed_userLocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(true) +                .setUserUnlocked(false) +                .setLockScreenCustomizationEnabled(true) +                .setConfigSelections() -        val underTest = createUnderTest() -        val actual by collectLastValue(underTest.lockScreenState) +            val actual by collectLastValue(underTest.lockScreenState) -        assertThat(actual).isEqualTo(LockScreenState.Hidden) -    } +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_stylusUsed_userUnlocked_customSelections_shouldEmitHidden() = runTest { -        TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(mock()) +    fun lockScreenState_stylusUsed_userLocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(true) +                .setUserUnlocked(false) +                .setLockScreenCustomizationEnabled(true) +                .setConfigSelections(underTest) -        val underTest = createUnderTest() -        val actual by collectLastValue(underTest.lockScreenState) +            val actual by collectLastValue(underTest.lockScreenState) -        assertThat(actual).isEqualTo(LockScreenState.Hidden) -    } +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_stylusUnused_userUnlocked_customSelections_shouldEmitHidden() = runTest { -        TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections(mock()) +    fun lockScreenState_stylusUsed_userUnlocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitVisible() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(true) +                .setUserUnlocked(true) +                .setLockScreenCustomizationEnabled(false) +                .setConfigSelections() -        val underTest = createUnderTest() -        val actual by collectLastValue(underTest.lockScreenState) +            val actual by collectLastValue(underTest.lockScreenState) -        assertThat(actual).isEqualTo(LockScreenState.Hidden) -    } +            assertThat(actual).isEqualTo(createLockScreenStateVisible()) +        } +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_stylusUsed_userLocked_customSelections_shouldEmitHidden() = runTest { -        TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections(mock()) +    fun lockScreenState_stylusUsed_userUnlocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitVisible() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(true) +                .setUserUnlocked(true) +                .setLockScreenCustomizationEnabled(false) +                .setConfigSelections(underTest) -        val underTest = createUnderTest() -        val actual by collectLastValue(underTest.lockScreenState) +            val actual by collectLastValue(underTest.lockScreenState) -        assertThat(actual).isEqualTo(LockScreenState.Hidden) -    } +            assertThat(actual).isEqualTo(createLockScreenStateVisible()) +        } +    @Suppress("ktlint:standard:max-line-length")      @Test -    fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest { -        TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections() +    fun lockScreenState_stylusUsed_userUnlocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(true) +                .setUserUnlocked(true) +                .setLockScreenCustomizationEnabled(true) +                .setConfigSelections() -        val underTest = createUnderTest(isEnabled = false) -        val actual by collectLastValue(underTest.lockScreenState) +            val actual by collectLastValue(underTest.lockScreenState) + +            assertThat(actual).isEqualTo(LockScreenState.Hidden) +        } + +    @Suppress("ktlint:standard:max-line-length") +    @Test +    fun lockScreenState_stylusUsed_userUnlocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitVisible() = +        runTest { +            val underTest = createUnderTest() +            TestConfig() +                .setStylusEverUsed(true) +                .setUserUnlocked(true) +                .setLockScreenCustomizationEnabled(true) +                .setConfigSelections(underTest) + +            val actual by collectLastValue(underTest.lockScreenState) + +            assertThat(actual).isEqualTo(createLockScreenStateVisible()) +        } -        assertThat(actual).isEqualTo(LockScreenState.Hidden) -    }      // endregion      @Test @@ -294,18 +437,24 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {              .isEqualTo(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE)          assertThat(disabled.actionIntent?.`package`).isEqualTo(context.packageName)      } +      // endregion      private inner class TestConfig {          fun setStylusEverUsed(value: Boolean) = also { -            whenever(InputSettings.isStylusEverUsed(mContext)).thenReturn(value) +            whenever(InputSettings.isStylusEverUsed(spiedContext)).thenReturn(value)          }          fun setUserUnlocked(value: Boolean) = also {              whenever(userManager.isUserUnlocked).thenReturn(value)          } +        fun setLockScreenCustomizationEnabled(value: Boolean) = also { +            `when`(spiedResources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)) +                .thenReturn(value) +        } +          fun setConfigSelections(vararg values: KeyguardQuickAffordanceConfig) = also {              val slotKey = "bottom-right"              val configSnapshots = values.toList() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java index 5c45b2e53047..3669e3d8fed1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java @@ -64,6 +64,10 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {      @Mock private SectionHeaderController mPeopleHeaderController;      @Mock private SectionHeaderController mAlertingHeaderController;      @Mock private SectionHeaderController mSilentHeaderController; +    @Mock private SectionHeaderController mNewsHeaderController; +    @Mock private SectionHeaderController mSocialHeaderController; +    @Mock private SectionHeaderController mRecsHeaderController; +    @Mock private SectionHeaderController mPromoHeaderController;      private NotificationSectionsManager mSectionsManager; @@ -94,7 +98,11 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {                          mIncomingHeaderController,                          mPeopleHeaderController,                          mAlertingHeaderController, -                        mSilentHeaderController +                        mSilentHeaderController, +                        mNewsHeaderController, +                        mSocialHeaderController, +                        mRecsHeaderController, +                        mPromoHeaderController                  );          // Required in order for the header inflation to work properly          when(mNssl.generateLayoutParams(any(AttributeSet.class))) diff --git a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt index 36ac4a469566..c4f93d107dbb 100644 --- a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt +++ b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt @@ -18,6 +18,7 @@ package android.hardware.input  import android.view.InputDevice  import android.view.KeyCharacterMap +import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD  import com.android.systemui.util.mockito.mock  import com.android.systemui.util.mockito.whenever  import org.mockito.ArgumentMatchers.anyInt @@ -25,7 +26,18 @@ import org.mockito.invocation.InvocationOnMock  class FakeInputManager { -    private val devices = mutableMapOf<Int, InputDevice>() +    private val keyCharacterMap = KeyCharacterMap.load(VIRTUAL_KEYBOARD) + +    private val virtualKeyboard = +        InputDevice.Builder() +            .setId(VIRTUAL_KEYBOARD) +            .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC) +            .setSources(InputDevice.SOURCE_KEYBOARD) +            .setEnabled(true) +            .setKeyCharacterMap(keyCharacterMap) +            .build() + +    private val devices = mutableMapOf<Int, InputDevice>(VIRTUAL_KEYBOARD to virtualKeyboard)      val inputManager =          mock<InputManager> { @@ -56,10 +68,6 @@ class FakeInputManager {          addKeyboard(id, enabled)      } -    fun addVirtualKeyboard(enabled: Boolean = true) { -        addKeyboard(id = KeyCharacterMap.VIRTUAL_KEYBOARD, enabled) -    } -      private fun addKeyboard(id: Int, enabled: Boolean = true) {          devices[id] =              InputDevice.Builder() @@ -67,6 +75,7 @@ class FakeInputManager {                  .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)                  .setSources(InputDevice.SOURCE_KEYBOARD)                  .setEnabled(enabled) +                .setKeyCharacterMap(keyCharacterMap)                  .build()      } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt index 9dae44dc8053..7c53639a85a6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt @@ -18,6 +18,7 @@ package com.android.systemui  import android.app.ActivityManager  import android.app.admin.DevicePolicyManager  import android.app.trust.TrustManager +import android.hardware.fingerprint.FingerprintManager  import android.os.UserManager  import android.service.notification.NotificationListenerService  import android.util.DisplayMetrics @@ -94,6 +95,7 @@ data class TestMocksModule(      @get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(),      @get:Provides val dozeParameters: DozeParameters = mock(),      @get:Provides val dumpManager: DumpManager = mock(), +    @get:Provides val fingerprintManager: FingerprintManager = mock(),      @get:Provides val headsUpManager: HeadsUpManager = mock(),      @get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(),      @get:Provides val keyguardBypassController: KeyguardBypassController = mock(), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt index cbfc7686a896..ae592b968f8b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt @@ -17,17 +17,20 @@  package com.android.systemui.biometrics.domain.interactor  import android.content.applicationContext +import android.hardware.fingerprint.FingerprintManager  import com.android.systemui.biometrics.authController  import com.android.systemui.kosmos.Kosmos  import com.android.systemui.kosmos.Kosmos.Fixture  import com.android.systemui.kosmos.applicationCoroutineScope  import com.android.systemui.user.domain.interactor.selectedUserInteractor +import com.android.systemui.util.mockito.mock  val Kosmos.udfpsOverlayInteractor by Fixture {      UdfpsOverlayInteractor(          context = applicationContext,          authController = authController,          selectedUserInteractor = selectedUserInteractor, +        fingerprintManager = mock<FingerprintManager>(),          scope = applicationCoroutineScope,      )  } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt index 385a6dc5a819..dab70f86be09 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt @@ -59,10 +59,13 @@ val Kosmos.shortcutHelperStateRepository by  val Kosmos.shortcutHelperCategoriesRepository by      Kosmos.Fixture {          ShortcutHelperCategoriesRepository( +            applicationContext, +            testDispatcher,              shortcutHelperSystemShortcutsSource,              shortcutHelperMultiTaskingShortcutsSource,              windowManager, -            shortcutHelperStateRepository +            fakeInputManager.inputManager, +            shortcutHelperStateRepository,          )      } diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java index 63bcda188ee6..4992c4bcc77a 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java @@ -447,14 +447,6 @@ public class RavenwoodBaseContext extends Context {      }      @Override -    public void sendOrderedBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, -            String[] receiverPermissions, int appOp, Bundle options, -            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, -            String initialData, Bundle initialExtras) { -        throw notSupported(); -    } - -    @Override      public void sendStickyBroadcast(Intent intent) {          throw notSupported();      } diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index c4d38e46da3c..a2bbff026b95 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -48,3 +48,6 @@ per-file VcnManagementService.java = file:/services/core/java/com/android/server  # SystemConfig  per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS + +# CertBlocklister +per-file Cert*.java = tweek@google.com, brambonne@google.com, prb@google.com, miguelaranda@google.com diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index a4026eb151b9..c2cb5e90ca58 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -803,22 +803,28 @@ public class RescueParty {          @Override          public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,                  @FailureReasons int failureReason, int mitigationCount) { +            int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;              if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH                      || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {                  if (Flags.recoverabilityDetection()) {                      if (!Flags.deprecateFlagsAndSettingsResets()) { -                        return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, +                        impact =  mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,                                  mayPerformReboot(failedPackage), failedPackage));                      } else { -                        return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); +                        impact =  mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));                      }                  } else { -                    return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, +                    impact =  mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,                              mayPerformReboot(failedPackage)));                  } -            } else { -                return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;              } + +            Slog.i(TAG, "Checking available remediations for health check failure." +                    + " failedPackage: " +                    + (failedPackage == null ? null : failedPackage.getPackageName()) +                    + " failureReason: " + failureReason +                    + " available impact: " + impact); +            return impact;          }          @Override @@ -827,6 +833,11 @@ public class RescueParty {              if (isDisabled()) {                  return false;              } +            Slog.i(TAG, "Executing remediation." +                    + " failedPackage: " +                    + (failedPackage == null ? null : failedPackage.getPackageName()) +                    + " failureReason: " + failureReason +                    + " mitigationCount: " + mitigationCount);              if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH                      || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {                  final int level; diff --git a/services/core/java/com/android/server/am/LmkdConnection.java b/services/core/java/com/android/server/am/LmkdConnection.java index 598f086db94d..4faadcbe0cca 100644 --- a/services/core/java/com/android/server/am/LmkdConnection.java +++ b/services/core/java/com/android/server/am/LmkdConnection.java @@ -91,10 +91,18 @@ public class LmkdConnection {      @GuardedBy("mLmkdSocketLock")      private LocalSocket mLmkdSocket = null; -    // socket I/O streams -    @GuardedBy("mLmkdSocketLock") +    // mutex to synchronize socket output stream with socket creation/destruction +    private final Object mLmkdOutputStreamLock = new Object(); + +    // socket output stream +    @GuardedBy("mLmkdOutputStreamLock")      private OutputStream mLmkdOutputStream = null; -    @GuardedBy("mLmkdSocketLock") + +    // mutex to synchronize socket input stream with socket creation/destruction +    private final Object mLmkdInputStreamLock = new Object(); + +    // socket input stream +    @GuardedBy("mLmkdInputStreamLock")      private InputStream mLmkdInputStream = null;      // buffer to store incoming data @@ -148,9 +156,13 @@ public class LmkdConnection {                  return false;              }              // connection established -            mLmkdSocket = socket; -            mLmkdOutputStream = ostream; -            mLmkdInputStream = istream; +            synchronized(mLmkdOutputStreamLock) { +                synchronized(mLmkdInputStreamLock) { +                    mLmkdSocket = socket; +                    mLmkdOutputStream = ostream; +                    mLmkdInputStream = istream; +                } +            }              mMsgQueue.addOnFileDescriptorEventListener(mLmkdSocket.getFileDescriptor(),                  EVENT_INPUT | EVENT_ERROR,                  new MessageQueue.OnFileDescriptorEventListener() { @@ -177,7 +189,13 @@ public class LmkdConnection {                  mMsgQueue.removeOnFileDescriptorEventListener(                          mLmkdSocket.getFileDescriptor());                  IoUtils.closeQuietly(mLmkdSocket); -                mLmkdSocket = null; +                synchronized(mLmkdOutputStreamLock) { +                    synchronized(mLmkdInputStreamLock) { +                        mLmkdOutputStream = null; +                        mLmkdInputStream = null; +                        mLmkdSocket = null; +                    } +                }              }              // wake up reply waiters if any              synchronized (mReplyBufLock) { @@ -262,24 +280,33 @@ public class LmkdConnection {      }      private boolean write(ByteBuffer buf) { -        synchronized (mLmkdSocketLock) { -            try { -                mLmkdOutputStream.write(buf.array(), 0, buf.position()); -            } catch (IOException ex) { -                return false; +        boolean result = false; + +        synchronized(mLmkdOutputStreamLock) { +            if (mLmkdOutputStream != null) { +                try { +                    mLmkdOutputStream.write(buf.array(), 0, buf.position()); +                    result = true; +                } catch (IOException ex) { +                }              } -            return true;          } + +        return result;      }      private int read(ByteBuffer buf) { -        synchronized (mLmkdSocketLock) { -            try { -                return mLmkdInputStream.read(buf.array(), 0, buf.array().length); -            } catch (IOException ex) { +        int result = -1; + +        synchronized(mLmkdInputStreamLock) { +            if (mLmkdInputStream != null) { +                try { +                    result = mLmkdInputStream.read(buf.array(), 0, buf.array().length); +                } catch (IOException ex) { +                }              } -            return -1;          } +        return result;      }      /** diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index d061e2d21811..fbd32a67fe6c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -31,7 +31,6 @@ import android.util.Slog;  import com.android.server.biometrics.log.BiometricContext;  import com.android.server.biometrics.log.BiometricLogger; -import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;  import java.util.function.Supplier; @@ -203,16 +202,6 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement          }      } -    // TODO(b/317414324): Deprecate setIgnoreDisplayTouches -    protected final void resetIgnoreDisplayTouches() { -        final AidlSession session = (AidlSession) getFreshDaemon(); -        try { -            session.getSession().setIgnoreDisplayTouches(false); -        } catch (RemoteException e) { -            Slog.e(TAG, "Remote exception when resetting setIgnoreDisplayTouches"); -        } -    } -      @Override      public boolean isInterruptable() {          return true; 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 4c86f57a190b..60cfd5a5a6ae 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 @@ -962,6 +962,19 @@ public class FingerprintService extends SystemService {              provider.onUdfpsUiEvent(event, requestId, sensorId);          } +        @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) +        @Override +        public void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches) { +            super.setIgnoreDisplayTouches_enforcePermission(); + +            final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); +            if (provider == null) { +                Slog.w(TAG, +                        "No matching provider for setIgnoreDisplayTouches, sensorId: " + sensorId); +                return; +            } +            provider.setIgnoreDisplayTouches(requestId, sensorId, ignoreTouches); +        }          @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)          @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index a6cf2f4b31ae..e4a99e6be72c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -134,6 +134,8 @@ public interface ServiceProvider extends      void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller); +    void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches); +      void onPowerPressed();      @NonNull diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java index dce0175ca593..15d7a476b345 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java @@ -31,4 +31,5 @@ public interface Udfps {      void onPointerUp(PointerContext pc);      void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event);      boolean isPointerDown(); +    void setIgnoreDisplayTouches(boolean ignoreTouches);  } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 72d92b974c1a..d04afdb72913 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -33,7 +33,6 @@ import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcqu  import android.hardware.biometrics.BiometricManager.Authenticators;  import android.hardware.biometrics.BiometricSourceType;  import android.hardware.biometrics.common.ICancellationSignal; -import android.hardware.biometrics.common.OperationState;  import android.hardware.biometrics.events.AuthenticationAcquiredInfo;  import android.hardware.biometrics.events.AuthenticationErrorInfo;  import android.hardware.biometrics.events.AuthenticationFailedInfo; @@ -182,7 +181,6 @@ public class FingerprintAuthenticationClient          handleLockout(authenticated);          if (authenticated) {              mState = STATE_STOPPED; -            resetIgnoreDisplayTouches();              mSensorOverlays.hide(getSensorId());              if (reportBiometricAuthAttempts()) {                  mAuthenticationStateListeners.onAuthenticationSucceeded( @@ -223,7 +221,6 @@ public class FingerprintAuthenticationClient                  // Send the error, but do not invoke the FinishCallback yet. Since lockout is not                  // controlled by the HAL, the framework must stop the sensor before finishing the                  // client. -                resetIgnoreDisplayTouches();                  mSensorOverlays.hide(getSensorId());                  mAuthenticationStateListeners.onAuthenticationError(                          new AuthenticationErrorInfo.Builder(BiometricSourceType.FINGERPRINT, @@ -275,7 +272,6 @@ public class FingerprintAuthenticationClient              BiometricNotificationUtils.showBadCalibrationNotification(getContext());          } -        resetIgnoreDisplayTouches();          mSensorOverlays.hide(getSensorId());          mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo                  .Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build() @@ -284,7 +280,6 @@ public class FingerprintAuthenticationClient      @Override      protected void startHalOperation() { -        resetIgnoreDisplayTouches();          mSensorOverlays.show(getSensorId(), getRequestReason(), this);          mAuthenticationStateListeners.onAuthenticationStarted(new AuthenticationStartedInfo                  .Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build() @@ -331,12 +326,6 @@ public class FingerprintAuthenticationClient              if (session.hasContextMethods()) {                  try {                      session.getSession().onContextChanged(ctx); -                    // TODO(b/317414324): Deprecate setIgnoreDisplayTouches -                    if (ctx.operationState != null && ctx.operationState.getTag() -                            == OperationState.fingerprintOperationState) { -                        session.getSession().setIgnoreDisplayTouches(ctx.operationState -                                .getFingerprintOperationState().isHardwareIgnoringTouches); -                    }                  } catch (RemoteException e) {                      Slog.e(TAG, "Unable to notify context changed", e);                  } @@ -353,7 +342,6 @@ public class FingerprintAuthenticationClient      @Override      protected void stopHalOperation() { -        resetIgnoreDisplayTouches();          mSensorOverlays.hide(getSensorId());          mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo                  .Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build() @@ -415,6 +403,15 @@ public class FingerprintAuthenticationClient      }      @Override +    public void setIgnoreDisplayTouches(boolean ignoreTouches) { +        try { +            getFreshDaemon().getSession().setIgnoreDisplayTouches(ignoreTouches); +        } catch (RemoteException e) { +            Slog.e(TAG, "Remote exception", e); +        } +    } + +    @Override      public boolean isPointerDown() {          return mIsPointerDown;      } @@ -457,7 +454,6 @@ public class FingerprintAuthenticationClient              Slog.e(TAG, "Remote exception", e);          } -        resetIgnoreDisplayTouches();          mSensorOverlays.hide(getSensorId());          mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo                  .Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build() @@ -492,7 +488,6 @@ public class FingerprintAuthenticationClient              Slog.e(TAG, "Remote exception", e);          } -        resetIgnoreDisplayTouches();          mSensorOverlays.hide(getSensorId());          mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo                  .Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build() diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index 36af5dba909f..fb48053913cf 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -87,7 +87,6 @@ public class FingerprintDetectClient extends AcquisitionClient<AidlSession>      @Override      protected void stopHalOperation() { -        resetIgnoreDisplayTouches();          mSensorOverlays.hide(getSensorId());          mAuthenticationStateListeners.onAuthenticationStopped(                  new AuthenticationStoppedInfo.Builder(BiometricSourceType.FINGERPRINT, @@ -107,7 +106,6 @@ public class FingerprintDetectClient extends AcquisitionClient<AidlSession>      @Override      protected void startHalOperation() { -        resetIgnoreDisplayTouches();          mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,                  this);          mAuthenticationStateListeners.onAuthenticationStarted( diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index 3a72d7e9a91a..993a68fd6ff8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -150,7 +150,6 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement                  controller -> controller.onEnrollmentProgress(getSensorId(), remaining));          if (remaining == 0) { -            resetIgnoreDisplayTouches();              mSensorOverlays.hide(getSensorId());              mAuthenticationStateListeners.onAuthenticationStopped(                      new AuthenticationStoppedInfo.Builder( @@ -211,7 +210,6 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement          );          super.onError(errorCode, vendorCode); -        resetIgnoreDisplayTouches();          mSensorOverlays.hide(getSensorId());          mAuthenticationStateListeners.onAuthenticationStopped(                  new AuthenticationStoppedInfo.Builder(BiometricSourceType.FINGERPRINT, @@ -227,7 +225,6 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement      @Override      protected void startHalOperation() { -        resetIgnoreDisplayTouches();          mSensorOverlays.show(getSensorId(),                  getRequestReasonFromFingerprintEnrollReason(mEnrollReason), this);          mAuthenticationStateListeners.onAuthenticationStarted(new AuthenticationStartedInfo @@ -277,7 +274,6 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement      @Override      protected void stopHalOperation() { -        resetIgnoreDisplayTouches();          mSensorOverlays.hide(getSensorId());          mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo                  .Builder(BiometricSourceType.FINGERPRINT, @@ -359,5 +355,14 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement      }      @Override +    public void setIgnoreDisplayTouches(boolean ignoreTouches) { +        try { +            getFreshDaemon().getSession().setIgnoreDisplayTouches(ignoreTouches); +        } catch (RemoteException e) { +            Slog.e(TAG, "Unable to send setIgnoreDisplayTouches", e); +        } +    } + +    @Override      public void onPowerPressed() {}  } 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 1bddb83b1441..12baf00c1c4a 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 @@ -790,6 +790,19 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi      }      @Override +    public void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches) { +        mFingerprintSensors.get(sensorId).getScheduler().getCurrentClientIfMatches( +                requestId, (client) -> { +                    if (!(client instanceof Udfps)) { +                        Slog.e(getTag(), +                                "setIgnoreDisplayTouches received during client: " + client); +                        return; +                    } +                    ((Udfps) client).setIgnoreDisplayTouches(ignoreTouches); +                }); +    } + +    @Override      public void onPowerPressed() {          for (int i = 0; i < mFingerprintSensors.size(); i++) {              final Sensor sensor = mFingerprintSensors.valueAt(i); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 7cd9144be77b..6694ebbe461a 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1485,6 +1485,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call                      || !mAutomaticBrightnessStrategy.shouldUseAutoBrightness())) {                  rawBrightnessState = getDozeBrightnessForOffload();                  brightnessState = clampScreenBrightness(rawBrightnessState); +                updateScreenBrightnessSetting = false;                  mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_MANUAL);                  mTempBrightnessEvent.setFlags(                          mTempBrightnessEvent.getFlags() | BrightnessEvent.FLAG_DOZE_SCALE); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 0f9c3440923b..49888db98755 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -597,6 +597,12 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {      @Constants.HandleMessageResult      protected int handleReportPhysicalAddress(HdmiCecMessage message) {          super.handleReportPhysicalAddress(message); +        // Ignore <Report Physical Address> while DeviceDiscoveryAction is in progress to avoid +        // starting a NewDeviceAction which might interfere in creating the list of known devices. +        if (hasAction(DeviceDiscoveryAction.class)) { +            return Constants.HANDLED; +        } +          int path = HdmiUtils.twoBytesToInt(message.getParams());          int address = message.getSource();          int type = message.getParams()[2]; diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index bef984bdcbf7..1f46af8b741d 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3364,6 +3364,10 @@ public class InputManagerService extends IInputManager.Stub          mPointerIconCache.setPointerFillStyle(fillStyle);      } +    void setPointerStrokeStyle(@PointerIcon.PointerIconVectorStyleStroke int strokeStyle) { +        mPointerIconCache.setPointerStrokeStyle(strokeStyle); +    } +      void setPointerScale(float scale) {          mPointerIconCache.setPointerScale(scale);      } diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java index 593b0917efc7..000f3122f05a 100644 --- a/services/core/java/com/android/server/input/InputSettingsObserver.java +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -18,6 +18,7 @@ package com.android.server.input;  import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;  import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK; +import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE;  import static android.view.flags.Flags.enableVectorCursorA11ySettings;  import static com.android.input.flags.Flags.rateLimitUserActivityPokeInDispatcher; @@ -103,6 +104,8 @@ class InputSettingsObserver extends ContentObserver {                          (reason) -> updateStylusPointerIconEnabled()),                  Map.entry(Settings.System.getUriFor(Settings.System.POINTER_FILL_STYLE),                          (reason) -> updatePointerFillStyleFromSettings()), +                Map.entry(Settings.System.getUriFor(Settings.System.POINTER_STROKE_STYLE), +                        (reason) -> updatePointerStrokeStyleFromSettings()),                  Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SCALE),                          (reason) -> updatePointerScaleFromSettings()));      } @@ -281,6 +284,17 @@ class InputSettingsObserver extends ContentObserver {          mService.setPointerFillStyle(pointerFillStyle);      } +    private void updatePointerStrokeStyleFromSettings() { +        if (!enableVectorCursorA11ySettings()) { +            return; +        } +        final int pointerStrokeStyle = Settings.System.getIntForUser( +                mContext.getContentResolver(), Settings.System.POINTER_STROKE_STYLE, +                POINTER_ICON_VECTOR_STYLE_STROKE_WHITE, +                UserHandle.USER_CURRENT); +        mService.setPointerStrokeStyle(pointerStrokeStyle); +    } +      private void updatePointerScaleFromSettings() {          if (!enableVectorCursorA11ySettings()) {              return; diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java index 44622d80da0e..297cd68d5d3d 100644 --- a/services/core/java/com/android/server/input/PointerIconCache.java +++ b/services/core/java/com/android/server/input/PointerIconCache.java @@ -18,6 +18,7 @@ package com.android.server.input;  import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;  import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK; +import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE;  import android.annotation.NonNull;  import android.content.Context; @@ -65,6 +66,9 @@ final class PointerIconCache {      private @PointerIcon.PointerIconVectorStyleFill int mPointerIconFillStyle =              POINTER_ICON_VECTOR_STYLE_FILL_BLACK;      @GuardedBy("mLoadedPointerIconsByDisplayAndType") +    private @PointerIcon.PointerIconVectorStyleStroke int mPointerIconStrokeStyle = +            POINTER_ICON_VECTOR_STYLE_STROKE_WHITE; +    @GuardedBy("mLoadedPointerIconsByDisplayAndType")      private float mPointerIconScale = DEFAULT_POINTER_SCALE;      private final DisplayManager.DisplayListener mDisplayListener = @@ -120,6 +124,11 @@ final class PointerIconCache {          mUiThreadHandler.post(() -> handleSetPointerFillStyle(fillStyle));      } +    /** Set the stroke style for vector pointer icons. */ +    public void setPointerStrokeStyle(@PointerIcon.PointerIconVectorStyleStroke int strokeStyle) { +        mUiThreadHandler.post(() -> handleSetPointerStrokeStyle(strokeStyle)); +    } +      /** Set the scale for vector pointer icons. */      public void setPointerScale(float scale) {          mUiThreadHandler.post(() -> handleSetPointerScale(scale)); @@ -144,6 +153,8 @@ final class PointerIconCache {                  theme.setTo(context.getTheme());                  theme.applyStyle(PointerIcon.vectorFillStyleToResource(mPointerIconFillStyle),                          /* force= */ true); +                theme.applyStyle(PointerIcon.vectorStrokeStyleToResource(mPointerIconStrokeStyle), +                        /* force= */ true);                  icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme),                          type, mUseLargePointerIcons, mPointerIconScale);                  iconsByType.put(type, icon); @@ -224,6 +235,20 @@ final class PointerIconCache {      }      @android.annotation.UiThread +    private void handleSetPointerStrokeStyle( +            @PointerIcon.PointerIconVectorStyleStroke int strokeStyle) { +        synchronized (mLoadedPointerIconsByDisplayAndType) { +            if (mPointerIconStrokeStyle == strokeStyle) { +                return; +            } +            mPointerIconStrokeStyle = strokeStyle; +            // Clear all cached icons on all displays. +            mLoadedPointerIconsByDisplayAndType.clear(); +        } +        mNative.reloadPointerIcons(); +    } + +    @android.annotation.UiThread      private void handleSetPointerScale(float scale) {          synchronized (mLoadedPointerIconsByDisplayAndType) {              if (mPointerIconScale == scale) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 1dfdc553641e..fbb6ccf63748 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -195,6 +195,7 @@ import java.lang.annotation.Target;  import java.security.InvalidParameterException;  import java.util.ArrayList;  import java.util.Arrays; +import java.util.Collection;  import java.util.Collections;  import java.util.List;  import java.util.Objects; @@ -298,22 +299,21 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.      private final String[] mNonPreemptibleInputMethods;      /** -     * See {@link #shouldEnableExperimentalConcurrentMultiUserMode(Context)} about when set to be -     * {@code true}. +     * See {@link #shouldEnableConcurrentMultiUserMode(Context)} about when set to be {@code true}.       */      @SharedByAllUsersField -    private final boolean mExperimentalConcurrentMultiUserModeEnabled; +    private final boolean mConcurrentMultiUserModeEnabled;      /** -     * Returns {@code true} if experimental concurrent multi-user mode is enabled. +     * Returns {@code true} if the concurrent multi-user mode is enabled.       *       * <p>Currently not compatible with profiles (e.g. work profile).</p>       *       * @param context {@link Context} to be used to query       *                {@link PackageManager#FEATURE_AUTOMOTIVE} -     * @return {@code true} if experimental concurrent multi-user mode is enabled. +     * @return {@code true} if the concurrent multi-user mode is enabled.       */ -    static boolean shouldEnableExperimentalConcurrentMultiUserMode(@NonNull Context context) { +    static boolean shouldEnableConcurrentMultiUserMode(@NonNull Context context) {          return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)                  && UserManager.isVisibleBackgroundUsersEnabled()                  && context.getResources().getBoolean(android.R.bool.config_perDisplayFocusEnabled) @@ -330,7 +330,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.      @UserIdInt      @BinderThread      private int resolveImeUserIdLocked(@UserIdInt int callingProcessUserId) { -        return mExperimentalConcurrentMultiUserModeEnabled ? callingProcessUserId : mCurrentUserId; +        return mConcurrentMultiUserModeEnabled ? callingProcessUserId : mCurrentUserId;      }      final Context mContext; @@ -571,10 +571,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.      private final ImeTrackerService mImeTrackerService;      class SettingsObserver extends ContentObserver { -        int mUserId; -        boolean mRegistered = false; -        @NonNull -        String mLastEnabled = "";          /**           * <em>This constructor must be called within the lock.</em> @@ -583,37 +579,29 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.              super(handler);          } -        @GuardedBy("ImfLock.class") -        public void registerContentObserverLocked(@UserIdInt int userId) { -            if (mRegistered && mUserId == userId) { -                return; -            } +        void registerContentObserverForAllUsers() {              ContentResolver resolver = mContext.getContentResolver(); -            if (mRegistered) { -                mContext.getContentResolver().unregisterContentObserver(this); -                mRegistered = false; -            } -            if (mUserId != userId) { -                mLastEnabled = ""; -                mUserId = userId; -            } -            resolver.registerContentObserver(Settings.Secure.getUriFor( -                    Settings.Secure.DEFAULT_INPUT_METHOD), false, this, userId); -            resolver.registerContentObserver(Settings.Secure.getUriFor( -                    Settings.Secure.ENABLED_INPUT_METHODS), false, this, userId); -            resolver.registerContentObserver(Settings.Secure.getUriFor( -                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, userId); -            resolver.registerContentObserver(Settings.Secure.getUriFor( -                    Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId); -            resolver.registerContentObserver(Settings.Secure.getUriFor( -                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, userId); -            resolver.registerContentObserver(Settings.Secure.getUriFor( -                    STYLUS_HANDWRITING_ENABLED), false, this); -            mRegistered = true; +            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor( +                    Settings.Secure.DEFAULT_INPUT_METHOD), false, this, UserHandle.ALL); +            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor( +                    Settings.Secure.ENABLED_INPUT_METHODS), false, this, UserHandle.ALL); +            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor( +                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, UserHandle.ALL); +            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor( +                    Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, UserHandle.ALL); +            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor( +                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, UserHandle.ALL); +            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor( +                    STYLUS_HANDWRITING_ENABLED), false, this, UserHandle.ALL);          }          @Override -        public void onChange(boolean selfChange, Uri uri) { +        public void onChange(boolean selfChange, @NonNull Collection<Uri> uris, int flags, +                @UserIdInt int userId) { +            uris.forEach(uri -> onChangeInternal(uri, userId)); +        } + +        private void onChangeInternal(@NonNull Uri uri, @UserIdInt int userId) {              final Uri showImeUri = Settings.Secure.getUriFor(                      Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);              final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor( @@ -621,23 +609,27 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.              final Uri stylusHandwritingEnabledUri = Settings.Secure.getUriFor(                      STYLUS_HANDWRITING_ENABLED);              synchronized (ImfLock.class) { +                if (!mConcurrentMultiUserModeEnabled && mCurrentUserId != userId) { +                    return; +                } +                  if (showImeUri.equals(uri)) {                      mMenuController.updateKeyboardFromSettingsLocked();                  } else if (accessibilityRequestingNoImeUri.equals(uri)) {                      final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(                              mContext.getContentResolver(), -                            Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, mUserId); +                            Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, userId);                      mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard(                              accessibilitySoftKeyboardSetting); -                    final var userData = getUserData(mUserId); +                    final var userData = getUserData(userId);                      if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) {                          hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,                                  0 /* flags */, SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE, -                                mUserId); -                    } else if (isShowRequestedForCurrentWindow(mUserId)) { +                                userId); +                    } else if (isShowRequestedForCurrentWindow(userId)) {                          showCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,                                  InputMethodManager.SHOW_IMPLICIT, -                                SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, mUserId); +                                SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, userId);                      }                  } else if (stylusHandwritingEnabledUri.equals(uri)) {                      InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); @@ -645,22 +637,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.                              .invalidateLocalConnectionlessStylusHandwritingAvailabilityCaches();                  } else {                      boolean enabledChanged = false; -                    String newEnabled = InputMethodSettingsRepository.get(mUserId) +                    String newEnabled = InputMethodSettingsRepository.get(userId)                              .getEnabledInputMethodsStr(); -                    if (!mLastEnabled.equals(newEnabled)) { -                        mLastEnabled = newEnabled; +                    final var userData = getUserData(userId); +                    if (!userData.mLastEnabledInputMethodsStr.equals(newEnabled)) { +                        userData.mLastEnabledInputMethodsStr = newEnabled;                          enabledChanged = true;                      } -                    updateInputMethodsFromSettingsLocked(enabledChanged, mUserId); +                    updateInputMethodsFromSettingsLocked(enabledChanged, userId);                  }              }          } - -        @Override -        public String toString() { -            return "SettingsObserver{mUserId=" + mUserId + " mRegistered=" + mRegistered -                    + " mLastEnabled=" + mLastEnabled + "}"; -        }      }      /** @@ -973,7 +960,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.          public Lifecycle(Context context) {              this(context, new InputMethodManagerService(context, -                            shouldEnableExperimentalConcurrentMultiUserMode(context))); +                            shouldEnableConcurrentMultiUserMode(context)));          }          public Lifecycle( @@ -1003,7 +990,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.          public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {              // Called on ActivityManager thread.              synchronized (ImfLock.class) { -                if (mService.mExperimentalConcurrentMultiUserModeEnabled) { +                if (mService.mConcurrentMultiUserModeEnabled) {                      // In concurrent multi-user mode, we in general do not rely on the concept of                      // current user.                      return; @@ -1037,9 +1024,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.              SecureSettingsWrapper.onUserStarting(userId);              synchronized (ImfLock.class) {                  mService.getUserData(userId); -                if (mService.mExperimentalConcurrentMultiUserModeEnabled) { +                if (mService.mConcurrentMultiUserModeEnabled) {                      if (mService.mCurrentUserId != userId && mService.mSystemReady) { -                        mService.experimentalInitializeVisibleBackgroundUserLocked(userId); +                        mService.initializeVisibleBackgroundUserLocked(userId);                      }                  }              } @@ -1062,8 +1049,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.                  // We need to rebuild IMEs.                  postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */, userId);                  updateInputMethodsFromSettingsLocked(true /* enabledChanged */, userId); -            } else if (mExperimentalConcurrentMultiUserModeEnabled) { -                experimentalInitializeVisibleBackgroundUserLocked(userId); +            } else if (mConcurrentMultiUserModeEnabled) { +                initializeVisibleBackgroundUserLocked(userId);              }          }      } @@ -1090,20 +1077,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.      }      public InputMethodManagerService(Context context, -            boolean experimentalConcurrentMultiUserModeEnabled) { -        this(context, experimentalConcurrentMultiUserModeEnabled, null, null, null); +            boolean concurrentMultiUserModeEnabled) { +        this(context, concurrentMultiUserModeEnabled, null, null, null);      }      @VisibleForTesting      InputMethodManagerService(              Context context, -            boolean experimentalConcurrentMultiUserModeEnabled, +            boolean concurrentMultiUserModeEnabled,              @Nullable ServiceThread serviceThreadForTesting,              @Nullable ServiceThread ioThreadForTesting,              @Nullable IntFunction<InputMethodBindingController> bindingControllerForTesting) {          synchronized (ImfLock.class) { -            mExperimentalConcurrentMultiUserModeEnabled = -                    experimentalConcurrentMultiUserModeEnabled; +            mConcurrentMultiUserModeEnabled = concurrentMultiUserModeEnabled;              mContext = context;              mRes = context.getResources();              SecureSettingsWrapper.onStart(mContext); @@ -1307,11 +1293,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.          maybeInitImeNavbarConfigLocked(newUserId); -        // ContentObserver should be registered again when the user is changed -        mSettingsObserver.registerContentObserverLocked(newUserId); +        final var newUserData = getUserData(newUserId); + +        // TODO(b/342027196): Double check if we need to always reset upon user switching. +        newUserData.mLastEnabledInputMethodsStr = "";          mCurrentUserId = newUserId; -        final var newUserData = getUserData(newUserId);          final String defaultImiId = SecureSettingsWrapper.getString(                  Settings.Secure.DEFAULT_INPUT_METHOD, null, newUserId); @@ -1402,7 +1389,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.                  }, "Lazily initialize IMMS#mImeDrawsImeNavBarRes");                  mMyPackageMonitor.register(mContext, UserHandle.ALL, mIoHandler); -                mSettingsObserver.registerContentObserverLocked(currentUserId); +                mSettingsObserver.registerContentObserverForAllUsers();                  final IntentFilter broadcastFilterForAllUsers = new IntentFilter();                  broadcastFilterForAllUsers.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); @@ -1428,10 +1415,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.                          AdditionalSubtypeMapRepository::startWriterThread,                          "Start AdditionalSubtypeMapRepository's writer thread"); -                if (mExperimentalConcurrentMultiUserModeEnabled) { +                if (mConcurrentMultiUserModeEnabled) {                      for (int userId : mUserManagerInternal.getUserIds()) {                          if (userId != mCurrentUserId) { -                            experimentalInitializeVisibleBackgroundUserLocked(userId); +                            initializeVisibleBackgroundUserLocked(userId);                          }                      }                  } @@ -2538,7 +2525,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.              @SuppressWarnings("GuardedBy") Consumer<ClientState> clearClientSession = c -> {                  // TODO(b/305849394): Figure out what we should do for single user IME mode.                  final boolean shouldClearClientSession = -                        !mExperimentalConcurrentMultiUserModeEnabled +                        !mConcurrentMultiUserModeEnabled                                  || UserHandle.getUserId(c.mUid) == userId;                  if (shouldClearClientSession) {                      clearClientSessionLocked(c); @@ -2840,27 +2827,25 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.      }      /** -     * This is an experimental implementation used when and only when -     * {@link #mExperimentalConcurrentMultiUserModeEnabled}. +     * This initialization logic is used when and only when {@link #mConcurrentMultiUserModeEnabled} +     * is set to {@code true}.       * -     * <p>Never assume what this method is doing is officially supported. For the canonical and -     * desired behaviors always refer to single-user code paths such as +     * <p>There remain several yet-to-be-implemented features. For the canonical and desired +     * behaviors always refer to single-user code paths such as       * {@link #updateInputMethodsFromSettingsLocked(boolean, int)}.</p>       *       * <p>Here are examples of missing features.</p>       * <ul> -     *     <li>Subtypes are not supported at all!</li>       *     <li>Profiles are not supported.</li>       *     <li>       *         {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated.       *     </li>       *     <li>{@link InputMethodBindingController#getDeviceIdToShowIme()} is ignored.</li> -     *     <li>{@link #mPreventImeStartupUnlessTextEditor} is ignored.</li>       *     <li>and so on.</li>       * </ul>       */      @GuardedBy("ImfLock.class") -    void experimentalInitializeVisibleBackgroundUserLocked(@UserIdInt int userId) { +    void initializeVisibleBackgroundUserLocked(@UserIdInt int userId) {          final var settings = InputMethodSettingsRepository.get(userId);          // Until we figure out what makes most sense, we enable all the pre-installed IMEs in @@ -2868,7 +2853,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.          String enabledImeIdsStr = settings.getEnabledInputMethodsStr();          for (var imi : settings.getMethodList()) {              if (!imi.isSystem()) { -                return; +                continue;              }              enabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(enabledImeIdsStr, imi.getId());          } @@ -2881,19 +2866,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.          if (TextUtils.isEmpty(id)) {              final InputMethodInfo imi = InputMethodInfoUtils.getMostApplicableDefaultIME(                      settings.getEnabledInputMethodList()); -            if (imi == null) { -                return; +            if (imi != null) { +                id = imi.getId(); +                settings.putSelectedInputMethod(id);              } -            id = imi.getId(); -            settings.putSelectedInputMethod(id);          } +        final var bindingController = getInputMethodBindingController(userId); +        bindingController.setSelectedMethodId(id); +        // Also re-initialize controllers.          final var userData = getUserData(userId);          userData.mSwitchingController.resetCircularListLocked(mContext, settings);          userData.mHardwareKeyboardShortcutController.update(settings); - -        final var bindingController = getInputMethodBindingController(userId); -        bindingController.setSelectedMethodId(id);      }      @GuardedBy("ImfLock.class") @@ -3701,8 +3685,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.                  final long ident = Binder.clearCallingIdentity();                  try {                      // Verify if IMMS is in the process of switching user. -                    if (!mExperimentalConcurrentMultiUserModeEnabled -                            && mUserSwitchHandlerTask != null) { +                    if (!mConcurrentMultiUserModeEnabled && mUserSwitchHandlerTask != null) {                          // There is already an on-going pending user switch task.                          final int nextUserId = mUserSwitchHandlerTask.mToUserId;                          if (userId == nextUserId) { @@ -3757,7 +3740,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.                      }                      // Verify if caller is a background user. -                    if (!mExperimentalConcurrentMultiUserModeEnabled && userId != mCurrentUserId) { +                    if (!mConcurrentMultiUserModeEnabled && userId != mCurrentUserId) {                          if (ArrayUtils.contains(                                  mUserManagerInternal.getProfileIds(mCurrentUserId, false),                                  userId)) { @@ -4269,9 +4252,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.                  }                  if (currentUser) {                      // To avoid unnecessary "updateInputMethodsFromSettingsLocked" from happening. -                    if (mSettingsObserver != null) { -                        mSettingsObserver.mLastEnabled = settings.getEnabledInputMethodsStr(); -                    } +                    final var userData = getUserData(userId); +                    userData.mLastEnabledInputMethodsStr = settings.getEnabledInputMethodsStr();                      updateInputMethodsFromSettingsLocked(false /* enabledChanged */, userId);                  }              } @@ -5539,7 +5521,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.      @GuardedBy("ImfLock.class")      private boolean switchToInputMethodLocked(String imeId, @UserIdInt int userId) {          final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); -        if (mExperimentalConcurrentMultiUserModeEnabled || userId == mCurrentUserId) { +        if (mConcurrentMultiUserModeEnabled || userId == mCurrentUserId) {              if (!settings.getMethodMap().containsKey(imeId)                      || !settings.getEnabledInputMethodList()                      .contains(settings.getMethodMap().get(imeId))) { @@ -6110,6 +6092,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.                          p.println("      inFullscreenMode=" + u.mInFullscreenMode);                          p.println("      switchingController:");                          u.mSwitchingController.dump(p, "        "); +                        p.println("      mLastEnabledInputMethodsStr=" +                                + u.mLastEnabledInputMethodsStr);                      };              mUserDataRepository.forAllUserData(userDataDump); @@ -6123,11 +6107,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.              mVisibilityStateComputer.dump(pw, "  ");              p.println("  mInFullscreenMode=" + userData.mInFullscreenMode);              p.println("  mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive); -            p.println("  mExperimentalConcurrentMultiUserModeEnabled=" -                    + mExperimentalConcurrentMultiUserModeEnabled); +            p.println("  mConcurrentMultiUserModeEnabled=" + mConcurrentMultiUserModeEnabled);              p.println("  ENABLE_HIDE_IME_CAPTION_BAR="                      + InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR); -            p.println("  mSettingsObserver=" + mSettingsObserver);              p.println("  mStylusIds=" + (mStylusIds != null                      ? Arrays.toString(mStylusIds.toArray()) : "")); diff --git a/services/core/java/com/android/server/inputmethod/UserDataRepository.java b/services/core/java/com/android/server/inputmethod/UserDataRepository.java index 48284fb3163c..59411ad71bc0 100644 --- a/services/core/java/com/android/server/inputmethod/UserDataRepository.java +++ b/services/core/java/com/android/server/inputmethod/UserDataRepository.java @@ -174,6 +174,13 @@ final class UserDataRepository {                  mEnabledAccessibilitySessions = new SparseArray<>();          /** +         * A per-user cache of {@link InputMethodSettings#getEnabledInputMethodsStr()}. +         */ +        @GuardedBy("ImfLock.class") +        @NonNull +        String mLastEnabledInputMethodsStr = ""; + +        /**           * Intended to be instantiated only from this file.           */          private UserData(@UserIdInt int userId, diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java index 7c1a5e113a5e..93ef6f044e1b 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java @@ -56,6 +56,7 @@ final class MediaRoute2ProviderWatcher {      private final PackageManager mPackageManager;      private final ArrayList<MediaRoute2ProviderServiceProxy> mProxies = new ArrayList<>(); +    private final Runnable mScanPackagesRunnable = this::scanPackages;      private boolean mRunning;      MediaRoute2ProviderWatcher(Context context, @@ -106,7 +107,7 @@ final class MediaRoute2ProviderWatcher {              mRunning = false;              mContext.unregisterReceiver(mScanPackagesReceiver); -            mHandler.removeCallbacks(this::scanPackages); +            mHandler.removeCallbacks(mScanPackagesRunnable);              // Stop all providers.              for (int i = mProxies.size() - 1; i >= 0; i--) { @@ -189,8 +190,8 @@ final class MediaRoute2ProviderWatcher {      }      private void postScanPackagesIfNeeded() { -        if (!mHandler.hasCallbacks(this::scanPackages)) { -            mHandler.post(this::scanPackages); +        if (!mHandler.hasCallbacks(mScanPackagesRunnable)) { +            mHandler.post(mScanPackagesRunnable);          }      } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 173fc5c86dd3..009e9b862b0f 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -4568,7 +4568,7 @@ final class InstallPackageHelper {                              PackageManagerException.INTERNAL_ERROR_SYSTEM_OVERLAY_STATIC);                  }              } else { -                if ((scanFlags & SCAN_AS_VENDOR) != 0) { +                if ((scanFlags & (SCAN_AS_VENDOR | SCAN_AS_ODM)) != 0) {                      if (pkg.getTargetSdkVersion() < ScanPackageUtils.getVendorPartitionVersion()) {                          Slog.w(TAG, "System overlay " + pkg.getPackageName()                                  + " targets an SDK below the required SDK level of vendor" diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c0b8034b9a56..2e63cdbf1823 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -186,6 +186,7 @@ import com.android.internal.pm.pkg.component.ParsedInstrumentation;  import com.android.internal.pm.pkg.component.ParsedMainComponent;  import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;  import com.android.internal.telephony.CarrierAppUtils; +import com.android.internal.telephony.TelephonyPermissions;  import com.android.internal.util.ArrayUtils;  import com.android.internal.util.CollectionUtils;  import com.android.internal.util.ConcurrentUtils; @@ -4492,8 +4493,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService      void setSystemAppHiddenUntilInstalled(@NonNull Computer snapshot, String packageName,              boolean hidden) {          final int callingUid = Binder.getCallingUid(); -        final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID -                || callingUid == Process.SYSTEM_UID; +        final boolean calledFromSystemOrPhone = TelephonyPermissions.isSystemOrPhone(callingUid);          if (!calledFromSystemOrPhone) {              mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,                      "setSystemAppHiddenUntilInstalled"); diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index ff8abf879487..924b36cef79a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -92,6 +92,7 @@ import android.util.proto.ProtoOutputStream;  import com.android.internal.content.InstallLocationUtils;  import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.telephony.TelephonyPermissions;  import com.android.internal.util.ArrayUtils;  import com.android.internal.util.FastPrintWriter;  import com.android.internal.util.HexDump; @@ -356,7 +357,7 @@ public class PackageManagerServiceUtils {       * If not, throws a {@link SecurityException}.       */      public static void enforceSystemOrPhoneCaller(String methodName, int callingUid) { -        if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) { +        if (!TelephonyPermissions.isSystemOrPhone(callingUid)) {              throw new SecurityException(                      "Cannot call " + methodName + " from UID " + callingUid);          } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 4d07ab5cbb30..8be20b0e4904 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1637,10 +1637,12 @@ final class DefaultPermissionGrantPolicy {      private boolean isSystemOrCertificateMatchingPackage(PackageInfo pi, String cert) {          if (cert == null) {              return pi.applicationInfo.isSystemApp(); +        } else if (Objects.equals(cert, "platform")) { +            return mServiceInternal.isPlatformSigned(pi.packageName); +        } else { +            return mContext.getPackageManager().hasSigningCertificate(pi.packageName, HexEncoding. +                    decode(cert.replace(":", "")), PackageManager.CERT_INPUT_SHA256);          } - -        return mContext.getPackageManager().hasSigningCertificate(pi.packageName, HexEncoding. -                decode(cert.replace(":", "")), PackageManager.CERT_INPUT_SHA256);      }      private static boolean doesPackageSupportRuntimePermissions(PackageInfo pkg) { diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index c85ceac9ea55..4f28e023da92 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -154,12 +154,22 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve              }          } +        Slog.i(TAG, "Checking available remediations for health check failure." +                + " failedPackage: " +                + (failedPackage == null ? null : failedPackage.getPackageName()) +                + " failureReason: " + failureReason +                + " available impact: " + impact);          return impact;      }      @Override      public boolean execute(@Nullable VersionedPackage failedPackage,              @FailureReasons int rollbackReason, int mitigationCount) { +        Slog.i(TAG, "Executing remediation." +                + " failedPackage: " +                + (failedPackage == null ? null : failedPackage.getPackageName()) +                + " rollbackReason: " + rollbackReason +                + " mitigationCount: " + mitigationCount);          if (Flags.recoverabilityDetection()) {              List<RollbackInfo> availableRollbacks = getAvailableRollbacks();              if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { @@ -503,6 +513,10 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve              @FailureReasons int rollbackReason) {          assertInWorkerThread(); +        Slog.i(TAG, "Rolling back package. RollbackId: " + rollback.getRollbackId() +                + " failedPackage: " +                + (failedPackage == null ? null : failedPackage.getPackageName()) +                + " rollbackReason: " + rollbackReason);          final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);          int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason);          final String failedPackageToLog; diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 2fc183d9113f..bc29b377f540 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -532,7 +532,8 @@ final class VibrationSettings {              return false;          } -        if (Flags.keyboardCategoryEnabled() && mVibrationConfig.hasFixedKeyboardAmplitude()) { +        if (Flags.keyboardCategoryEnabled() +                && mVibrationConfig.isKeyboardVibrationSettingsSupported()) {              int category = callerInfo.attrs.getCategory();              if (usage == USAGE_TOUCH && category == CATEGORY_KEYBOARD) {                  // Keyboard touch has a different user setting. diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 484481b4840b..8be338049435 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -734,6 +734,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A       */      private boolean mOccludesParent; +    /** Whether the activity have style floating */ +    private boolean mStyleFloating; +      /**       * Unlike {@link #mOccludesParent} which can be changed at runtime. This is a static attribute       * from the style of activity. Because we don't want {@link WindowContainer#getOrientation()} @@ -2188,7 +2191,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A                  realTheme, com.android.internal.R.styleable.Window, mUserId);          if (ent != null) { -            mOccludesParent = !ActivityInfo.isTranslucentOrFloating(ent.array) +            final boolean styleTranslucent = ent.array.getBoolean( +                    com.android.internal.R.styleable.Window_windowIsTranslucent, false); +            mStyleFloating = ent.array.getBoolean( +                    com.android.internal.R.styleable.Window_windowIsFloating, false); +            mOccludesParent = !(styleTranslucent || mStyleFloating)                      // This style is propagated to the main window attributes with                      // FLAG_SHOW_WALLPAPER from PhoneWindow#generateLayout.                      || ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false); @@ -2197,6 +2204,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A              mOptOutEdgeToEdge = ent.array.getBoolean(                      R.styleable.Window_windowOptOutEdgeToEdgeEnforcement, false);          } else { +            mStyleFloating = false;              mStyleFillsParent = mOccludesParent = true;              noDisplay = false;              mOptOutEdgeToEdge = false; @@ -3237,6 +3245,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A          return occludesParent(true /* includingFinishing */);      } +    boolean isStyleFloating() { +        return mStyleFloating; +    } +      /** Returns true if this activity is not finishing, is opaque and fills the entire space of       * this task. */      boolean occludesParent() { diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index d45ed12e5ef6..14e256f7a815 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -1335,12 +1335,16 @@ class BackNavigationController {                  }                  // If there is only one adaptor, attach the windowless window to top activity,                  // because fixed rotation only applies on activity. -                // Note that embedded activity won't use fixed rotation. -                final Configuration openConfig = mAdaptors.length == 1 +                // Note that embedded activity won't use fixed rotation. Also, there is only one +                // animation target for closing task. +                final boolean chooseActivity = mAdaptors.length == 1 +                        && (switchType == ACTIVITY_SWITCH || mainActivity.mDisplayContent +                                .isFixedRotationLaunchingApp(mainActivity)); +                final Configuration openConfig = chooseActivity                          ? mainActivity.getConfiguration() : openTask.getConfiguration();                  mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController                          .addWindowlessStartingSurface(openTask, mainActivity, -                                mAdaptors.length == 1 ? mainActivity.getSurfaceControl() +                                chooseActivity ? mainActivity.getSurfaceControl()                                          : mRemoteAnimationTarget.leash, snapshot, openConfig,                              new IWindowlessStartingSurfaceCallback.Stub() {                              // Once the starting surface has been created in shell, it will call diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java index 857e03d71f3f..475a50473780 100644 --- a/services/core/java/com/android/server/wm/DeviceStateController.java +++ b/services/core/java/com/android/server/wm/DeviceStateController.java @@ -16,9 +16,19 @@  package com.android.server.wm; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN; +  import android.annotation.CallbackExecutor;  import android.annotation.NonNull; +import android.annotation.Nullable;  import android.content.Context; +import android.hardware.devicestate.DeviceStateManager; +import android.hardware.devicestate.feature.flags.FeatureFlags; +import android.hardware.devicestate.feature.flags.FeatureFlagsImpl;  import android.util.ArrayMap;  import android.util.Pair; @@ -28,6 +38,7 @@ import com.android.internal.annotations.VisibleForTesting;  import com.android.internal.util.ArrayUtils;  import java.util.ArrayList; +import java.util.Collections;  import java.util.List;  import java.util.Map;  import java.util.concurrent.Executor; @@ -43,16 +54,16 @@ final class DeviceStateController {      @NonNull      private final WindowManagerGlobalLock mWmLock;      @NonNull -    private final int[] mOpenDeviceStates; +    private final List<Integer> mOpenDeviceStates;      @NonNull -    private final int[] mHalfFoldedDeviceStates; +    private final List<Integer> mHalfFoldedDeviceStates;      @NonNull -    private final int[] mFoldedDeviceStates; +    private final List<Integer> mFoldedDeviceStates;      @NonNull -    private final int[] mRearDisplayDeviceStates; -    private final int mConcurrentDisplayDeviceState; +    private final List<Integer> mRearDisplayDeviceStates; +    private final List<Integer> mConcurrentDisplayDeviceStates;      @NonNull -    private final int[] mReverseRotationAroundZAxisStates; +    private final List<Integer> mReverseRotationAroundZAxisStates;      @GuardedBy("mWmLock")      @NonNull      @VisibleForTesting @@ -76,18 +87,55 @@ final class DeviceStateController {      DeviceStateController(@NonNull Context context, @NonNull WindowManagerGlobalLock wmLock) {          mWmLock = wmLock; -        mOpenDeviceStates = context.getResources() -                .getIntArray(R.array.config_openDeviceStates); -        mHalfFoldedDeviceStates = context.getResources() -                .getIntArray(R.array.config_halfFoldedDeviceStates); -        mFoldedDeviceStates = context.getResources() -                .getIntArray(R.array.config_foldedDeviceStates); -        mRearDisplayDeviceStates = context.getResources() -                .getIntArray(R.array.config_rearDisplayDeviceStates); -        mConcurrentDisplayDeviceState = context.getResources() -                .getInteger(R.integer.config_deviceStateConcurrentRearDisplay); -        mReverseRotationAroundZAxisStates = context.getResources() -                .getIntArray(R.array.config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis); +        final FeatureFlags deviceStateManagerFlags = new FeatureFlagsImpl(); +        if (deviceStateManagerFlags.deviceStatePropertyMigration()) { +            mOpenDeviceStates = new ArrayList<>(); +            mHalfFoldedDeviceStates = new ArrayList<>(); +            mFoldedDeviceStates = new ArrayList<>(); +            mRearDisplayDeviceStates = new ArrayList<>(); +            mConcurrentDisplayDeviceStates = new ArrayList<>(); + +            final DeviceStateManager deviceStateManager = +                    context.getSystemService(DeviceStateManager.class); +            final List<android.hardware.devicestate.DeviceState> deviceStates = +                    deviceStateManager.getSupportedDeviceStates(); + +            for (int i = 0; i < deviceStates.size(); i++) { +                final android.hardware.devicestate.DeviceState state = deviceStates.get(i); +                if (state.hasProperty( +                        PROPERTY_FEATURE_REAR_DISPLAY)) { +                    mRearDisplayDeviceStates.add(state.getIdentifier()); +                } else if (state.hasProperty( +                        PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT)) { +                    mConcurrentDisplayDeviceStates.add(state.getIdentifier()); +                } else if (state.hasProperty( +                        PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)) { +                    mFoldedDeviceStates.add(state.getIdentifier()); +                } else if (state.hasProperty( +                        PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) { +                    if (state.hasProperty( +                            PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN)) { +                        mHalfFoldedDeviceStates.add(state.getIdentifier()); +                    } else { +                        mOpenDeviceStates.add(state.getIdentifier()); +                    } +                } +            } +        } else { +            mOpenDeviceStates = copyIntArrayToList(context.getResources() +                    .getIntArray(R.array.config_openDeviceStates)); +            mHalfFoldedDeviceStates = copyIntArrayToList(context.getResources() +                    .getIntArray(R.array.config_halfFoldedDeviceStates)); +            mFoldedDeviceStates = copyIntArrayToList(context.getResources() +                    .getIntArray(R.array.config_foldedDeviceStates)); +            mRearDisplayDeviceStates = copyIntArrayToList(context.getResources() +                    .getIntArray(R.array.config_rearDisplayDeviceStates)); +            mConcurrentDisplayDeviceStates = new ArrayList<>(List.of(context.getResources() +                    .getInteger(R.integer.config_deviceStateConcurrentRearDisplay))); +        } + +        mReverseRotationAroundZAxisStates = copyIntArrayToList(context.getResources().getIntArray( +                R.array.config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis));          mMatchBuiltInDisplayOrientationToDefaultDisplay = context.getResources()                  .getBoolean(R.bool                          .config_matchSecondaryInternalDisplaysOrientationToReverseDefaultDisplay); @@ -145,7 +193,6 @@ final class DeviceStateController {       */      public void onDeviceStateReceivedByDisplayManager(int state) {          mCurrentState = state; -          final DeviceState deviceState;          if (ArrayUtils.contains(mHalfFoldedDeviceStates, state)) {              deviceState = DeviceState.HALF_FOLDED; @@ -155,9 +202,10 @@ final class DeviceStateController {              deviceState = DeviceState.REAR;          } else if (ArrayUtils.contains(mOpenDeviceStates, state)) {              deviceState = DeviceState.OPEN; -        } else if (state == mConcurrentDisplayDeviceState) { +        } else if (ArrayUtils.contains(mConcurrentDisplayDeviceStates, state)) {              deviceState = DeviceState.CONCURRENT;          } else { +              deviceState = DeviceState.UNKNOWN;          } @@ -190,4 +238,16 @@ final class DeviceStateController {          }          return entries;      } + +    @NonNull +    private List<Integer> copyIntArrayToList(@Nullable int[] values) { +        if (values == null) { +            return Collections.emptyList(); +        } +        final List<Integer> valueList = new ArrayList<>(); +        for (int i = 0; i < values.length; i++) { +            valueList.add(values[i]); +        } +        return valueList; +    }  } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 7206b36a41d5..33a649be43d0 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3411,6 +3411,7 @@ class Task extends TaskFragment {          info.isVisibleRequested = isVisibleRequested();          info.isSleeping = shouldSleepActivities();          info.isTopActivityTransparent = top != null && !top.fillsParent(); +        info.isTopActivityStyleFloating = top != null && top.isStyleFloating();          appCompatTaskInfo.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET;          appCompatTaskInfo.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;          appCompatTaskInfo.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index fec1175785ea..de73e6cfad12 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -28,6 +28,7 @@ import static android.graphics.GraphicsProtos.dumpPointProto;  import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;  import static android.os.PowerManager.DRAW_WAKE_LOCK;  import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.util.SequenceUtils.getNextSeq;  import static android.view.SurfaceControl.Transaction;  import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;  import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; @@ -96,7 +97,6 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;  import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;  import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;  import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; -import static android.util.SequenceUtils.getNextSeq;  import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;  import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM; @@ -249,8 +249,8 @@ import android.window.OnBackInvokedCallbackInfo;  import com.android.internal.annotations.VisibleForTesting;  import com.android.internal.policy.KeyInterceptionInfo; -import com.android.internal.protolog.common.LogLevel;  import com.android.internal.protolog.ProtoLog; +import com.android.internal.protolog.common.LogLevel;  import com.android.internal.util.FrameworkStatsLog;  import com.android.internal.util.ToBooleanFunction;  import com.android.server.policy.WindowManagerPolicy; @@ -1976,8 +1976,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP       */      boolean isReadyForDisplay() {          final boolean parentAndClientVisible = !isParentWindowHidden() -                && mViewVisibility == View.VISIBLE && mToken.isVisible(); -        return mHasSurface && isVisibleByPolicy() && !mDestroying +                && mViewVisibility == View.VISIBLE; +        return mHasSurface && isVisibleByPolicy() && !mDestroying && mToken.isVisible()                  && (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));      } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 7a710dc51004..7649a4e84bc3 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -193,7 +193,7 @@ cc_defaults {          "android.hardware.thermal-V2-ndk",          "android.hardware.tv.input@1.0",          "android.hardware.tv.input-V2-ndk", -        "android.hardware.vibrator-V2-cpp", +        "android.hardware.vibrator-V2-ndk",          "android.hardware.vibrator@1.0",          "android.hardware.vibrator@1.1",          "android.hardware.vibrator@1.2", diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp index 4be21d872383..2804a10c317f 100644 --- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp +++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp @@ -16,24 +16,21 @@  #define LOG_TAG "VibratorController" +#include <aidl/android/hardware/vibrator/IVibrator.h>  #include <android/hardware/vibrator/1.3/IVibrator.h> -#include <android/hardware/vibrator/IVibrator.h> -  #include <nativehelper/JNIHelp.h> -#include "android_runtime/AndroidRuntime.h" -#include "core_jni_helpers.h" -#include "jni.h" -  #include <utils/Log.h>  #include <utils/misc.h> -  #include <vibratorservice/VibratorHalController.h> +#include "android_runtime/AndroidRuntime.h"  #include "com_android_server_vibrator_VibratorManagerService.h" +#include "core_jni_helpers.h" +#include "jni.h"  namespace V1_0 = android::hardware::vibrator::V1_0;  namespace V1_3 = android::hardware::vibrator::V1_3; -namespace aidl = android::hardware::vibrator; +namespace Aidl = aidl::android::hardware::vibrator;  namespace android { @@ -67,29 +64,29 @@ static struct {  } sRampClassInfo;  static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) == -              static_cast<uint8_t>(aidl::EffectStrength::LIGHT)); +              static_cast<uint8_t>(Aidl::EffectStrength::LIGHT));  static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) == -              static_cast<uint8_t>(aidl::EffectStrength::MEDIUM)); +              static_cast<uint8_t>(Aidl::EffectStrength::MEDIUM));  static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) == -              static_cast<uint8_t>(aidl::EffectStrength::STRONG)); +              static_cast<uint8_t>(Aidl::EffectStrength::STRONG));  static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) == -              static_cast<uint8_t>(aidl::Effect::CLICK)); +              static_cast<uint8_t>(Aidl::Effect::CLICK));  static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) == -              static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK)); -static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(aidl::Effect::TICK)); -static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(aidl::Effect::THUD)); -static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(aidl::Effect::POP)); +              static_cast<uint8_t>(Aidl::Effect::DOUBLE_CLICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(Aidl::Effect::TICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(Aidl::Effect::THUD)); +static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(Aidl::Effect::POP));  static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) == -              static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK)); +              static_cast<uint8_t>(Aidl::Effect::HEAVY_CLICK));  static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) == -              static_cast<uint8_t>(aidl::Effect::RINGTONE_1)); +              static_cast<uint8_t>(Aidl::Effect::RINGTONE_1));  static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) == -              static_cast<uint8_t>(aidl::Effect::RINGTONE_2)); +              static_cast<uint8_t>(Aidl::Effect::RINGTONE_2));  static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) == -              static_cast<uint8_t>(aidl::Effect::RINGTONE_15)); +              static_cast<uint8_t>(Aidl::Effect::RINGTONE_15));  static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) == -              static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); +              static_cast<uint8_t>(Aidl::Effect::TEXTURE_TICK));  static std::shared_ptr<vibrator::HalController> findVibrator(int32_t vibratorId) {      vibrator::ManagerHalController* manager = @@ -155,15 +152,15 @@ private:      std::atomic<int64_t> mCallbackId;  }; -static aidl::BrakingPwle brakingPwle(aidl::Braking braking, int32_t duration) { -    aidl::BrakingPwle pwle; +static Aidl::BrakingPwle brakingPwle(Aidl::Braking braking, int32_t duration) { +    Aidl::BrakingPwle pwle;      pwle.braking = braking;      pwle.duration = duration;      return pwle;  } -static aidl::ActivePwle activePwleFromJavaPrimitive(JNIEnv* env, jobject ramp) { -    aidl::ActivePwle pwle; +static Aidl::ActivePwle activePwleFromJavaPrimitive(JNIEnv* env, jobject ramp) { +    Aidl::ActivePwle pwle;      pwle.startAmplitude =              static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startAmplitude));      pwle.endAmplitude = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endAmplitude)); @@ -175,20 +172,20 @@ static aidl::ActivePwle activePwleFromJavaPrimitive(JNIEnv* env, jobject ramp) {  }  /* Return true if braking is not NONE and the active PWLE starts and ends with zero amplitude. */ -static bool shouldBeReplacedWithBraking(aidl::ActivePwle activePwle, aidl::Braking braking) { -    return (braking != aidl::Braking::NONE) && (activePwle.startAmplitude == 0) && +static bool shouldBeReplacedWithBraking(Aidl::ActivePwle activePwle, Aidl::Braking braking) { +    return (braking != Aidl::Braking::NONE) && (activePwle.startAmplitude == 0) &&              (activePwle.endAmplitude == 0);  }  /* Return true if braking is not NONE and the active PWLE only ends with zero amplitude. */ -static bool shouldAddLastBraking(aidl::ActivePwle lastActivePwle, aidl::Braking braking) { -    return (braking != aidl::Braking::NONE) && (lastActivePwle.startAmplitude > 0) && +static bool shouldAddLastBraking(Aidl::ActivePwle lastActivePwle, Aidl::Braking braking) { +    return (braking != Aidl::Braking::NONE) && (lastActivePwle.startAmplitude > 0) &&              (lastActivePwle.endAmplitude == 0);  } -static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) { -    aidl::CompositeEffect effect; -    effect.primitive = static_cast<aidl::CompositePrimitive>( +static Aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) { +    Aidl::CompositeEffect effect; +    effect.primitive = static_cast<Aidl::CompositePrimitive>(              env->GetIntField(primitive, sPrimitiveClassInfo.id));      effect.scale = static_cast<float>(env->GetFloatField(primitive, sPrimitiveClassInfo.scale));      effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, sPrimitiveClassInfo.delay)); @@ -282,8 +279,8 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, j          ALOGE("vibratorPerformEffect failed because native wrapper was not initialized");          return -1;      } -    aidl::Effect effectType = static_cast<aidl::Effect>(effect); -    aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength); +    Aidl::Effect effectType = static_cast<Aidl::Effect>(effect); +    Aidl::EffectStrength effectStrength = static_cast<Aidl::EffectStrength>(strength);      auto callback = wrapper->createCallback(vibrationId);      auto performEffectFn = [effectType, effectStrength, &callback](vibrator::HalWrapper* hal) {          return hal->performEffect(effectType, effectStrength, callback); @@ -300,7 +297,7 @@ static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlon          return -1;      }      size_t size = env->GetArrayLength(composition); -    std::vector<aidl::CompositeEffect> effects; +    std::vector<Aidl::CompositeEffect> effects;      for (size_t i = 0; i < size; i++) {          jobject element = env->GetObjectArrayElement(composition, i);          effects.push_back(effectFromJavaPrimitive(env, element)); @@ -321,13 +318,13 @@ static jlong vibratorPerformPwleEffect(JNIEnv* env, jclass /* clazz */, jlong pt          ALOGE("vibratorPerformPwleEffect failed because native wrapper was not initialized");          return -1;      } -    aidl::Braking braking = static_cast<aidl::Braking>(brakingId); +    Aidl::Braking braking = static_cast<Aidl::Braking>(brakingId);      size_t size = env->GetArrayLength(waveform); -    std::vector<aidl::PrimitivePwle> primitives; +    std::vector<Aidl::PrimitivePwle> primitives;      std::chrono::milliseconds totalDuration(0);      for (size_t i = 0; i < size; i++) {          jobject element = env->GetObjectArrayElement(waveform, i); -        aidl::ActivePwle activePwle = activePwleFromJavaPrimitive(env, element); +        Aidl::ActivePwle activePwle = activePwleFromJavaPrimitive(env, element);          if ((i > 0) && shouldBeReplacedWithBraking(activePwle, braking)) {              primitives.push_back(brakingPwle(braking, activePwle.duration));          } else { @@ -356,8 +353,8 @@ static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong ptr, j          return;      }      auto alwaysOnEnableFn = [id, effect, strength](vibrator::HalWrapper* hal) { -        return hal->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect), -                                   static_cast<aidl::EffectStrength>(strength)); +        return hal->alwaysOnEnable(static_cast<int32_t>(id), static_cast<Aidl::Effect>(effect), +                                   static_cast<Aidl::EffectStrength>(strength));      };      wrapper->halCall<void>(alwaysOnEnableFn, "alwaysOnEnable");  } @@ -389,7 +386,7 @@ static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,                                static_cast<jlong>(info.capabilities.value()));      }      if (info.supportedEffects.isOk()) { -        std::vector<aidl::Effect> effects = info.supportedEffects.value(); +        std::vector<Aidl::Effect> effects = info.supportedEffects.value();          jintArray supportedEffects = env->NewIntArray(effects.size());          env->SetIntArrayRegion(supportedEffects, 0, effects.size(),                                 reinterpret_cast<jint*>(effects.data())); @@ -397,7 +394,7 @@ static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,                                sVibratorInfoBuilderClassInfo.setSupportedEffects, supportedEffects);      }      if (info.supportedBraking.isOk()) { -        std::vector<aidl::Braking> braking = info.supportedBraking.value(); +        std::vector<Aidl::Braking> braking = info.supportedBraking.value();          jintArray supportedBraking = env->NewIntArray(braking.size());          env->SetIntArrayRegion(supportedBraking, 0, braking.size(),                                 reinterpret_cast<jint*>(braking.data())); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 215cf2c5c85c..791d030a6b63 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -2134,14 +2134,20 @@ public final class SystemServer implements Dumpable {              }              t.traceEnd(); -            t.traceBegin("StartVpnManagerService"); -            try { -                vpnManager = VpnManagerService.create(context); -                ServiceManager.addService(Context.VPN_MANAGEMENT_SERVICE, vpnManager); -            } catch (Throwable e) { -                reportWtf("starting VPN Manager Service", e); +            if (!isWatch || !android.server.Flags.allowRemovingVpnService()) { +                t.traceBegin("StartVpnManagerService"); +                try { +                    vpnManager = VpnManagerService.create(context); +                    ServiceManager.addService(Context.VPN_MANAGEMENT_SERVICE, vpnManager); +                } catch (Throwable e) { +                    reportWtf("starting VPN Manager Service", e); +                } +                t.traceEnd(); +            } else { +                // VPN management currently does not work in Wear, so skip starting the +                // VPN manager SystemService. +                Slog.i(TAG, "Not starting VpnManagerService");              } -            t.traceEnd();              t.traceBegin("StartVcnManagementService");              try { diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig index e8aa68cf1a63..29f387117073 100644 --- a/services/java/com/android/server/flags.aconfig +++ b/services/java/com/android/server/flags.aconfig @@ -21,4 +21,11 @@ flag {       namespace: "wear_frameworks"       description: "Remove WearableSensingManagerService on Wear"       bug: "340929916" +} + +flag { +     name: "allow_removing_vpn_service" +     namespace: "wear_frameworks" +     description: "Allow removing VpnManagerService" +     bug: "340928692"  }
\ No newline at end of file diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index 78dbc60dbae0..0b7438cd1b17 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -2687,7 +2687,7 @@ class PermissionService(private val service: AccessCheckingService) :                  runtimePermissionChangedUidDevices.getOrPut(uid) { mutableSetOf() } += deviceId              } -            if (permission.hasGids && !wasPermissionGranted && isPermissionGranted) { +            if (permission.hasGids && (wasPermissionGranted != isPermissionGranted)) {                  gidsChangedUids += uid              }          } diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 977a8a05d6f3..64dbc50b8dd2 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -398,14 +398,17 @@ public final class ProfcollectForwardingService extends SystemService {                  if (randomNum >= traceFrequency) {                      return;                  } -                // Wait for 1s before starting tracing. +                // For a small percentage a traces, we collect the initialization behavior. +                boolean traceInitialization = ThreadLocalRandom.current().nextInt(10) < 1; +                int traceDelay = traceInitialization ? 0 : 1000; +                String traceTag = traceInitialization ? "camera_init" : "camera";                  BackgroundThread.get().getThreadHandler().postDelayed(() -> {                      try { -                        mIProfcollect.trace_once("camera"); +                        mIProfcollect.trace_once(traceTag);                      } catch (RemoteException e) {                          Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());                      } -                }, 1000); +                }, traceDelay);              }          }, null);      } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java index 80eab112d814..17d9ef9fad34 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java @@ -233,7 +233,7 @@ public class InputMethodManagerServiceTestBase {                          Process.THREAD_PRIORITY_FOREGROUND,                          true /* allowIo */);          mInputMethodManagerService = new InputMethodManagerService(mContext, -                InputMethodManagerService.shouldEnableExperimentalConcurrentMultiUserMode(mContext), +                InputMethodManagerService.shouldEnableConcurrentMultiUserMode(mContext),                  mServiceThread, mIoThread,                  unusedUserId -> mMockInputMethodBindingController);          spyOn(mInputMethodManagerService); diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index d070aaa93b01..2018e1a3ae62 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -1942,7 +1942,7 @@ public final class DisplayPowerControllerTest {      }      @Test -    public void testDozeManualBrightness() { +    public void testDozeManualBrightness_DpcRefactorDisabled() {          when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);          mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);          Settings.System.putInt(mContext.getContentResolver(), @@ -1972,6 +1972,45 @@ public final class DisplayPowerControllerTest {                  /* ignoreAnimationLimits= */ anyBoolean());          assertEquals(brightness * DOZE_SCALE_FACTOR, mHolder.dpc.getDozeBrightnessForOffload(),                  /* delta= */ 0); +        // This brightness shouldn't be stored in the setting +        verify(mHolder.brightnessSetting, never()).setBrightness(brightness * DOZE_SCALE_FACTOR); +    } + +    @Test +    public void testDozeManualBrightness_DpcRefactorEnabled() { +        when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true); +        when(mDisplayManagerFlagsMock.isRefactorDisplayPowerControllerEnabled()).thenReturn(true); +        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); +        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); +        Settings.System.putInt(mContext.getContentResolver(), +                Settings.System.SCREEN_BRIGHTNESS_MODE, +                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); +        float brightness = 0.277f; +        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); +        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); +        when(mHolder.hbmController.getCurrentBrightnessMax()) +                .thenReturn(PowerManager.BRIGHTNESS_MAX); +        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + +        DisplayPowerRequest dpr = new DisplayPowerRequest(); +        dpr.policy = DisplayPowerRequest.POLICY_DOZE; +        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); +        advanceTime(1); // Run updatePowerState, initialize + +        ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = +                ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); +        verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture()); +        BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); +        listener.onBrightnessChanged(brightness); +        advanceTime(1); // Send messages, run updatePowerState + +        verify(mHolder.animator).animateTo(eq(brightness * DOZE_SCALE_FACTOR), +                /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(), +                /* ignoreAnimationLimits= */ anyBoolean()); +        assertEquals(brightness * DOZE_SCALE_FACTOR, mHolder.dpc.getDozeBrightnessForOffload(), +                /* delta= */ 0); +        // This brightness shouldn't be stored in the setting +        verify(mHolder.brightnessSetting, never()).setBrightness(brightness * DOZE_SCALE_FACTOR);      }      @Test diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java index ecd799f44552..6ec888cd2e45 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -406,8 +406,6 @@ public class FingerprintAuthenticationClientTest {          mContextInjector.getValue().accept(opContext);          verify(mHal).onContextChanged(same(opContext)); -        verify(mHal, times(2)).setIgnoreDisplayTouches( -                opContext.operationState.getFingerprintOperationState().isHardwareIgnoringTouches);          client.stopHalOperation(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 21364b861ed8..87b52e6194ce 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -2068,7 +2068,6 @@ public class HdmiCecLocalDeviceTvTest {          assertThat(mPowerManager.isInteractive()).isTrue();      } -      @Test      public void handleStandby_fromNonActiveSource_previousActiveSourceNotSet_Standby() {          HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1, @@ -2091,6 +2090,35 @@ public class HdmiCecLocalDeviceTvTest {                  .isFalse();      } +    @Test +    public void handleReportPhysicalAddress_DeviceDiscoveryActionInProgress_noNewDeviceAction() { +        mHdmiControlService.getHdmiCecNetwork().clearDeviceList(); +        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); +        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); +        mNativeWrapper.clearResultMessages(); +        mTestLooper.dispatchAll(); + +        HdmiCecMessage reportPhysicalAddressFromPlayback1 = +                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( +                        ADDR_PLAYBACK_1, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK); +        HdmiCecMessage reportPhysicalAddressFromPlayback2 = +                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( +                        ADDR_PLAYBACK_2, 0x2000, HdmiDeviceInfo.DEVICE_PLAYBACK); +        HdmiCecMessage giveOsdName = HdmiCecMessageBuilder.buildGiveOsdNameCommand( +                ADDR_TV, ADDR_PLAYBACK_2); +        // Skip state waiting for <Report Physical Address> for DeviceDiscoveryAction s.t. message +        // can be dispatched to local device TV. +        mNativeWrapper.onCecMessage(reportPhysicalAddressFromPlayback1); +        mNativeWrapper.clearResultMessages(); +        mTestLooper.dispatchAll(); + +        mNativeWrapper.onCecMessage(reportPhysicalAddressFromPlayback2); +        mTestLooper.dispatchAll(); + +        // NewDeviceAction did not start and <Give OSD Name> was not sent. +        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveOsdName); +    } +      protected static class MockTvDevice extends HdmiCecLocalDeviceTv {          MockTvDevice(HdmiControlService service) {              super(service); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java index 88a94830e98a..60d8964267f1 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -605,7 +605,7 @@ public class VibrationSettingsTest {      public void shouldIgnoreVibration_withKeyboardSettingsOff_shouldIgnoreKeyboardVibration() {          setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);          setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 0 /* OFF*/); -        setHasFixedKeyboardAmplitudeIntensity(true); +        setKeyboardVibrationSettingsSupported(true);          // Keyboard touch ignored.          assertVibrationIgnoredForAttributes( @@ -630,7 +630,7 @@ public class VibrationSettingsTest {      public void shouldIgnoreVibration_withKeyboardSettingsOn_shouldNotIgnoreKeyboardVibration() {          setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);          setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */); -        setHasFixedKeyboardAmplitudeIntensity(true); +        setKeyboardVibrationSettingsSupported(true);          // General touch ignored.          assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS); @@ -645,10 +645,10 @@ public class VibrationSettingsTest {      @Test      @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED) -    public void shouldIgnoreVibration_noFixedKeyboardAmplitude_ignoresKeyboardTouchVibration() { +    public void shouldIgnoreVibration_notSupportKeyboardVibration_ignoresKeyboardTouchVibration() {          setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);          setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */); -        setHasFixedKeyboardAmplitudeIntensity(false); +        setKeyboardVibrationSettingsSupported(false);          // General touch ignored.          assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS); @@ -974,8 +974,8 @@ public class VibrationSettingsTest {          when(mVibrationConfigMock.ignoreVibrationsOnWirelessCharger()).thenReturn(ignore);      } -    private void setHasFixedKeyboardAmplitudeIntensity(boolean hasFixedAmplitude) { -        when(mVibrationConfigMock.hasFixedKeyboardAmplitude()).thenReturn(hasFixedAmplitude); +    private void setKeyboardVibrationSettingsSupported(boolean supported) { +        when(mVibrationConfigMock.isKeyboardVibrationSettingsSupported()).thenReturn(supported);      }      private void deleteUserSetting(String settingName) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java index 51255948ed4a..36861fae35da 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java @@ -16,6 +16,15 @@  package com.android.server.wm; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN; +import static android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION; +  import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;  import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; @@ -24,8 +33,11 @@ import static org.junit.Assert.assertTrue;  import android.content.Context;  import android.content.res.Resources; +import android.hardware.devicestate.DeviceState;  import android.hardware.devicestate.DeviceStateManager;  import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled;  import android.util.Pair;  import androidx.test.filters.SmallTest; @@ -37,6 +49,8 @@ import com.google.common.util.concurrent.MoreExecutors;  import org.junit.Before;  import org.junit.Test; +import java.util.ArrayList; +import java.util.HashSet;  import java.util.List;  import java.util.concurrent.Executor;  import java.util.function.Consumer; @@ -79,39 +93,67 @@ public class DeviceStateControllerTests {      @Test      public void testInitialization() {          initialize(true /* supportFold */, true /* supportHalfFolded */); -        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); +        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());          assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);      }      @Test      public void testInitializationWithNoFoldSupport() {          initialize(false /* supportFold */, false /* supportHalfFolded */); -        mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); +        mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());          // Note that the folded state is ignored.          assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState);      }      @Test -    public void testWithFoldSupported() { +    @RequiresFlagsDisabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION) +    public void testWithFoldSupported_withOverlayConfigValues() {          initialize(true /* supportFold */, false /* supportHalfFolded */); -        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); +        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());          assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); -        mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); +        mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());          assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); -        mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates[0]); +        mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier());          assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored      }      @Test -    public void testWithHalfFoldSupported() { +    @RequiresFlagsEnabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION) +    public void testWithFoldSupported_withDeviceStateManagerPropertyAPI() { +        initialize(true /* supportFold */, false /* supportHalfFolded */); +        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier()); +        assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); +        mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier()); +        assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); +        mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier()); +        assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored +    } + +    @Test +    @RequiresFlagsDisabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION) +    public void testWithHalfFoldSupported_withOverlayConfigValue() { +        initialize(true /* supportFold */, true /* supportHalfFolded */); +        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier()); +        assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); +        mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier()); +        assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); +        mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier()); +        assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState); +        mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState.getIdentifier()); +        assertEquals(DeviceStateController.DeviceState.CONCURRENT, mCurrentState); +    } + +    @Test +    @RequiresFlagsEnabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION) +    public void testWithHalfFoldSupported_withDeviceStateManagerPropertyApi() {          initialize(true /* supportFold */, true /* supportHalfFolded */); -        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); +        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());          assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); -        mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); +        mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());          assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); -        mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates[0]); +        mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier());          assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState); -        mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState); +        mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState.getIdentifier());          assertEquals(DeviceStateController.DeviceState.CONCURRENT, mCurrentState);      } @@ -121,16 +163,18 @@ public class DeviceStateControllerTests {          assertEquals(1, mTarget.mDeviceStateCallbacks.size());          assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDelegate)); -        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); +        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());          assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); -        mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); +        mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());          assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);          // The callback should not receive state change when it is unregistered.          mTarget.unregisterDeviceStateCallback(mDelegate);          assertTrue(mTarget.mDeviceStateCallbacks.isEmpty()); -        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); -        assertEquals(DeviceStateController.DeviceState.FOLDED /* unchanged */, mCurrentState); + +        mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier()); +        assertEquals(DeviceStateController.DeviceState.FOLDED /* unchanged */, +                mCurrentState);      }      @Test @@ -151,16 +195,50 @@ public class DeviceStateControllerTests {          assertEquals(mExecutor, entries.get(0).second);      } -    private final int[] mFoldedStates = {0}; -    private final int[] mOpenDeviceStates = {1}; -    private final int[] mHalfFoldedStates = {2}; -    private final int[] mRearDisplayStates = {3}; -    private final int mConcurrentDisplayState = 4; +    private final List<DeviceState> mFoldedStates = new ArrayList<>( +            List.of(new DeviceState(new DeviceState.Configuration.Builder(0, +                    "folded").setSystemProperties(new HashSet<>( +                    List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY))) +                    .setPhysicalProperties(new HashSet<>( +                            List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED))) +                    .build()))); +    private final List<DeviceState> mOpenDeviceStates = new ArrayList<>( +            List.of(new DeviceState(new DeviceState.Configuration.Builder(1, +                    "open").setSystemProperties(new HashSet<>( +                            List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY))) +                    .setPhysicalProperties(new HashSet<>( +                            List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN))) +                    .build()))); +    private final List<DeviceState> mHalfFoldedStates = new ArrayList<>( +            List.of(new DeviceState(new DeviceState.Configuration.Builder(2, +                    "half_folded").setSystemProperties(new HashSet<>( +                            List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY))) +                    .setPhysicalProperties(new HashSet<>( +                            List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN))) +                    .build()))); +    private final List<DeviceState> mRearDisplayStates = new ArrayList<>( +            List.of(new DeviceState(new DeviceState.Configuration.Builder(3, +                    "rear_display").setSystemProperties(new HashSet<>( +                            List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY, +                                    PROPERTY_FEATURE_REAR_DISPLAY))) +                    .setPhysicalProperties(new HashSet<>( +                            List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN))) +                    .build()))); +    private final DeviceState mConcurrentDisplayState = new DeviceState( +            new DeviceState.Configuration.Builder(4, "concurrent_display") +                    .setSystemProperties(new HashSet<>(List.of( +                            PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY, +                            PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT))) +                    .setPhysicalProperties(new HashSet<>(List.of( +                            PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN))) +                    .build());      private class DeviceStateControllerBuilder {          private boolean mSupportFold = false;          private boolean mSupportHalfFold = false; +          private Consumer<DeviceStateController.DeviceState> mDelegate; +        private final List<DeviceState> mDeviceStateList = new ArrayList<>();          DeviceStateControllerBuilder setSupportFold(                  boolean supportFold, boolean supportHalfFold) { @@ -179,13 +257,17 @@ public class DeviceStateControllerTests {              if (enableFold || enableHalfFold) {                  when(mMockContext.getResources()                          .getIntArray(R.array.config_openDeviceStates)) -                        .thenReturn(mOpenDeviceStates); +                        .thenReturn(mapDeviceStateListToIdentifierArray(mOpenDeviceStates));                  when(mMockContext.getResources()                          .getIntArray(R.array.config_rearDisplayDeviceStates)) -                        .thenReturn(mRearDisplayStates); +                        .thenReturn(mapDeviceStateListToIdentifierArray(mRearDisplayStates));                  when(mMockContext.getResources()                          .getInteger(R.integer.config_deviceStateConcurrentRearDisplay)) -                        .thenReturn(mConcurrentDisplayState); +                        .thenReturn(mConcurrentDisplayState.getIdentifier()); + +                mDeviceStateList.addAll(mOpenDeviceStates); +                mDeviceStateList.addAll(mRearDisplayStates); +                mDeviceStateList.add(mConcurrentDisplayState);              } else {                  // Match the default value in framework resources                  when(mMockContext.getResources() @@ -196,12 +278,14 @@ public class DeviceStateControllerTests {              if (enableFold) {                  when(mMockContext.getResources()                          .getIntArray(R.array.config_foldedDeviceStates)) -                        .thenReturn(mFoldedStates); +                        .thenReturn(mapDeviceStateListToIdentifierArray(mFoldedStates)); +                mDeviceStateList.addAll(mFoldedStates);              }              if (enableHalfFold) {                  when(mMockContext.getResources()                          .getIntArray(R.array.config_halfFoldedDeviceStates)) -                        .thenReturn(mHalfFoldedStates); +                        .thenReturn(mapDeviceStateListToIdentifierArray(mHalfFoldedStates)); +                mDeviceStateList.addAll(mHalfFoldedStates);              }          } @@ -210,11 +294,20 @@ public class DeviceStateControllerTests {              mMockDeviceStateManager = mock(DeviceStateManager.class);              when(mMockContext.getSystemService(DeviceStateManager.class))                      .thenReturn(mMockDeviceStateManager); +            when(mMockDeviceStateManager.getSupportedDeviceStates()).thenReturn(mDeviceStateList);              Resources mockRes = mock(Resources.class);              when(mMockContext.getResources()).thenReturn((mockRes));              mockFold(mSupportFold, mSupportHalfFold);              mTarget = new DeviceStateController(mMockContext, new WindowManagerGlobalLock());              mTarget.registerDeviceStateCallback(mDelegate, mExecutor);          } + +        private int[] mapDeviceStateListToIdentifierArray(List<DeviceState> deviceStates) { +            int[] identifiers = new int[deviceStates.size()]; +            for (int i = 0; i < deviceStates.size(); i++) { +                identifiers[i] = deviceStates.get(i).getIdentifier(); +            } +            return identifiers; +        }      }  } diff --git a/tests/Input/assets/testPointerStrokeStyle.png b/tests/Input/assets/testPointerStrokeStyle.pngBinary files differ new file mode 100644 index 000000000000..4ddde70b2f0a --- /dev/null +++ b/tests/Input/assets/testPointerStrokeStyle.png diff --git a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt index d196b85a7466..e0f8c6d6ff4a 100644 --- a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt +++ b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt @@ -88,6 +88,35 @@ class PointerIconLoadingTest {          theme.applyStyle(              PointerIcon.vectorFillStyleToResource(PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_GREEN),              /* force= */ true) +        theme.applyStyle(PointerIcon.vectorStrokeStyleToResource( +            PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE), /* force= */ true) + +        val pointerIcon = +            PointerIcon.getLoadedSystemIcon( +                ContextThemeWrapper(context, theme), +                PointerIcon.TYPE_ARROW, +                /* useLargeIcons= */ false, +                /* pointerScale= */ 1f) + +        pointerIcon.getBitmap().assertAgainstGolden( +            screenshotRule, +            testName.methodName, +            exactScreenshotMatcher +        ) +    } + +    @Test +    fun testPointerStrokeStyle() { +        assumeTrue(enableVectorCursors()) +        assumeTrue(enableVectorCursorA11ySettings()) + +        val theme: Resources.Theme = context.getResources().newTheme() +        theme.setTo(context.getTheme()) +        theme.applyStyle( +            PointerIcon.vectorFillStyleToResource(PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK), +            /* force= */ true) +        theme.applyStyle(PointerIcon.vectorStrokeStyleToResource( +            PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_BLACK), /* force= */ true)          val pointerIcon =              PointerIcon.getLoadedSystemIcon( diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java index 489ef4444e1d..3722fefb12ad 100644 --- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java +++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java @@ -47,6 +47,8 @@ import android.net.ConnectivityModuleConnector.ConnectivityModuleHealthListener;  import android.os.Handler;  import android.os.SystemProperties;  import android.os.test.TestLooper; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags;  import android.platform.test.flag.junit.SetFlagsRule;  import android.provider.DeviceConfig;  import android.util.AtomicFile; @@ -288,7 +290,8 @@ public class CrashRecoveryTest {      }      @Test -    public void testBootLoopWithRescuePartyAndRollbackPackageHealthObserver() throws Exception { +    @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) +    public void testBootLoopWithRescuePartyAndRollbackObserver() throws Exception {          PackageWatchdog watchdog = createWatchdog();          RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);          RollbackPackageHealthObserver rollbackObserver = @@ -360,6 +363,56 @@ public class CrashRecoveryTest {          verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);      } +    @Test +    @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) +    public void testBootLoopWithRescuePartyAndRollbackObserverNoFlags() throws Exception { +        PackageWatchdog watchdog = createWatchdog(); +        RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog); +        RollbackPackageHealthObserver rollbackObserver = +                setUpRollbackPackageHealthObserver(watchdog); + +        verify(rescuePartyObserver, never()).executeBootLoopMitigation(1); +        verify(rollbackObserver, never()).executeBootLoopMitigation(1); +        for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) { +            watchdog.noteBoot(); +        } +        verify(rescuePartyObserver).executeBootLoopMitigation(1); +        verify(rescuePartyObserver, never()).executeBootLoopMitigation(2); +        verify(rollbackObserver, never()).executeBootLoopMitigation(1); + +        watchdog.noteBoot(); + +        verify(rescuePartyObserver, never()).executeBootLoopMitigation(2); +        verify(rollbackObserver).executeBootLoopMitigation(1); +        verify(rollbackObserver, never()).executeBootLoopMitigation(2); +        // Update the list of available rollbacks after executing bootloop mitigation once +        when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_HIGH, +                ROLLBACK_INFO_MANUAL)); + +        watchdog.noteBoot(); + +        verify(rescuePartyObserver, never()).executeBootLoopMitigation(2); +        verify(rollbackObserver).executeBootLoopMitigation(2); +        verify(rollbackObserver, never()).executeBootLoopMitigation(3); +        // Update the list of available rollbacks after executing bootloop mitigation +        when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_MANUAL)); + +        watchdog.noteBoot(); + +        verify(rescuePartyObserver).executeBootLoopMitigation(2); +        verify(rescuePartyObserver, never()).executeBootLoopMitigation(3); +        verify(rollbackObserver, never()).executeBootLoopMitigation(3); + +        moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1); +        Mockito.reset(rescuePartyObserver); + +        for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) { +            watchdog.noteBoot(); +        } +        verify(rescuePartyObserver).executeBootLoopMitigation(1); +        verify(rescuePartyObserver, never()).executeBootLoopMitigation(2); +    } +      RollbackPackageHealthObserver setUpRollbackPackageHealthObserver(PackageWatchdog watchdog) {          RollbackPackageHealthObserver rollbackObserver =                  spy(new RollbackPackageHealthObserver(mSpyContext, mApexManager)); |