diff options
189 files changed, 3918 insertions, 1450 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a60fd117fd17..e724ab885c39 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -12676,21 +12676,27 @@ package android.security { package android.security.advancedprotection { @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionFeature implements android.os.Parcelable { - ctor public AdvancedProtectionFeature(int); + ctor public AdvancedProtectionFeature(@NonNull String); method public int describeContents(); - method public int getId(); + method @NonNull public String getId(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.security.advancedprotection.AdvancedProtectionFeature> CREATOR; } @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager { + method @NonNull public android.content.Intent createSupportIntent(@NonNull String, @Nullable String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public java.util.List<android.security.advancedprotection.AdvancedProtectionFeature> getAdvancedProtectionFeatures(); method @RequiresPermission(android.Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean); - field public static final int FEATURE_ID_DISALLOW_CELLULAR_2G = 0; // 0x0 - field public static final int FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = 1; // 0x1 - field public static final int FEATURE_ID_DISALLOW_USB = 2; // 0x2 - field public static final int FEATURE_ID_DISALLOW_WEP = 3; // 0x3 - field public static final int FEATURE_ID_ENABLE_MTE = 4; // 0x4 + field @FlaggedApi("android.security.aapm_api") public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG = "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG"; + field public static final String EXTRA_SUPPORT_DIALOG_FEATURE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE"; + field public static final String EXTRA_SUPPORT_DIALOG_TYPE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE"; + field public static final String FEATURE_ID_DISALLOW_CELLULAR_2G = "android.security.advancedprotection.feature_disallow_2g"; + field public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = "android.security.advancedprotection.feature_disallow_install_unknown_sources"; + field public static final String FEATURE_ID_DISALLOW_USB = "android.security.advancedprotection.feature_disallow_usb"; + field public static final String FEATURE_ID_DISALLOW_WEP = "android.security.advancedprotection.feature_disallow_wep"; + field public static final String FEATURE_ID_ENABLE_MTE = "android.security.advancedprotection.feature_enable_mte"; + field public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION = "android.security.advancedprotection.type_blocked_interaction"; + field public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING = "android.security.advancedprotection.type_disabled_setting"; } } diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 4db3727e3de2..0e459027102e 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -38,6 +38,7 @@ import android.os.BundleMerger; import android.os.PowerExemptionManager; import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerExemptionManager.TempAllowListType; +import android.os.Process; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -76,6 +77,7 @@ public class BroadcastOptions extends ComponentOptions { FLAG_IS_ALARM_BROADCAST, FLAG_SHARE_IDENTITY, FLAG_INTERACTIVE, + FLAG_DEBUG_LOG, }) @Retention(RetentionPolicy.SOURCE) public @interface Flags {} @@ -86,6 +88,7 @@ public class BroadcastOptions extends ComponentOptions { private static final int FLAG_IS_ALARM_BROADCAST = 1 << 3; private static final int FLAG_SHARE_IDENTITY = 1 << 4; private static final int FLAG_INTERACTIVE = 1 << 5; + private static final int FLAG_DEBUG_LOG = 1 << 6; /** * Change ID which is invalid. @@ -1082,6 +1085,34 @@ public class BroadcastOptions extends ComponentOptions { } /** + * If enabled, additional debug messages for broadcast delivery will be logged. + * + * <p> This will only take effect when used by {@link Process#SHELL_UID} + * or {@link Process#ROOT_UID} or by apps under instrumentation. + * + * @hide + */ + @NonNull + public BroadcastOptions setDebugLogEnabled(boolean enabled) { + if (enabled) { + mFlags |= FLAG_DEBUG_LOG; + } else { + mFlags &= ~FLAG_DEBUG_LOG; + } + return this; + } + + /** + * @return if additional debug messages for broadcast delivery are enabled. + * + * @see #setDebugLogEnabled(boolean) + * @hide + */ + public boolean isDebugLogEnabled() { + return (mFlags & FLAG_DEBUG_LOG) != 0; + } + + /** * Returns the created options as a Bundle, which can be passed to * {@link android.content.Context#sendBroadcast(android.content.Intent) * Context.sendBroadcast(Intent)} and related methods. diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index a6c1a57a8131..0451ac0d6897 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -270,6 +270,6 @@ interface INotificationManager int[] getAllowedAdjustmentKeyTypes(); void setAssistantAdjustmentKeyTypeState(int type, boolean enabled); - String[] getTypeAdjustmentDeniedPackages(); - void setTypeAdjustmentForPackageState(String pkg, boolean enabled); + int[] getAllowedAdjustmentKeyTypesForPackage(String pkg); + void setAssistantAdjustmentKeyTypeStateForPackage(String pkg, int type, boolean enabled); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index ec10913360af..08bd854525ec 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1986,10 +1986,12 @@ public class NotificationManager { * @hide */ @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) - public void setTypeAdjustmentForPackageState(@NonNull String pkg, boolean enabled) { + public void setAssistantAdjustmentKeyTypeStateForPackage(@NonNull String pkg, + @Adjustment.Types int type, + boolean enabled) { INotificationManager service = service(); try { - service.setTypeAdjustmentForPackageState(pkg, enabled); + service.setAssistantAdjustmentKeyTypeStateForPackage(pkg, type, enabled); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index f29e2e8f10a3..2ad9660534a6 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -211,6 +211,16 @@ flag { } flag { + name: "place_add_user_dialog_within_activity" + namespace: "multiuser" + description: "Display dialog within activity to make it traversable by Accessibility" + bug: "383034393" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "property_invalidated_cache_bypass_mismatched_uids" namespace: "multiuser" description: "Bypass the cache when the process UID does not match the binder UID." diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java index da9d4f472bb6..0e2c05f92e7c 100644 --- a/core/java/android/hardware/display/DisplayTopology.java +++ b/core/java/android/hardware/display/DisplayTopology.java @@ -246,10 +246,9 @@ public final class DisplayTopology implements Parcelable { // The optimal pair is the pair which has the smallest deviation. The deviation consists of // an x-axis component and a y-axis component, called xDeviation and yDeviation. // - // The deviations are like distances but a little different. They are calculated in two - // steps. The first step calculates both axes in a similar way. The next step compares the - // two values and chooses which axis to attach along. Depending on which axis is chosen, - // the deviation for one axis is updated. See below for details. + // The deviations are like distances but a little different. When they are calculated, each + // dimension is treated differently, depending on which edges (left+right or top+bottom) are + // attached. while (!needsParent.isEmpty()) { double bestDist = Double.POSITIVE_INFINITY; TreeNode bestChild = null, bestParent = null; @@ -263,33 +262,32 @@ public final class DisplayTopology implements Parcelable { float parentRight = parentPos.x + parent.getWidth(); float parentBottom = parentPos.y + parent.getHeight(); - // This is the smaller of the two ranges minus the amount of overlap shared - // between them. The "amount of overlap" is negative if there is no overlap, but - // this does not make a parenting ineligible, because we allow for attaching at - // the corner and for floating point error. The overlap is more negative the - // farther apart the closest corner pair is. - // - // For each axis, this calculates (SmallerRange - Overlap). If one range lies - // completely in the other (or they are equal), the axis' deviation will be - // zero. - // - // The "SmallerRange," which refers to smaller of the widths of the two rects, - // or smaller of the heights of the two rects, is added to the deviation so that - // a maximum overlap results in a deviation of zero. - float xSmallerRange = Math.min(child.getWidth(), parent.getWidth()); - float ySmallerRange = Math.min(child.getHeight(), parent.getHeight()); - float xOverlap - = Math.min(parentRight, childRight) - - Math.max(parentPos.x, childPos.x); - float yOverlap - = Math.min(parentBottom, childBottom) - - Math.max(parentPos.y, childPos.y); - float xDeviation = xSmallerRange - xOverlap; - float yDeviation = ySmallerRange - yOverlap; + // The "amount of overlap" indicates how much of one display is within the other + // (considering one axis only). It's zero if they only share an edge and + // negative if they're away from each other. + // A zero or negative overlap does not make a parenting ineligible, because we + // allow for attaching at the corner and for floating point error. + float xOverlap = + Math.min(parentRight, childRight) - Math.max(parentPos.x, childPos.x); + float yOverlap = + Math.min(parentBottom, childBottom) - Math.max(parentPos.y, childPos.y); + float xDeviation, yDeviation; float offset; int pos; - if (xDeviation <= yDeviation) { + if (Math.abs(xOverlap) > Math.abs(yOverlap)) { + // Deviation in each dimension is a penalty in the potential parenting. To + // get the X deviation, overlap is subtracted from the lesser width so that + // a maximum overlap results in a deviation of zero. + // Note that because xOverlap is *subtracted* from the lesser width, no + // overlap in X becomes a *penalty* if we are attaching on the top+bottom + // edges. + // + // The Y deviation is simply the distance from the clamping edges. + // + // Treatment of the X and Y deviations are swapped for + // POSITION_LEFT/POSITION_RIGHT attachments in the "else" block below. + xDeviation = Math.min(child.getWidth(), parent.getWidth()) - xOverlap; if (childPos.y < parentPos.y) { yDeviation = childBottom - parentPos.y; pos = POSITION_TOP; @@ -299,6 +297,7 @@ public final class DisplayTopology implements Parcelable { } offset = childPos.x - parentPos.x; } else { + yDeviation = Math.min(child.getHeight(), parent.getHeight()) - yOverlap; if (childPos.x < parentPos.x) { xDeviation = childRight - parentPos.x; pos = POSITION_LEFT; diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java index 34c88e91a979..8da630c95135 100644 --- a/core/java/android/hardware/input/InputSettings.java +++ b/core/java/android/hardware/input/InputSettings.java @@ -28,6 +28,7 @@ import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag; import static com.android.hardware.input.Flags.mouseScrollingAcceleration; import static com.android.hardware.input.Flags.mouseReverseVerticalScrolling; import static com.android.hardware.input.Flags.mouseSwapPrimaryButton; +import static com.android.hardware.input.Flags.pointerAcceleration; import static com.android.hardware.input.Flags.touchpadSystemGestureDisable; import static com.android.hardware.input.Flags.touchpadThreeFingerTapShortcut; import static com.android.hardware.input.Flags.touchpadVisualizer; @@ -418,6 +419,15 @@ public class InputSettings { } /** + * Returns true if the feature flag for the pointer acceleration toggle is + * enabled. + * @hide + */ + public static boolean isPointerAccelerationFeatureFlagEnabled() { + return pointerAcceleration(); + } + + /** * Returns true if the touchpad visualizer is allowed to appear. * * @param context The application context. @@ -720,6 +730,47 @@ public class InputSettings { } /** + * Whether cursor acceleration is enabled or not for connected mice. + * + * @param context The application context. + * + * @hide + */ + public static boolean isMousePointerAccelerationEnabled(@NonNull Context context) { + if (!isPointerAccelerationFeatureFlagEnabled()) { + return false; + } + + return Settings.System.getIntForUser(context.getContentResolver(), + Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED, 1, UserHandle.USER_CURRENT) + == 1; + } + + /** + * Sets whether mouse acceleration is enabled. + * + * When enabled, the mouse cursor moves farther when it is moved faster. + * When disabled, the mouse cursor speed becomes directly proportional to + * the speed at which the mouse is moved. + * + * @param context The application context. + * @param enabled Will enable mouse acceleration if true, disable it if + * false. + * @hide + */ + @RequiresPermission(Manifest.permission.WRITE_SETTINGS) + public static void setMouseAccelerationEnabled(@NonNull Context context, + boolean enabled) { + if (!isPointerAccelerationFeatureFlagEnabled()) { + return; + } + Settings.System.putIntForUser(context.getContentResolver(), + Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED, enabled ? 1 : 0, + UserHandle.USER_CURRENT); + } + + + /** * Whether Accessibility bounce keys feature is enabled. * * <p> diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java index 1cc64f9edc12..4c907502b8c8 100644 --- a/core/java/android/inputmethodservice/InlineSuggestionSession.java +++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java @@ -29,6 +29,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import android.view.autofill.AutofillFeatureFlags; import android.view.autofill.AutofillId; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; @@ -81,6 +82,7 @@ class InlineSuggestionSession { @Nullable private Boolean mPreviousResponseIsEmpty; + private boolean mAlwaysNotifyAutofill = false; /** * Indicates whether {@link #makeInlineSuggestionRequestUncheck()} has been called or not, @@ -105,6 +107,7 @@ class InlineSuggestionSession { mResponseConsumer = responseConsumer; mInlineSuggestionSessionController = inlineSuggestionSessionController; mMainThreadHandler = mainThreadHandler; + mAlwaysNotifyAutofill = AutofillFeatureFlags.isImproveFillDialogEnabled(); } @MainThread @@ -176,6 +179,9 @@ class InlineSuggestionSession { try { final InlineSuggestionsRequest request = mRequestSupplier.apply( mRequestInfo.getUiExtras()); + if (mAlwaysNotifyAutofill) { + mResponseCallback = new InlineSuggestionsResponseCallbackImpl(this); + } if (request == null) { if (DEBUG) { Log.d(TAG, "onCreateInlineSuggestionsRequest() returned null request"); @@ -184,7 +190,9 @@ class InlineSuggestionSession { } else { request.setHostInputToken(mHostInputTokenSupplier.get()); request.filterContentTypes(); - mResponseCallback = new InlineSuggestionsResponseCallbackImpl(this); + if (!mAlwaysNotifyAutofill) { + mResponseCallback = new InlineSuggestionsResponseCallbackImpl(this); + } mCallback.onInlineSuggestionsRequest(request, mResponseCallback); } } catch (RemoteException e) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2ad6669979b7..6e58780ac7a7 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6362,6 +6362,16 @@ public final class Settings { public static final String MOUSE_SCROLLING_ACCELERATION = "mouse_scrolling_acceleration"; /** + * Whether mouse acceleration is enabled. + * + * When enabled, the mouse cursor will accelerate as the mouse moves faster. + * + * @hide + */ + public static final String MOUSE_POINTER_ACCELERATION_ENABLED = + "mouse_pointer_acceleration_enabled"; + + /** * Pointer fill style, specified by * {@link android.view.PointerIcon.PointerIconVectorStyleFill} constants. * @@ -6610,6 +6620,7 @@ public final class Settings { PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE); PRIVATE_SETTINGS.add(MOUSE_REVERSE_VERTICAL_SCROLLING); PRIVATE_SETTINGS.add(MOUSE_SWAP_PRIMARY_BUTTON); + PRIVATE_SETTINGS.add(MOUSE_POINTER_ACCELERATION_ENABLED); PRIVATE_SETTINGS.add(PREFERRED_REGION); PRIVATE_SETTINGS.add(MOUSE_SCROLLING_ACCELERATION); } diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java index d476d960890b..a086bf7f8b08 100644 --- a/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java +++ b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java @@ -30,25 +30,26 @@ import android.security.Flags; @FlaggedApi(Flags.FLAG_AAPM_API) @SystemApi public final class AdvancedProtectionFeature implements Parcelable { - private final int mId; + private final String mId; /** * Create an object identifying an Advanced Protection feature for AdvancedProtectionManager - * @param id Feature identifier. It is used by Settings screens to display information about - * this feature. + * @param id A unique ID to identify this feature. It is used by Settings screens to display + * information about this feature. */ - public AdvancedProtectionFeature(@AdvancedProtectionManager.FeatureId int id) { + public AdvancedProtectionFeature(@NonNull String id) { mId = id; } private AdvancedProtectionFeature(Parcel in) { - mId = in.readInt(); + mId = in.readString8(); } /** * @return the unique ID representing this feature */ - public int getId() { + @NonNull + public String getId() { return mId; } @@ -59,7 +60,7 @@ public final class AdvancedProtectionFeature implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mId); + dest.writeString8(mId); } @NonNull diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java index ea01fc98eda0..59628e8e69d7 100644 --- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java +++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java @@ -24,18 +24,17 @@ import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; -import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.StringDef; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; -import android.net.wifi.WifiManager; import android.os.Binder; import android.os.RemoteException; -import android.os.UserManager; import android.security.Flags; import android.util.Log; @@ -60,57 +59,54 @@ public final class AdvancedProtectionManager { private static final String TAG = "AdvancedProtectionMgr"; /** - * Advanced Protection's identifier for setting policies or restrictions in - * {@link DevicePolicyManager}. + * Advanced Protection's identifier for setting policies or restrictions in DevicePolicyManager. * * @hide */ public static final String ADVANCED_PROTECTION_SYSTEM_ENTITY = "android.security.advancedprotection"; /** - * Feature identifier for disallowing connections to 2G networks. + * Feature identifier for disallowing 2G. * - * @see UserManager#DISALLOW_CELLULAR_2G * @hide */ @SystemApi - public static final int FEATURE_ID_DISALLOW_CELLULAR_2G = 0; + public static final String FEATURE_ID_DISALLOW_CELLULAR_2G = + "android.security.advancedprotection.feature_disallow_2g"; /** - * Feature identifier for disallowing installs of apps from unknown sources. + * Feature identifier for disallowing install of unknown sources. * - * @see UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY * @hide */ @SystemApi - public static final int FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = 1; + public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = + "android.security.advancedprotection.feature_disallow_install_unknown_sources"; /** - * Feature identifier for disallowing USB connections. + * Feature identifier for disallowing USB. * * @hide */ @SystemApi - public static final int FEATURE_ID_DISALLOW_USB = 2; + public static final String FEATURE_ID_DISALLOW_USB = + "android.security.advancedprotection.feature_disallow_usb"; /** - * Feature identifier for disallowing connections to Wi-Fi Wired Equivalent Privacy (WEP) - * networks. + * Feature identifier for disallowing WEP. * - * @see WifiManager#isWepSupported() * @hide */ @SystemApi - public static final int FEATURE_ID_DISALLOW_WEP = 3; + public static final String FEATURE_ID_DISALLOW_WEP = + "android.security.advancedprotection.feature_disallow_wep"; /** - * Feature identifier for enabling the Memory Tagging Extension (MTE). MTE is a CPU extension - * that allows to protect against certain classes of security problems at a small runtime - * performance cost overhead. + * Feature identifier for enabling MTE. * - * @see DevicePolicyManager#setMtePolicy(int) * @hide */ @SystemApi - public static final int FEATURE_ID_ENABLE_MTE = 4; + public static final String FEATURE_ID_ENABLE_MTE = + "android.security.advancedprotection.feature_enable_mte"; /** @hide */ - @IntDef(prefix = { "FEATURE_ID_" }, value = { + @StringDef(prefix = { "FEATURE_ID_" }, value = { FEATURE_ID_DISALLOW_CELLULAR_2G, FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES, FEATURE_ID_DISALLOW_USB, @@ -120,7 +116,7 @@ public final class AdvancedProtectionManager { @Retention(RetentionPolicy.SOURCE) public @interface FeatureId {} - private static final Set<Integer> ALL_FEATURE_IDS = Set.of( + private static final Set<String> ALL_FEATURE_IDS = Set.of( FEATURE_ID_DISALLOW_CELLULAR_2G, FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES, FEATURE_ID_DISALLOW_USB, @@ -139,6 +135,9 @@ public final class AdvancedProtectionManager { * Output: Nothing. * * @hide */ + @SystemApi + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + @FlaggedApi(android.security.Flags.FLAG_AAPM_API) public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG = "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG"; @@ -148,6 +147,7 @@ public final class AdvancedProtectionManager { * * @hide */ @FeatureId + @SystemApi public static final String EXTRA_SUPPORT_DIALOG_FEATURE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE"; @@ -157,41 +157,37 @@ public final class AdvancedProtectionManager { * * @hide */ @SupportDialogType + @SystemApi public static final String EXTRA_SUPPORT_DIALOG_TYPE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE"; /** - * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating an unknown action was blocked by - * advanced protection, hence the support dialog should display a default explanation. - * - * @hide */ - public static final int SUPPORT_DIALOG_TYPE_UNKNOWN = 0; - - /** * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user performed an action that was * blocked by advanced protection. * * @hide */ - public static final int SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION = 1; + @SystemApi + public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION = + "android.security.advancedprotection.type_blocked_interaction"; /** * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user pressed on a setting toggle * that was disabled by advanced protection. * * @hide */ - public static final int SUPPORT_DIALOG_TYPE_DISABLED_SETTING = 2; + @SystemApi + public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING = + "android.security.advancedprotection.type_disabled_setting"; /** @hide */ - @IntDef(prefix = { "SUPPORT_DIALOG_TYPE_" }, value = { - SUPPORT_DIALOG_TYPE_UNKNOWN, + @StringDef(prefix = { "SUPPORT_DIALOG_TYPE_" }, value = { SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION, SUPPORT_DIALOG_TYPE_DISABLED_SETTING, }) @Retention(RetentionPolicy.SOURCE) public @interface SupportDialogType {} - private static final Set<Integer> ALL_SUPPORT_DIALOG_TYPES = Set.of( - SUPPORT_DIALOG_TYPE_UNKNOWN, + private static final Set<String> ALL_SUPPORT_DIALOG_TYPES = Set.of( SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION, SUPPORT_DIALOG_TYPE_DISABLED_SETTING); @@ -328,13 +324,15 @@ public final class AdvancedProtectionManager { * disabled by advanced protection. * @hide */ - public static @NonNull Intent createSupportIntent(@FeatureId int featureId, - @SupportDialogType int type) { + @SystemApi + public @NonNull Intent createSupportIntent(@NonNull @FeatureId String featureId, + @Nullable @SupportDialogType String type) { + Objects.requireNonNull(featureId); if (!ALL_FEATURE_IDS.contains(featureId)) { throw new IllegalArgumentException(featureId + " is not a valid feature ID. See" + " FEATURE_ID_* APIs."); } - if (!ALL_SUPPORT_DIALOG_TYPES.contains(type)) { + if (type != null && !ALL_SUPPORT_DIALOG_TYPES.contains(type)) { throw new IllegalArgumentException(type + " is not a valid type. See" + " SUPPORT_DIALOG_TYPE_* APIs."); } @@ -342,19 +340,21 @@ public final class AdvancedProtectionManager { Intent intent = new Intent(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG); intent.setFlags(FLAG_ACTIVITY_NEW_TASK); intent.putExtra(EXTRA_SUPPORT_DIALOG_FEATURE, featureId); - intent.putExtra(EXTRA_SUPPORT_DIALOG_TYPE, type); + if (type != null) { + intent.putExtra(EXTRA_SUPPORT_DIALOG_TYPE, type); + } return intent; } /** @hide */ - public static @NonNull Intent createSupportIntentForPolicyIdentifierOrRestriction( - @NonNull String identifier, @SupportDialogType int type) { + public @NonNull Intent createSupportIntentForPolicyIdentifierOrRestriction( + @NonNull String identifier, @Nullable @SupportDialogType String type) { Objects.requireNonNull(identifier); - if (!ALL_SUPPORT_DIALOG_TYPES.contains(type)) { + if (type != null && !ALL_SUPPORT_DIALOG_TYPES.contains(type)) { throw new IllegalArgumentException(type + " is not a valid type. See" + " SUPPORT_DIALOG_TYPE_* APIs."); } - final int featureId; + final String featureId; if (DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY.equals(identifier)) { featureId = FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES; } else if (DISALLOW_CELLULAR_2G.equals(identifier)) { diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index 4f741982081b..880622a1e121 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -221,13 +221,13 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { @Override public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes, - int[] hideTypes, int[] cancelTypes) { + int[] hideTypes, int[] cancelTypes, int[] transientTypes) { if (Flags.refactorInsetsController()) { - return super.setControl(control, showTypes, hideTypes, cancelTypes); + return super.setControl(control, showTypes, hideTypes, cancelTypes, transientTypes); } else { ImeTracing.getInstance().triggerClientDump("ImeInsetsSourceConsumer#setControl", mController.getHost().getInputMethodManager(), null /* icProto */); - if (!super.setControl(control, showTypes, hideTypes, cancelTypes)) { + if (!super.setControl(control, showTypes, hideTypes, cancelTypes, transientTypes)) { return false; } if (control == null && !mIsRequestedVisibleAwaitingLeash) { diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index b0813f3a98f6..c174fbe0bbcd 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -959,6 +959,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final @InsetsType int[] showTypes = new int[1]; final @InsetsType int[] hideTypes = new int[1]; final @InsetsType int[] cancelTypes = new int[1]; + final @InsetsType int[] transientTypes = new int[1]; ImeTracker.Token statsToken = null; // Ensure to update all existing source consumers @@ -984,7 +985,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // control may be null, but we still need to update the control to null if it got // revoked. - consumer.setControl(control, showTypes, hideTypes, cancelTypes); + consumer.setControl(control, showTypes, hideTypes, cancelTypes, transientTypes); } // Ensure to create source consumers if not available yet. @@ -992,7 +993,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation for (int i = mTmpControlArray.size() - 1; i >= 0; i--) { final InsetsSourceControl control = mTmpControlArray.valueAt(i); getSourceConsumer(control.getId(), control.getType()) - .setControl(control, showTypes, hideTypes, cancelTypes); + .setControl(control, showTypes, hideTypes, cancelTypes, transientTypes); } } @@ -1020,10 +1021,16 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation handlePendingControlRequest(statsToken); } else { if (showTypes[0] != 0) { - applyAnimation(showTypes[0], true /* show */, false /* fromIme */, statsToken); + applyAnimation(showTypes[0], true /* show */, false /* fromIme */, + false /* skipsCallbacks */, statsToken); } if (hideTypes[0] != 0) { - applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, statsToken); + applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, + // The animation of hiding transient types shouldn't be detected by the + // app. Otherwise, it might be able to react to the callbacks and cause + // flickering. + (hideTypes[0] & ~transientTypes[0]) == 0 /* skipsCallbacks */, + statsToken); } } } else { @@ -1033,7 +1040,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation ImeTracker.TYPE_SHOW, ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROLS_CHANGED, mHost.isHandlingPointerEvent() /* fromUser */); - applyAnimation(showTypes[0], true /* show */, false /* fromIme */, newStatsToken); + applyAnimation(showTypes[0], true /* show */, false /* fromIme */, + false /* skipsCallbacks */, newStatsToken); } if (hideTypes[0] != 0) { final var newStatsToken = @@ -1041,7 +1049,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROLS_CHANGED, mHost.isHandlingPointerEvent() /* fromUser */); - applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, newStatsToken); + applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, + // The animation of hiding transient types shouldn't be detected by the app. + // Otherwise, it might be able to react to the callbacks and cause + // flickering. + (hideTypes[0] & ~transientTypes[0]) == 0 /* skipsCallbacks */, + newStatsToken); } } @@ -1174,7 +1187,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // TODO(b/353463205) check if this is needed here ImeTracker.forLatency().onShown(statsToken, ActivityThread::currentApplication); } - applyAnimation(typesReady, true /* show */, fromIme, statsToken); + applyAnimation(typesReady, true /* show */, fromIme, false /* skipsCallbacks */, + statsToken); } /** @@ -1287,7 +1301,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation handlePendingControlRequest(statsToken); getImeSourceConsumer().removeSurface(); } - applyAnimation(typesReady, false /* show */, fromIme, statsToken); + applyAnimation(typesReady, false /* show */, fromIme, false /* skipsCallbacks */, + statsToken); } @Override @@ -2007,24 +2022,24 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme, - @Nullable ImeTracker.Token statsToken) { + boolean skipsCallbacks, @Nullable ImeTracker.Token statsToken) { // TODO(b/166736352): We should only skip the animation of specific types, not all types. - boolean skipAnim = false; + boolean skipsAnim = false; if ((types & ime()) != 0) { final InsetsSourceControl imeControl = mImeSourceConsumer.getControl(); // Skip showing animation once that made by system for some reason. // (e.g. starting window with IME snapshot) if (imeControl != null) { - skipAnim = imeControl.getAndClearSkipAnimationOnce() && show + skipsAnim = imeControl.getAndClearSkipAnimationOnce() && show && mImeSourceConsumer.hasViewFocusWhenWindowFocusGain(); } } - applyAnimation(types, show, fromIme, skipAnim, statsToken); + applyAnimation(types, show, fromIme, skipsAnim, skipsCallbacks, statsToken); } @VisibleForTesting public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme, - boolean skipAnim, @Nullable ImeTracker.Token statsToken) { + boolean skipsAnim, boolean skipsCallbacks, @Nullable ImeTracker.Token statsToken) { if (types == 0) { // nothing to animate. if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate. Stopping here"); @@ -2040,7 +2055,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks(); final InternalAnimationControlListener listener = new InternalAnimationControlListener( show, hasAnimationCallbacks, types, mHost.getSystemBarsBehavior(), - skipAnim || mAnimationsDisabled, mHost.dipToPx(FLOATING_IME_BOTTOM_INSET_DP), + skipsAnim || mAnimationsDisabled, mHost.dipToPx(FLOATING_IME_BOTTOM_INSET_DP), mLoggingListener, mJankContext); // We are about to playing the default animation (show/hide). Passing a null frame indicates @@ -2050,7 +2065,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation listener /* insetsAnimationSpec */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, - !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken, + !hasAnimationCallbacks || skipsCallbacks /* useInsetsAnimationThread */, statsToken, false /* fromPredictiveBack */); } @@ -2173,12 +2188,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation new InsetsSourceControl(ID_IME_CAPTION_BAR, captionBar(), null /* leash */, false /* initialVisible */, new Point(), Insets.NONE), - new int[1], new int[1], new int[1]); + new int[1], new int[1], new int[1], new int[1]); } else { mState.removeSource(ID_IME_CAPTION_BAR); InsetsSourceConsumer sourceConsumer = mSourceConsumers.get(ID_IME_CAPTION_BAR); if (sourceConsumer != null) { - sourceConsumer.setControl(null, new int[1], new int[1], new int[1]); + sourceConsumer.setControl(null, new int[1], new int[1], new int[1], new int[1]); } } mHost.notifyInsetsChanged(); diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 17f33c1af4ed..e8e66210bca6 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -130,7 +130,10 @@ public class InsetsSourceConsumer { * @return Whether the control has changed from the server */ public boolean setControl(@Nullable InsetsSourceControl control, - @InsetsType int[] showTypes, @InsetsType int[] hideTypes, int[] cancelTypes) { + @InsetsType int[] showTypes, + @InsetsType int[] hideTypes, + @InsetsType int[] cancelTypes, + @InsetsType int[] transientTypes) { if (Objects.equals(mSourceControl, control)) { if (mSourceControl != null && mSourceControl != control) { mSourceControl.release(SurfaceControl::release); @@ -185,6 +188,9 @@ public class InsetsSourceConsumer { } else { hideTypes[0] |= mType; } + if (lastControl != null && lastControl.isFake()) { + transientTypes[0] |= mType; + } } else { // We are gaining control, but don't need to run an animation. // However make sure that the leash visibility is still up to date. diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index acbd95bf6810..7f2f0e8863df 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -142,6 +142,10 @@ public class InsetsSourceControl implements Parcelable { return mInsetsHint; } + public boolean isFake() { + return mLeash == null && Insets.NONE.equals(mInsetsHint); + } + public void setSkipAnimationOnce(boolean skipAnimation) { mSkipAnimationOnce = skipAnimation; } diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java index d527007e586e..206e47f13890 100644 --- a/core/java/android/view/autofill/AutofillFeatureFlags.java +++ b/core/java/android/view/autofill/AutofillFeatureFlags.java @@ -16,6 +16,8 @@ package android.view.autofill; +import static android.service.autofill.Flags.improveFillDialogAconfig; + import android.annotation.SuppressLint; import android.annotation.TestApi; import android.provider.DeviceConfig; @@ -662,7 +664,7 @@ public class AutofillFeatureFlags { public static boolean isImproveFillDialogEnabled() { // TODO(b/266379948): Add condition for checking whether device has PCC first - return DeviceConfig.getBoolean( + return improveFillDialogAconfig() && DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_AUTOFILL, DEVICE_CONFIG_IMPROVE_FILL_DIALOG_ENABLED, DEFAULT_IMPROVE_FILL_DIALOG_ENABLED); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index eaa8f60e83a7..e904345fb03e 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -992,23 +992,7 @@ public final class InputMethodManager { private void setCurrentRootViewLocked(ViewRootImpl rootView) { final boolean wasEmpty = mCurRootView == null; if (Flags.refactorInsetsController() && !wasEmpty && mCurRootView != rootView) { - final int softInputMode = mCurRootView.mWindowAttributes.softInputMode; - final int state = - softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE; - if (state == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) { - // when losing input focus (e.g., by going to another window), we reset the - // requestedVisibleTypes of WindowInsetsController by hiding the IME - final var statsToken = ImeTracker.forLogging().onStart( - ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT, - SoftInputShowHideReason.HIDE_WINDOW_LOST_FOCUS, - false /* fromUser */); - if (DEBUG) { - Log.d(TAG, "setCurrentRootViewLocked, hiding IME because " - + "of STATE_ALWAYS_HIDDEN"); - } - mCurRootView.getInsetsController().hide(WindowInsets.Type.ime(), - false /* fromIme */, statsToken); - } + onImeFocusLost(mCurRootView); } mImeDispatcher.switchRootView(mCurRootView, rootView); @@ -1019,6 +1003,26 @@ public final class InputMethodManager { } } + private void onImeFocusLost(@NonNull ViewRootImpl previousRootView) { + final int softInputMode = previousRootView.mWindowAttributes.softInputMode; + final int state = + softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE; + if (state == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) { + // when losing input focus (e.g., by going to another window), we reset the + // requestedVisibleTypes of WindowInsetsController by hiding the IME + final var statsToken = ImeTracker.forLogging().onStart( + ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT, + SoftInputShowHideReason.HIDE_WINDOW_LOST_FOCUS, + false /* fromUser */); + if (DEBUG) { + Log.d(TAG, "onImeFocusLost, hiding IME because " + + "of STATE_ALWAYS_HIDDEN"); + } + previousRootView.getInsetsController().hide(WindowInsets.Type.ime(), + false /* fromIme */, statsToken); + } + } + /** @hide */ public DelegateImpl getDelegate() { return mDelegate; diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig index d5ba32cafebd..4b5adfcc2c9b 100644 --- a/core/java/android/window/flags/responsible_apis.aconfig +++ b/core/java/android/window/flags/responsible_apis.aconfig @@ -75,4 +75,9 @@ flag { bug: "362575865" } - +flag { + name: "bal_strict_mode_grace_period" + namespace: "responsible_apis" + description: "Strict mode violation triggered by grace period usage" + bug: "384807495" +} diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java index 751cfde70164..b7537ed3f54b 100644 --- a/core/java/com/android/internal/widget/NotificationExpandButton.java +++ b/core/java/com/android/internal/widget/NotificationExpandButton.java @@ -149,8 +149,10 @@ public class NotificationExpandButton extends FrameLayout { setContentDescription(mContext.getText(contentDescriptionId)); mIconView.setImageDrawable(getContext().getDrawable(drawableId)); - // changing the expanded state can affect the number display - updateNumber(); + if (!notificationsRedesignTemplates()) { + // changing the expanded state can affect the number display + updateNumber(); + } } private void updateNumber() { @@ -189,6 +191,9 @@ public class NotificationExpandButton extends FrameLayout { } private boolean shouldShowNumber() { + if (notificationsRedesignTemplates()) { + return mNumber > 1; + } return !mExpanded && mNumber > 1; } @@ -230,7 +235,7 @@ public class NotificationExpandButton extends FrameLayout { /** * Sets the number shown inside the expand button. - * This only appears when the expand button is collapsed, and when greater than 1. + * This only appears when {@link this#shouldShowNumber()} is true. */ @RemotableViewMethod public void setNumber(int number) { diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto index dd9bfa51c634..0d99200f4e6f 100644 --- a/core/proto/android/providers/settings/system.proto +++ b/core/proto/android/providers/settings/system.proto @@ -228,6 +228,7 @@ message SystemSettingsProto { optional SettingProto reverse_vertical_scrolling = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto swap_primary_button = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto scrolling_acceleration = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pointer_acceleration_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Mouse mouse = 38; diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 3ef3dfdc0183..d7c4212dda07 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -73,6 +73,7 @@ android_test { "frameworks-core-util-lib", "mockwebserver", "guava", + "guava-android-testlib", "android.app.usage.flags-aconfig-java", "android.view.accessibility.flags-aconfig-java", "androidx.core_core", diff --git a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java index 931d64640ea2..f1925bb74981 100644 --- a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java +++ b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java @@ -29,6 +29,8 @@ import android.os.IBinder; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.google.common.testing.EqualsTester; + import org.junit.Test; import org.junit.runner.RunWith; @@ -122,12 +124,16 @@ public class BackgroundStartPrivilegesTest { @Test public void backgroundStartPrivilege_equals_works() { - assertThat(NONE).isEqualTo(NONE); - assertThat(ALLOW_BAL).isEqualTo(ALLOW_BAL); - assertThat(ALLOW_FGS).isEqualTo(ALLOW_FGS); - assertThat(BSP_ALLOW_A).isEqualTo(BSP_ALLOW_A); - assertThat(NONE).isNotEqualTo(ALLOW_BAL); - assertThat(ALLOW_FGS).isNotEqualTo(ALLOW_BAL); - assertThat(BSP_ALLOW_A).isNotEqualTo(BSP_ALLOW_B); + Binder token = new Binder(); + Binder anotherToken = new Binder(); + new EqualsTester() + .addEqualityGroup(NONE) + .addEqualityGroup(ALLOW_BAL) + .addEqualityGroup(ALLOW_FGS) + .addEqualityGroup(BackgroundStartPrivileges.allowBackgroundActivityStarts(token), + BackgroundStartPrivileges.allowBackgroundActivityStarts(token)) + .addEqualityGroup( + BackgroundStartPrivileges.allowBackgroundActivityStarts(anotherToken)) + .testEquals(); } } diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt index b7d2562c8dd0..008db5ef114e 100644 --- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt +++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt @@ -643,13 +643,65 @@ class DisplayTopologyTest { verifyDisplay( root.children[0], id = 1, width = 30f, height = 30f, POSITION_RIGHT, offset = 10f, noOfChildren = 1) - // In the case of corner adjacency, we prefer a left/right attachment. verifyDisplay( root.children[0].children[0], id = 2, width = 29.5f, height = 30f, POSITION_BOTTOM, offset = 30f, noOfChildren = 0) } @Test + fun rearrange_preferLessShiftInOverlapDimension() { + val root = rearrangeRects( + // '*' represents overlap + // Clamping requires moving display 2 and 1 slightly to avoid overlap with 0. We should + // shift the minimal amount to avoid overlap - e.g. display 2 shifts left (10 pixels) + // rather than up (20 pixels). + // 222 + // 22*00 + // 22*00 + // 0**1 + // 111 + // 111 + RectF(20f, 10f, 50f, 40f), + RectF(30f, 30f, 60f, 60f), + RectF(0f, 0f, 30f, 30f), + ) + + verifyDisplay(root, id = 0, width = 30f, height = 30f, noOfChildren = 2) + verifyDisplay( + root.children[0], id = 1, width = 30f, height = 30f, POSITION_BOTTOM, offset = 10f, + noOfChildren = 0) + verifyDisplay( + root.children[1], id = 2, width = 30f, height = 30f, POSITION_LEFT, offset = -10f, + noOfChildren = 0) + } + + @Test + fun rearrange_doNotAttachCornerForShortOverlapOnLongEdgeBottom() { + val root = rearrangeRects( + RectF(0f, 0f, 1920f, 1080f), + RectF(1850f, 1070f, 3770f, 2150f), + ) + + verifyDisplay(root, id = 0, width = 1920f, height = 1080f, noOfChildren = 1) + verifyDisplay( + root.children[0], id = 1, width = 1920f, height = 1080f, POSITION_BOTTOM, + offset = 1850f, noOfChildren = 0) + } + + @Test + fun rearrange_doNotAttachCornerForShortOverlapOnLongEdgeLeft() { + val root = rearrangeRects( + RectF(0f, 0f, 1080f, 1920f), + RectF(-1070f, -1880f, 10f, 40f), + ) + + verifyDisplay(root, id = 0, width = 1080f, height = 1920f, noOfChildren = 1) + verifyDisplay( + root.children[0], id = 1, width = 1080f, height = 1920f, POSITION_LEFT, + offset = -1880f, noOfChildren = 0) + } + + @Test fun copy() { val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f, /* height= */ 600f, /* position= */ 0, /* offset= */ 0f) diff --git a/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java b/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java deleted file mode 100644 index 45864b01c795..000000000000 --- a/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java +++ /dev/null @@ -1,97 +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 android.security.advancedprotection; - -import static android.security.advancedprotection.AdvancedProtectionManager.ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG; -import static android.security.advancedprotection.AdvancedProtectionManager.EXTRA_SUPPORT_DIALOG_FEATURE; -import static android.security.advancedprotection.AdvancedProtectionManager.EXTRA_SUPPORT_DIALOG_TYPE; -import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G; -import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION; -import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING; -import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; - -import android.content.Intent; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public class AdvancedProtectionManagerTest { - private static final int FEATURE_ID_INVALID = -1; - private static final int SUPPORT_DIALOG_TYPE_INVALID = -1; - - @Test - public void testCreateSupportIntent_validFeature_validTypeUnknown_createsIntent() { - Intent intent = AdvancedProtectionManager.createSupportIntent( - FEATURE_ID_DISALLOW_CELLULAR_2G, SUPPORT_DIALOG_TYPE_UNKNOWN); - - assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction()); - assertEquals(FEATURE_ID_DISALLOW_CELLULAR_2G, intent.getIntExtra( - EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID)); - assertEquals(SUPPORT_DIALOG_TYPE_UNKNOWN, intent.getIntExtra(EXTRA_SUPPORT_DIALOG_TYPE, - SUPPORT_DIALOG_TYPE_INVALID)); - } - - @Test - public void testCreateSupportIntent_validFeature_validTypeBlockedInteraction_createsIntent() { - Intent intent = AdvancedProtectionManager.createSupportIntent( - FEATURE_ID_DISALLOW_CELLULAR_2G, SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION); - - assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction()); - assertEquals(FEATURE_ID_DISALLOW_CELLULAR_2G, intent.getIntExtra( - EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID)); - assertEquals(SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION, intent.getIntExtra( - EXTRA_SUPPORT_DIALOG_TYPE, SUPPORT_DIALOG_TYPE_INVALID)); - } - - @Test - public void testCreateSupportIntent_validFeature_validTypeDisabledSetting_createsIntent() { - Intent intent = AdvancedProtectionManager.createSupportIntent( - FEATURE_ID_DISALLOW_CELLULAR_2G, SUPPORT_DIALOG_TYPE_DISABLED_SETTING); - - assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction()); - assertEquals(FEATURE_ID_DISALLOW_CELLULAR_2G, intent.getIntExtra( - EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID)); - assertEquals(SUPPORT_DIALOG_TYPE_DISABLED_SETTING, intent.getIntExtra( - EXTRA_SUPPORT_DIALOG_TYPE, SUPPORT_DIALOG_TYPE_INVALID)); - } - - @Test - public void testCreateSupportIntent_validFeature_invalidType_throwsIllegalArgument() { - assertThrows(IllegalArgumentException.class, () -> - AdvancedProtectionManager.createSupportIntent(FEATURE_ID_DISALLOW_CELLULAR_2G, - SUPPORT_DIALOG_TYPE_INVALID)); - } - - @Test - public void testCreateSupportIntent_invalidFeature_validType_throwsIllegalArgument() { - assertThrows(IllegalArgumentException.class, () -> - AdvancedProtectionManager.createSupportIntent(FEATURE_ID_INVALID, - SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION)); - } - - @Test - public void testCreateSupportIntent_invalidFeature_invalidType_throwsIllegalArgument() { - assertThrows(IllegalArgumentException.class, () -> - AdvancedProtectionManager.createSupportIntent(FEATURE_ID_INVALID, - SUPPORT_DIALOG_TYPE_INVALID)); - } -} diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index 248db65d7435..4a54f6b12c9d 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -133,7 +133,7 @@ public class ImeInsetsSourceConsumerTest { // Called once through the show flow. verify(mController).applyAnimation( eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */, - eq(statsToken)); + eq(false) /* skipsCallbacks */, eq(statsToken)); // set control and verify visibility is applied. InsetsSourceControl control = new InsetsSourceControl(ID_IME, @@ -142,10 +142,10 @@ public class ImeInsetsSourceConsumerTest { // IME show animation should be triggered when control becomes available. verify(mController).applyAnimation( eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */, - and(not(eq(statsToken)), notNull())); + eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull())); verify(mController, never()).applyAnimation( eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */, - and(not(eq(statsToken)), notNull())); + eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull())); }); } @@ -163,7 +163,7 @@ public class ImeInsetsSourceConsumerTest { // Called once through the show flow. verify(mController).applyAnimation( eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */, - eq(statsToken)); + eq(false) /* skipsCallbacks */, eq(statsToken)); // Clear previous invocations to verify this is never called with control without leash. clearInvocations(mController); @@ -175,10 +175,10 @@ public class ImeInsetsSourceConsumerTest { // as we have no leash. verify(mController, never()).applyAnimation( eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */, - and(not(eq(statsToken)), notNull())); + eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull())); verify(mController, never()).applyAnimation( eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */, - and(not(eq(statsToken)), notNull())); + eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull())); // set control with leash and verify visibility is applied. InsetsSourceControl controlWithLeash = new InsetsSourceControl(ID_IME, @@ -187,10 +187,10 @@ public class ImeInsetsSourceConsumerTest { // IME show animation should be triggered when control with leash becomes available. verify(mController).applyAnimation( eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */, - and(not(eq(statsToken)), notNull())); + eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull())); verify(mController, never()).applyAnimation( eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */, - and(not(eq(statsToken)), notNull())); + eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull())); }); } @@ -223,7 +223,8 @@ public class ImeInsetsSourceConsumerTest { // Called once through the show flow. verify(mController).applyAnimation(eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */, - eq(false) /* skipAnim */, eq(statsToken)); + eq(false) /* skipsAnim */, eq(false) /* skipsCallbacks */, + eq(statsToken)); } // set control and verify visibility is applied. @@ -241,7 +242,8 @@ public class ImeInsetsSourceConsumerTest { // so the statsToken won't match. verify(mController).applyAnimation(eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */, - eq(expectSkipAnim) /* skipAnim */, and(not(eq(statsToken)), notNull())); + eq(expectSkipAnim) /* skipsAnim */, eq(false) /* skipsCallbacks */, + and(not(eq(statsToken)), notNull())); } // If previously hasViewFocus is false, verify when requesting the IME visible next @@ -252,14 +254,16 @@ public class ImeInsetsSourceConsumerTest { // Called once through the show flow. verify(mController).applyAnimation(eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */, - eq(false) /* skipAnim */, eq(statsTokenNext)); + eq(false) /* skipsAnim */, eq(false) /* skipsCallbacks */, + eq(statsTokenNext)); mController.onControlsChanged(new InsetsSourceControl[]{ control }); // Verify IME show animation should be triggered when control becomes available and // the animation will be skipped by getAndClearSkipAnimationOnce invoked. verify(control).getAndClearSkipAnimationOnce(); verify(mController).applyAnimation(eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */, - eq(true) /* skipAnim */, and(not(eq(statsToken)), notNull())); + eq(true) /* skipsAnim */, eq(false) /* skipsCallbacks */, + and(not(eq(statsToken)), notNull())); } }); } diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index d7f6a29d7c86..905d897e9ab0 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -100,14 +100,14 @@ public class InsetsAnimationControlImplTest { topConsumer.setControl( new InsetsSourceControl(ID_STATUS_BAR, WindowInsets.Type.statusBars(), mStatusLeash, true, new Point(0, 0), Insets.of(0, 100, 0, 0)), - new int[1], new int[1], new int[1]); + new int[1], new int[1], new int[1], new int[1]); InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(ID_NAVIGATION_BAR, WindowInsets.Type.navigationBars(), mInsetsState, mMockController); navConsumer.setControl( new InsetsSourceControl(ID_NAVIGATION_BAR, WindowInsets.Type.navigationBars(), mNavLeash, true, new Point(400, 0), Insets.of(0, 0, 100, 0)), - new int[1], new int[1], new int[1]); + new int[1], new int[1], new int[1], new int[1]); mMockController.setRequestedVisibleTypes(0, WindowInsets.Type.navigationBars()); navConsumer.applyLocalVisibilityOverride(); diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 3a8f7ee3d7c8..45d66e8ee3a9 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -117,7 +117,7 @@ public class InsetsSourceConsumerTest { mConsumer.setControl( new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash, true /* initialVisible */, new Point(), Insets.NONE), - new int[1], new int[1], new int[1]); + new int[1], new int[1], new int[1], new int[1]); } @Test @@ -129,7 +129,7 @@ public class InsetsSourceConsumerTest { newControl.setInsetsHint(Insets.of(0, 0, 0, 100)); int[] cancelTypes = {0}; - mConsumer.setControl(newControl, new int[1], new int[1], cancelTypes); + mConsumer.setControl(newControl, new int[1], new int[1], cancelTypes, new int[1]); assertEquals(statusBars(), cancelTypes[0]); }); @@ -196,7 +196,7 @@ public class InsetsSourceConsumerTest { @Test public void testRestore() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - mConsumer.setControl(null, new int[1], new int[1], new int[1]); + mConsumer.setControl(null, new int[1], new int[1], new int[1], new int[1]); mSurfaceParamsApplied = false; mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars()); assertFalse(mSurfaceParamsApplied); @@ -204,7 +204,7 @@ public class InsetsSourceConsumerTest { mConsumer.setControl( new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash, true /* initialVisible */, new Point(), Insets.NONE), - new int[1], hideTypes, new int[1]); + new int[1], hideTypes, new int[1], new int[1]); assertEquals(statusBars(), hideTypes[0]); assertFalse(mRemoveSurfaceCalled); }); @@ -214,7 +214,7 @@ public class InsetsSourceConsumerTest { public void testRestore_noAnimation() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars()); - mConsumer.setControl(null, new int[1], new int[1], new int[1]); + mConsumer.setControl(null, new int[1], new int[1], new int[1], new int[1]); mLeash = new SurfaceControl.Builder(mSession) .setName("testSurface") .build(); @@ -223,7 +223,7 @@ public class InsetsSourceConsumerTest { mConsumer.setControl( new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash, false /* initialVisible */, new Point(), Insets.NONE), - new int[1], hideTypes, new int[1]); + new int[1], hideTypes, new int[1], new int[1]); assertTrue(mRemoveSurfaceCalled); assertEquals(0, hideTypes[0]); }); @@ -252,7 +252,7 @@ public class InsetsSourceConsumerTest { // Initial IME insets source control with its leash. imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash, false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1], - new int[1]); + new int[1], new int[1]); mSurfaceParamsApplied = false; // Verify when the app requests controlling show IME animation, the IME leash @@ -262,7 +262,7 @@ public class InsetsSourceConsumerTest { assertEquals(ANIMATION_TYPE_USER, insetsController.getAnimationType(ime())); imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash, true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1], - new int[1]); + new int[1], new int[1]); assertFalse(mSurfaceParamsApplied); }); } diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java index 7e87462b64de..166b388d9b7b 100644 --- a/media/java/android/media/quality/MediaQualityManager.java +++ b/media/java/android/media/quality/MediaQualityManager.java @@ -276,16 +276,19 @@ public final class MediaQualityManager { /** * Sets preferred default picture profile. * - * @param id the ID of the default profile. {@code null} to unset the default profile. + * @param pictureProfileId the ID of the default profile. {@code null} to unset the default + * profile. * @return {@code true} if it's set successfully; {@code false} otherwise. * + * @see PictureProfile#getProfileId() + * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) - public boolean setDefaultPictureProfile(@Nullable String id) { + public boolean setDefaultPictureProfile(@Nullable String pictureProfileId) { try { - return mService.setDefaultPictureProfile(id, mUserHandle); + return mService.setDefaultPictureProfile(pictureProfileId, mUserHandle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -467,16 +470,19 @@ public final class MediaQualityManager { /** * Sets preferred default sound profile. * - * @param id the ID of the default profile. {@code null} to unset the default profile. + * @param soundProfileId the ID of the default profile. {@code null} to unset the default + * profile. * @return {@code true} if it's set successfully; {@code false} otherwise. * + * @see SoundProfile#getProfileId() + * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE) - public boolean setDefaultSoundProfile(@Nullable String id) { + public boolean setDefaultSoundProfile(@Nullable String soundProfileId) { try { - return mService.setDefaultSoundProfile(id, mUserHandle); + return mService.setDefaultSoundProfile(soundProfileId, mUserHandle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index b4afb7d8cd4c..7374f80fd9db 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -444,12 +444,23 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } /** - * @return {@code true} if {@code cachedBluetoothDevice} is hearing aid device + * @return {@code true} if {@code cachedBluetoothDevice} is hearing aid device. + * @deprecated use {@link #isHearingDevice() } + * // TODO: b/385679160 - Target to deprecate it and replace with #isHearingDevice() */ + @Deprecated public boolean isHearingAidDevice() { return mHearingAidInfo != null; } + /** + * @return {@code true} if {@code cachedBluetoothDevice} support any of hearing device profile. + */ + public boolean isHearingDevice() { + return getProfiles().stream().anyMatch( + p -> (p instanceof HearingAidProfile || p instanceof HapClientProfile)); + } + public int getDeviceSide() { return mHearingAidInfo != null ? mHearingAidInfo.getSide() : HearingAidInfo.DeviceSide.SIDE_INVALID; @@ -910,12 +921,33 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } } + /** + * Checks if the device is connected to the specified Bluetooth profile. + * + * @param profile The Bluetooth profile to check. + * @return {@code true} if the device is connected to the profile. + */ public boolean isConnectedProfile(LocalBluetoothProfile profile) { int status = getProfileConnectionState(profile); return status == BluetoothProfile.STATE_CONNECTED; } + /** + * Checks if the device is connected to the Bluetooth profile with the given ID. + * + * @param profileId The ID of the Bluetooth profile to check. + * @return {@code true} if the device is connected to the profile. + */ + public boolean isConnectedProfile(int profileId) { + for (LocalBluetoothProfile profile : getProfiles()) { + if (profile.getProfileId() == profileId) { + return isConnectedProfile(profile); + } + } + return false; + } + public boolean isBusy() { synchronized (mProfileLock) { for (LocalBluetoothProfile profile : mProfiles) { @@ -1891,13 +1923,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } /** - * @return {@code true} if {@code cachedBluetoothDevice} is LeAudio hearing aid device - */ - public boolean isConnectedLeAudioHearingAidDevice() { - return isConnectedHapClientDevice() && isConnectedLeAudioDevice(); - } - - /** * @return {@code true} if {@code cachedBluetoothDevice} is hearing aid device * * The device may be an ASHA hearing aid that supports {@link HearingAidProfile} or a LeAudio @@ -1908,6 +1933,13 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } /** + * @return {@code true} if {@code cachedBluetoothDevice} is LeAudio hearing aid device + */ + public boolean isConnectedLeAudioHearingAidDevice() { + return isConnectedHapClientDevice() && isConnectedLeAudioDevice(); + } + + /** * @return {@code true} if {@code cachedBluetoothDevice} is LeAudio device */ public boolean isConnectedLeAudioDevice() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java index b754706fb9a1..313013cdcf79 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java @@ -24,7 +24,10 @@ import android.bluetooth.le.ScanFilter; import android.content.Context; import android.util.Log; +import androidx.annotation.NonNull; + import com.android.internal.annotations.VisibleForTesting; +import com.android.settingslib.flags.Flags; import java.sql.Timestamp; import java.util.ArrayList; @@ -46,7 +49,7 @@ public class CachedBluetoothDeviceManager { private final LocalBluetoothManager mBtManager; @VisibleForTesting - final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>(); + final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>(); @VisibleForTesting HearingAidDeviceManager mHearingAidDeviceManager; @VisibleForTesting @@ -192,6 +195,20 @@ public class CachedBluetoothDeviceManager { } /** + * Notifies the connection status if device is hearing device. + * + * @param device The {@link CachedBluetoothDevice} need to be hearing device + */ + public synchronized void notifyHearingDevicesConnectionStatusChangedIfNeeded( + @NonNull CachedBluetoothDevice device) { + if (!device.isHearingDevice()) { + return; + } + + mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); + } + + /** * Search for existing sub device {@link CachedBluetoothDevice}. * * @param device the address of the Bluetooth device @@ -388,8 +405,14 @@ public class CachedBluetoothDeviceManager { /** Handles when the device been set as active/inactive. */ public synchronized void onActiveDeviceChanged(CachedBluetoothDevice cachedBluetoothDevice) { - if (cachedBluetoothDevice.isHearingAidDevice()) { + if (cachedBluetoothDevice == null) { + return; + } + if (cachedBluetoothDevice.isHearingDevice()) { mHearingAidDeviceManager.onActiveDeviceChanged(cachedBluetoothDevice); + if (Flags.hearingDeviceSetConnectionStatusReport()) { + mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); + } } } @@ -421,6 +444,14 @@ public class CachedBluetoothDeviceManager { mainDevice.unpair(); mainDevice.setSubDevice(null); } + + // TODO: b/386121967 - Should change to use isHearingDevice but mProfile get clear here. + // Need to consider where to put this logic when using isHearingDevice() + if (device.isHearingAidDevice()) { + if (Flags.hearingDeviceSetConnectionStatusReport()) { + mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); + } + } } /** @@ -579,6 +610,11 @@ public class CachedBluetoothDeviceManager { return mOngoingSetMemberPair != null && mOngoingSetMemberPair.equals(device); } + @NonNull + public HearingAidDeviceManager getHearingAidDeviceManager() { + return mHearingAidDeviceManager; + } + private void log(String msg) { if (DEBUG) { Log.d(TAG, msg); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java index ad34e837f508..b2c279466ee4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java @@ -15,7 +15,11 @@ */ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothDevice.BOND_BONDED; + +import android.annotation.CallbackExecutor; import android.bluetooth.BluetoothCsipSetCoordinator; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHapClient; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; @@ -30,15 +34,25 @@ import android.provider.Settings; import android.util.FeatureFlagUtils; import android.util.Log; +import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.collection.ArraySet; + import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.RoutingValue; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; /** - * HearingAidDeviceManager manages the set of remote HearingAid(ASHA) Bluetooth devices. + * HearingAidDeviceManager manages the set of remote bluetooth hearing devices. */ public class HearingAidDeviceManager { private static final String TAG = "HearingAidDeviceManager"; @@ -49,6 +63,12 @@ public class HearingAidDeviceManager { private final LocalBluetoothManager mBtManager; private final List<CachedBluetoothDevice> mCachedDevices; private final HearingAidAudioRoutingHelper mRoutingHelper; + private static final Map<ConnectionStatusListener, Executor> + mConnectionStatusListeners = new ConcurrentHashMap<>(); + @ConnectionStatus + private int mDevicesConnectionStatus = ConnectionStatus.NO_DEVICE_BONDED; + private boolean mInitialDevicesConnectionStatusUpdate = false; + HearingAidDeviceManager(Context context, LocalBluetoothManager localBtManager, List<CachedBluetoothDevice> CachedDevices) { mContext = context; @@ -68,6 +88,191 @@ public class HearingAidDeviceManager { mRoutingHelper = routingHelper; } + /** + * Defines the connection status for hearing devices. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + ConnectionStatus.NO_DEVICE_BONDED, + ConnectionStatus.DISCONNECTED, + ConnectionStatus.CONNECTED, + ConnectionStatus.CONNECTING_OR_DISCONNECTING, + ConnectionStatus.ACTIVE + }) + public @interface ConnectionStatus { + int NO_DEVICE_BONDED = -1; + int DISCONNECTED = 0; + int CONNECTED = 1; + int CONNECTING_OR_DISCONNECTING = 2; + int ACTIVE = 3; + } + /** + * Interface for connection status listener. + */ + public interface ConnectionStatusListener { + /** + * Callback when hearing devices connection status change. + * + * <p>devices here means singular device or binaural device. + * E.g. One of hearing device is in CONNECTED status and another is in DISCONNECTED, + * it will callback CONNECTED status. + * + * @param status Updated {@link ConnectionStatus} + */ + void onDevicesConnectionStatusChanged(@ConnectionStatus int status); + } + + /** + * Registers a listener to be notified of connection status changes. + * + * @param listener The listener to register. + * @param executor The executor on which the listener's callback will be run. + */ + public void registerConnectionStatusListener( + @NonNull ConnectionStatusListener listener, + @NonNull @CallbackExecutor Executor executor) { + mConnectionStatusListeners.put(listener, executor); + } + + /** + * Unregisters a listener previously registered with + * {@link #registerConnectionStatusListener(ConnectionStatusListener, Executor)}. + * + * @param listener The listener to unregister. + */ + public void unregisterConnectionStatusListener( + @NonNull ConnectionStatusListener listener) { + mConnectionStatusListeners.remove(listener); + } + + private void notifyDevicesConnectionStatusChanged(int status) { + mConnectionStatusListeners.forEach((listener, executor) -> + executor.execute(() -> listener.onDevicesConnectionStatusChanged(status))); + } + + /** + * Updates the connection status of the hearing devices based on the currently bonded + * hearing aid devices. + */ + synchronized void notifyDevicesConnectionStatusChanged() { + final int prevVal = mDevicesConnectionStatus; + updateDevicesConnectionStatus(); + if (mDevicesConnectionStatus != prevVal) { + notifyDevicesConnectionStatusChanged(mDevicesConnectionStatus); + } + } + + private void updateDevicesConnectionStatus() { + mInitialDevicesConnectionStatusUpdate = true; + // Add all hearing devices including sub and member into a set. + Set<CachedBluetoothDevice> allHearingDevices = mCachedDevices.stream() + .filter(d -> d.getBondState() == BluetoothDevice.BOND_BONDED + && d.isHearingDevice()) + .flatMap(d -> getAssociatedCachedDevice(d).stream()) + .collect(Collectors.toSet()); + + // Status sequence matters here. If one of the hearing devices is in previous + // ConnectionStatus, we will treat whole hearing devices is in this status. + // E.g. One of hearing device is in CONNECTED status and another is in DISCONNECTED + // status, the hearing devices connection status will notify CONNECTED status. + if (isConnectingOrDisconnectingConnectionStatus(allHearingDevices)) { + mDevicesConnectionStatus = ConnectionStatus.CONNECTING_OR_DISCONNECTING; + } else if (isActiveConnectionStatus(allHearingDevices)) { + mDevicesConnectionStatus = ConnectionStatus.ACTIVE; + } else if (isConnectedStatus(allHearingDevices)) { + mDevicesConnectionStatus = ConnectionStatus.CONNECTED; + } else if (isDisconnectedStatus(allHearingDevices)) { + mDevicesConnectionStatus = ConnectionStatus.DISCONNECTED; + } else { + mDevicesConnectionStatus = ConnectionStatus.NO_DEVICE_BONDED; + } + + if (DEBUG) { + Log.d(TAG, "updateDevicesConnectionStatus: " + mDevicesConnectionStatus); + } + } + + /** + * @return all the related CachedBluetoothDevices for this device. + */ + @NonNull + public Set<CachedBluetoothDevice> getAssociatedCachedDevice( + @NonNull CachedBluetoothDevice device) { + ArraySet<CachedBluetoothDevice> cachedDeviceSet = new ArraySet<>(); + cachedDeviceSet.add(device); + // Associated device should be added into memberDevice if it support CSIP profile. + Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice(); + if (!memberDevices.isEmpty()) { + cachedDeviceSet.addAll(memberDevices); + return cachedDeviceSet; + } + // If not support CSIP profile, it should be ASHA hearing device and added into subDevice. + CachedBluetoothDevice subDevice = device.getSubDevice(); + if (subDevice != null) { + cachedDeviceSet.add(subDevice); + return cachedDeviceSet; + } + + return cachedDeviceSet; + } + + private boolean isConnectingOrDisconnectingConnectionStatus( + Set<CachedBluetoothDevice> devices) { + HearingAidProfile hearingAidProfile = mBtManager.getProfileManager().getHearingAidProfile(); + HapClientProfile hapClientProfile = mBtManager.getProfileManager().getHapClientProfile(); + + for (CachedBluetoothDevice device : devices) { + if (hearingAidProfile != null) { + int status = device.getProfileConnectionState(hearingAidProfile); + if (status == BluetoothProfile.STATE_DISCONNECTING + || status == BluetoothProfile.STATE_CONNECTING) { + return true; + } + } + if (hapClientProfile != null) { + int status = device.getProfileConnectionState(hapClientProfile); + if (status == BluetoothProfile.STATE_DISCONNECTING + || status == BluetoothProfile.STATE_CONNECTING) { + return true; + } + } + } + return false; + } + + private boolean isActiveConnectionStatus(Set<CachedBluetoothDevice> devices) { + for (CachedBluetoothDevice device : devices) { + if ((device.isActiveDevice(BluetoothProfile.HEARING_AID) + && device.isConnectedProfile(BluetoothProfile.HEARING_AID)) + || (device.isActiveDevice(BluetoothProfile.LE_AUDIO) + && device.isConnectedProfile(BluetoothProfile.LE_AUDIO))) { + return true; + } + } + return false; + } + + private boolean isConnectedStatus(Set<CachedBluetoothDevice> devices) { + return devices.stream().anyMatch(CachedBluetoothDevice::isConnected); + } + + private boolean isDisconnectedStatus(Set<CachedBluetoothDevice> devices) { + return devices.stream().anyMatch( + d -> (!d.isConnected() && d.getBondState() == BOND_BONDED)); + } + + /** + * Gets the connection status for hearing device set. Will update connection status first if + * never updated. + */ + @ConnectionStatus + public int getDevicesConnectionStatus() { + if (!mInitialDevicesConnectionStatusUpdate) { + updateDevicesConnectionStatus(); + } + return mDevicesConnectionStatus; + } + void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice, List<ScanFilter> leScanFilters) { HearingAidInfo info = generateHearingAidInfo(newDevice); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 8dfeb559a8b5..7c24df9e9019 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -47,12 +47,14 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; +import com.android.settingslib.flags.Flags; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; @@ -345,11 +347,17 @@ public class LocalBluetoothProfileManager { oldState == BluetoothProfile.STATE_CONNECTING) { Log.i(TAG, "Failed to connect " + mProfile + " device"); } + final boolean isAshaProfile = getHearingAidProfile() != null + && mProfile instanceof HearingAidProfile; + final boolean isHapClientProfile = getHapClientProfile() != null + && mProfile instanceof HapClientProfile; + final boolean isLeAudioProfile = getLeAudioProfile() != null + && mProfile instanceof LeAudioProfile; + final boolean isHapClientOrLeAudioProfile = isHapClientProfile || isLeAudioProfile; + final boolean isCsipProfile = getCsipSetCoordinatorProfile() != null + && mProfile instanceof CsipSetCoordinatorProfile; - if (getHearingAidProfile() != null - && mProfile instanceof HearingAidProfile - && (newState == BluetoothProfile.STATE_CONNECTED)) { - + if (isAshaProfile && (newState == BluetoothProfile.STATE_CONNECTED)) { // Check if the HiSyncID has being initialized if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) { long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice()); @@ -366,11 +374,6 @@ public class LocalBluetoothProfileManager { HearingAidStatsLogUtils.logHearingAidInfo(cachedDevice); } - final boolean isHapClientProfile = getHapClientProfile() != null - && mProfile instanceof HapClientProfile; - final boolean isLeAudioProfile = getLeAudioProfile() != null - && mProfile instanceof LeAudioProfile; - final boolean isHapClientOrLeAudioProfile = isHapClientProfile || isLeAudioProfile; if (isHapClientOrLeAudioProfile && newState == BluetoothProfile.STATE_CONNECTED) { // Checks if both profiles are connected to the device. Hearing aid info need @@ -385,9 +388,7 @@ public class LocalBluetoothProfileManager { } } - if (getCsipSetCoordinatorProfile() != null - && mProfile instanceof CsipSetCoordinatorProfile - && newState == BluetoothProfile.STATE_CONNECTED) { + if (isCsipProfile && (newState == BluetoothProfile.STATE_CONNECTED)) { // Check if the GroupID has being initialized if (cachedDevice.getGroupId() == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) { final Map<Integer, ParcelUuid> groupIdMap = getCsipSetCoordinatorProfile() @@ -403,6 +404,21 @@ public class LocalBluetoothProfileManager { } } + // LE_AUDIO, CSIP_SET_COORDINATOR profiles will also impact the connection status + // change, e.g. device need to active on LE_AUDIO to become active connection status. + final Set<Integer> hearingDeviceConnectionStatusProfileId = Set.of( + BluetoothProfile.HEARING_AID, + BluetoothProfile.HAP_CLIENT, + BluetoothProfile.LE_AUDIO, + BluetoothProfile.CSIP_SET_COORDINATOR + ); + if (Flags.hearingDeviceSetConnectionStatusReport()) { + if (hearingDeviceConnectionStatusProfileId.contains(mProfile.getProfileId())) { + mDeviceManager.notifyHearingDevicesConnectionStatusChangedIfNeeded( + cachedDevice); + } + } + cachedDevice.onProfileStateChanged(mProfile, newState); // Dispatch profile changed after device update boolean needDispatchProfileConnectionState = true; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt index c71b19c9235f..e01f27964733 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt @@ -501,7 +501,7 @@ open class WifiUtils { val wifiManager = context.getSystemService(WifiManager::class.java) ?: return@launch val aapmManager = context.getSystemService(AdvancedProtectionManager::class.java) if (isAdvancedProtectionEnabled(aapmManager)) { - val intent = AdvancedProtectionManager.createSupportIntent( + val intent = aapmManager.createSupportIntent( AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP, AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION) onStartActivity(intent) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java index 05f471f62f1d..69e99c616910 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java @@ -33,21 +33,37 @@ import android.bluetooth.BluetoothUuid; import android.content.Context; import android.os.Parcel; import android.os.ParcelUuid; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; + +import androidx.test.core.app.ApplicationProvider; + +import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import java.util.Collection; +import java.util.List; import java.util.Map; @RunWith(RobolectricTestRunner.class) public class CachedBluetoothDeviceManagerTest { + @Rule + public MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + private final Context mContext = ApplicationProvider.getApplicationContext(); + private final static String DEVICE_NAME_1 = "TestName_1"; private final static String DEVICE_NAME_2 = "TestName_2"; private final static String DEVICE_NAME_3 = "TestName_3"; @@ -82,6 +98,8 @@ public class CachedBluetoothDeviceManagerTest { @Mock private HearingAidProfile mHearingAidProfile; @Mock + private HapClientProfile mHapClientProfile; + @Mock private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile; @Mock private BluetoothDevice mDevice1; @@ -89,12 +107,11 @@ public class CachedBluetoothDeviceManagerTest { private BluetoothDevice mDevice2; @Mock private BluetoothDevice mDevice3; + private HearingAidDeviceManager mHearingAidDeviceManager; private CachedBluetoothDevice mCachedDevice1; private CachedBluetoothDevice mCachedDevice2; private CachedBluetoothDevice mCachedDevice3; private CachedBluetoothDeviceManager mCachedDeviceManager; - private HearingAidDeviceManager mHearingAidDeviceManager; - private Context mContext; private BluetoothClass createBtClass(int deviceClass) { Parcel p = Parcel.obtain(); @@ -108,8 +125,6 @@ public class CachedBluetoothDeviceManagerTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1); when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2); when(mDevice3.getAddress()).thenReturn(DEVICE_ADDRESS_3); @@ -129,13 +144,15 @@ public class CachedBluetoothDeviceManagerTest { when(mA2dpProfile.isProfileReady()).thenReturn(true); when(mPanProfile.isProfileReady()).thenReturn(true); when(mHearingAidProfile.isProfileReady()).thenReturn(true); + when(mHapClientProfile.isProfileReady()).thenReturn(true); when(mCsipSetCoordinatorProfile.isProfileReady()) .thenReturn(true); doAnswer((invocation) -> mHearingAidProfile). when(mLocalProfileManager).getHearingAidProfile(); doAnswer((invocation) -> mCsipSetCoordinatorProfile) .when(mLocalProfileManager).getCsipSetCoordinatorProfile(); - mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager); + mCachedDeviceManager = spy( + new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager)); mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1)); mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2)); mCachedDevice3 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3)); @@ -621,12 +638,55 @@ public class CachedBluetoothDeviceManagerTest { public void onActiveDeviceChanged_validHiSyncId_callExpectedFunction() { doNothing().when(mHearingAidDeviceManager).onActiveDeviceChanged(any()); when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); - CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); - cachedDevice1.setHearingAidInfo( - new HearingAidInfo.Builder().setHiSyncId(HISYNCID1).build()); + when(mCachedDevice1.getProfiles()).thenReturn( + ImmutableList.of(mHapClientProfile, mHearingAidProfile)); + + mCachedDeviceManager.onActiveDeviceChanged(mCachedDevice1); + + verify(mHearingAidDeviceManager).onActiveDeviceChanged(mCachedDevice1); + } + + @Test + @RequiresFlagsEnabled( + com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT) + public void onActiveDeviceChanged_hearingDevice_callReportConnectionStatus() { + when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mCachedDevice1.getProfiles()).thenReturn( + ImmutableList.of(mHapClientProfile, mHearingAidProfile)); + + mCachedDeviceManager.onActiveDeviceChanged(mCachedDevice1); + + verify(mHearingAidDeviceManager).notifyDevicesConnectionStatusChanged(); + } + + @Test + @RequiresFlagsEnabled( + com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT) + public void onDeviceUnpaired_hearingDevice_callReportConnectionStatus() { + when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mCachedDevice1.getProfiles()).thenReturn( + ImmutableList.of(mHapClientProfile, mHearingAidProfile)); + + mCachedDeviceManager.onDeviceUnpaired(mCachedDevice1); + + verify(mHearingAidDeviceManager).notifyDevicesConnectionStatusChanged(); + } + + @Test + public void notifyHearingDevicesConnectionStatusChanged_nonHearingDevice_notCallFunction() { + when(mCachedDevice1.getProfiles()).thenReturn(List.of(mA2dpProfile)); + + mCachedDeviceManager.notifyHearingDevicesConnectionStatusChangedIfNeeded(mCachedDevice1); + + verify(mHearingAidDeviceManager, never()).notifyDevicesConnectionStatusChanged(); + } + + @Test + public void notifyHearingDevicesConnectionStatusChanged_hearingDeviceProfile_callFunction() { + when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile)); - mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1); + mCachedDeviceManager.notifyHearingDevicesConnectionStatusChangedIfNeeded(mCachedDevice1); - verify(mHearingAidDeviceManager).onActiveDeviceChanged(cachedDevice1); + verify(mHearingAidDeviceManager).notifyDevicesConnectionStatusChanged(); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index 30f8a798b674..d933a1ced8bc 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -2074,6 +2074,21 @@ public class CachedBluetoothDeviceTest { assertThat(mCachedDevice.getConnectionSummary(false)).isNull(); } + @Test + public void isHearingDevice_supportHearingRelatedProfiles_returnTrue() { + when(mCachedDevice.getProfiles()).thenReturn( + ImmutableList.of(mHapClientProfile, mHearingAidProfile)); + + assertThat(mCachedDevice.isHearingDevice()).isTrue(); + } + + @Test + public void isHearingDevice_supportOnlyLeAudioProfile_returnFalse() { + when(mCachedDevice.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile)); + + assertThat(mCachedDevice.isHearingDevice()).isFalse(); + } + private void updateProfileStatus(LocalBluetoothProfile profile, int status) { doReturn(status).when(profile).getConnectionStatus(mDevice); mCachedDevice.onProfileStateChanged(profile, status); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java index 2458c5b2eb6e..21dde1fd9411 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java @@ -54,6 +54,8 @@ import android.util.FeatureFlagUtils; import androidx.test.core.app.ApplicationProvider; +import com.android.settingslib.bluetooth.HearingAidDeviceManager.ConnectionStatus; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -112,7 +114,10 @@ public class HearingAidDeviceManagerTest { private BluetoothDevice mDevice1; @Mock private BluetoothDevice mDevice2; - + @Mock + private HearingAidDeviceManager.ConnectionStatusListener mConnectionStatusListener; + @Mock + private HearingAidDeviceManager.ConnectionStatusListener mConnectionStatusListener2; private BluetoothClass createBtClass(int deviceClass) { Parcel p = Parcel.obtain(); @@ -140,6 +145,8 @@ public class HearingAidDeviceManagerTest { when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile); when(mLocalProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile); when(mLocalProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile); + when(mHapClientProfile.getProfileId()).thenReturn(BluetoothProfile.HAP_CLIENT); + when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO); when(mAudioStrategy.getAudioAttributesForLegacyStreamType( AudioManager.STREAM_MUSIC)) .thenReturn((new AudioAttributes.Builder()).build()); @@ -826,6 +833,125 @@ public class HearingAidDeviceManagerTest { verify(mHapClientProfile).selectPreset(mDevice2, PRESET_INDEX_1); } + @Test + public void getAssociatedCachedDevice_existSubDevice_returnSize2() { + mCachedDevice1.setSubDevice(mCachedDevice2); + + //including self device + assertThat(mHearingAidDeviceManager.getAssociatedCachedDevice( + mCachedDevice1).size()).isEqualTo(2); + } + + @Test + public void getAssociatedCachedDevice_existMemberDevice_returnSize2() { + mCachedDevice1.addMemberDevice(mCachedDevice2); + + //including self device + assertThat(mHearingAidDeviceManager.getAssociatedCachedDevice( + mCachedDevice1).size()).isEqualTo(2); + } + + @Test + public void notifyDevicesConnectionStatusChanged_connecting_connectingStatus() { + when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile)); + when(mHapClientProfile.getConnectionStatus(mDevice1)).thenReturn( + BluetoothProfile.STATE_CONNECTING); + + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); + + assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo( + ConnectionStatus.CONNECTING_OR_DISCONNECTING); + } + + @Test + public void notifyDevicesConnectionStatusChanged_activeConnectedProfile_activeStatus() { + when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile, mLeAudioProfile)); + when(mLeAudioProfile.getConnectionStatus(mDevice1)).thenReturn( + BluetoothProfile.STATE_CONNECTED); + when(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true); + + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); + + assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo( + ConnectionStatus.ACTIVE); + } + + @Test + public void notifyDevicesConnectionStatusChanged_isConnected_connectedStatus() { + when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile, mLeAudioProfile)); + when(mCachedDevice1.isConnected()).thenReturn(true); + + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); + + assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo( + ConnectionStatus.CONNECTED); + } + + @Test + public void notifyDevicesConnectionStatusChanged_bondedNotConnected_disconnectedStatus() { + when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mCachedDevice1.isConnected()).thenReturn(false); + when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile)); + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + + mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); + + assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo( + ConnectionStatus.DISCONNECTED); + } + + @Test + public void notifyDevicesConnectionStatusChanged_bondNone_noDeviceBondedStatus() { + when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE); + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + + mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); + + assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo( + ConnectionStatus.NO_DEVICE_BONDED); + } + + @Test + public void notifyDevicesConnectionStatusChanged_noRegisteredListener_noCallback() { + when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile, mLeAudioProfile)); + when(mCachedDevice1.isConnected()).thenReturn(true); + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + + mHearingAidDeviceManager.registerConnectionStatusListener( + mConnectionStatusListener, mContext.getMainExecutor()); + mHearingAidDeviceManager.unregisterConnectionStatusListener( + mConnectionStatusListener); + mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); + + verify(mConnectionStatusListener, never()).onDevicesConnectionStatusChanged(anyInt()); + } + + @Test + public void notifyDevicesConnectionStatusChanged_twoRegisteredListener_callbackEachConnected() { + when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile, mLeAudioProfile)); + when(mCachedDevice1.isConnected()).thenReturn(true); + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + + mHearingAidDeviceManager.registerConnectionStatusListener( + mConnectionStatusListener, mContext.getMainExecutor()); + mHearingAidDeviceManager.registerConnectionStatusListener( + mConnectionStatusListener2, mContext.getMainExecutor()); + mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); + + verify(mConnectionStatusListener).onDevicesConnectionStatusChanged( + ConnectionStatus.CONNECTED); + verify(mConnectionStatusListener2).onDevicesConnectionStatusChanged( + ConnectionStatus.CONNECTED); + } + private HearingAidInfo getLeftAshaHearingAidInfo(long hiSyncId) { return new HearingAidInfo.Builder() .setAshaDeviceSide(HearingAidInfo.DeviceSide.SIDE_LEFT) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java index 6ff90ba4b391..219bfe0c8c53 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java @@ -37,10 +37,14 @@ import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.Intent; import android.os.ParcelUuid; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -56,6 +60,9 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class}) public class LocalBluetoothProfileManagerTest { + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private static final long HISYNCID = 10; private static final int GROUP_ID = 1; @@ -305,6 +312,25 @@ public class LocalBluetoothProfileManagerTest { verify(mCachedBluetoothDevice).refresh(); } + @Test + @RequiresFlagsEnabled( + com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT) + public void stateChangedHandler_hapProfileStateChanged_notifyHearingDevicesConnectionStatus() { + mShadowBluetoothAdapter.setSupportedProfiles(generateList( + new int[] {BluetoothProfile.HAP_CLIENT})); + mProfileManager.updateLocalProfiles(); + + mIntent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); + mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING); + mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); + + mContext.sendBroadcast(mIntent); + + verify(mDeviceManager).notifyHearingDevicesConnectionStatusChangedIfNeeded( + mCachedBluetoothDevice); + } + private List<Integer> generateList(int[] profiles) { if (profiles == null) { return null; diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index f1bbfc62681a..5b4ee8bdb339 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -110,6 +110,7 @@ public class SystemSettings { Settings.System.MOUSE_REVERSE_VERTICAL_SCROLLING, Settings.System.MOUSE_SCROLLING_ACCELERATION, Settings.System.MOUSE_SWAP_PRIMARY_BUTTON, + Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED, Settings.System.TOUCHPAD_POINTER_SPEED, Settings.System.TOUCHPAD_NATURAL_SCROLLING, Settings.System.TOUCHPAD_TAP_TO_CLICK, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 6abd9b73e26d..0432eeacec4d 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -226,6 +226,7 @@ public class SystemSettingsValidators { VALIDATORS.put(System.MOUSE_REVERSE_VERTICAL_SCROLLING, BOOLEAN_VALIDATOR); VALIDATORS.put(System.MOUSE_SWAP_PRIMARY_BUTTON, BOOLEAN_VALIDATOR); VALIDATORS.put(System.MOUSE_SCROLLING_ACCELERATION, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.MOUSE_POINTER_ACCELERATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7)); VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR); VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR); diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 3ee2db10d7e5..b88ae3751d22 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -278,13 +278,13 @@ filegroup { "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt", "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt", "tests/src/**/systemui/qs/tiles/HotspotTileTest.java", - "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java", + "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java", "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java", "tests/src/**/systemui/wmshell/BubblesTest.java", "tests/src/**/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java", "tests/src/**/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java", "tests/src/**/systemui/shared/system/RemoteTransitionTest.java", - "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java", + "tests/src/**/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java", "tests/src/**/systemui/qs/external/TileLifecycleManagerTest.java", "tests/src/**/systemui/ScreenDecorationsTest.java", "tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java", diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index a93e4a08331c..7d201c18a141 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -492,7 +492,7 @@ flag { name: "status_bar_notification_chips" namespace: "systemui" description: "Show promoted ongoing notifications as chips in the status bar" - bug: "361346412" + bug: "364653005" } flag { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt index 2233debde277..4ee6db3d516c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.compose.animation.scene.effect +package com.android.compose.gesture.effect import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationSpec @@ -118,56 +118,3 @@ open class BaseContentOverscrollEffect( } } } - -/** An overscroll effect that ensures only a single fling animation is triggered. */ -internal class GestureEffect(private val delegate: ContentOverscrollEffect) : - ContentOverscrollEffect by delegate { - private var shouldFling = false - - override fun applyToScroll( - delta: Offset, - source: NestedScrollSource, - performScroll: (Offset) -> Offset, - ): Offset { - shouldFling = true - return delegate.applyToScroll(delta, source, performScroll) - } - - override suspend fun applyToFling( - velocity: Velocity, - performFling: suspend (Velocity) -> Velocity, - ) { - if (!shouldFling) { - performFling(velocity) - return - } - shouldFling = false - delegate.applyToFling(velocity, performFling) - } - - suspend fun ensureApplyToFlingIsCalled() { - applyToFling(Velocity.Zero) { Velocity.Zero } - } -} - -/** - * An overscroll effect that only applies visual effects and does not interfere with the actual - * scrolling or flinging behavior. - */ -internal class VisualEffect(private val delegate: ContentOverscrollEffect) : - ContentOverscrollEffect by delegate { - override fun applyToScroll( - delta: Offset, - source: NestedScrollSource, - performScroll: (Offset) -> Offset, - ): Offset { - return performScroll(delta) - } - - override suspend fun applyToFling( - velocity: Velocity, - performFling: suspend (Velocity) -> Velocity, - ) { - performFling(velocity) - } -} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt index f459c46d3e6f..d9924038d8ef 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.compose.animation.scene.effect +package com.android.compose.gesture.effect import androidx.annotation.VisibleForTesting import androidx.compose.animation.core.AnimationSpec @@ -34,7 +34,6 @@ import androidx.compose.ui.node.LayoutModifierNode import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.dp -import com.android.compose.animation.scene.ProgressConverter import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope @@ -80,7 +79,7 @@ class OffsetOverscrollEffect( ) @VisibleForTesting - internal fun computeOffset(density: Density, overscrollDistance: Float): Int { + fun computeOffset(density: Density, overscrollDistance: Float): Int { val maxDistancePx = with(density) { MaxDistance.toPx() } val progress = ProgressConverter.Default.convert(overscrollDistance / maxDistancePx) return (progress * maxDistancePx).roundToInt() @@ -98,3 +97,35 @@ fun rememberOffsetOverscrollEffect( OffsetOverscrollEffect(orientation, animationScope, animationSpec) } } + +/** This converter lets you change a linear progress into a function of your choice. */ +fun interface ProgressConverter { + fun convert(progress: Float): Float + + companion object { + /** Starts linearly with some resistance and slowly approaches to 0.2f */ + val Default = tanh(maxProgress = 0.2f, tilt = 3f) + + /** + * The scroll stays linear, with [factor] you can control how much resistance there is. + * + * @param factor If you choose a value between 0f and 1f, the progress will grow more + * slowly, like there's resistance. A value of 1f means there's no resistance. + */ + fun linear(factor: Float = 1f) = ProgressConverter { it * factor } + + /** + * This function starts linear and slowly approaches [maxProgress]. + * + * See a [visual representation](https://www.desmos.com/calculator/usgvvf0z1u) of this + * function. + * + * @param maxProgress is the maximum progress value. + * @param tilt behaves similarly to the factor in the [linear] function, and allows you to + * control how quickly you get to the [maxProgress]. + */ + fun tanh(maxProgress: Float, tilt: Float = 1f) = ProgressConverter { + maxProgress * kotlin.math.tanh(x = it / (maxProgress * tilt)) + } + } +} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/util/SpaceVectorConverter.kt index ca50e778d131..ca50e778d131 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/ui/util/SpaceVectorConverter.kt diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt index da8fe3094448..5a3f240deb44 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.compose.animation.scene.effect +package com.android.compose.gesture.effect import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.rememberScrollableState diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt index 7956d0293a1d..9643f192e066 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt @@ -34,7 +34,6 @@ import androidx.compose.ui.unit.IntRect import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.padding -import com.android.compose.modifiers.thenIf import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.keyguard.ui.composable.LockscreenLongPress import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection @@ -97,18 +96,15 @@ constructor( ) } - Box { + Box(modifier = Modifier.fillMaxWidth()) { with(topAreaSection) { DefaultClockLayout( smartSpacePaddingTop = viewModel::getSmartSpacePaddingTop, isShadeLayoutWide = isShadeLayoutWide, modifier = - Modifier.thenIf(isShadeLayoutWide) { - Modifier.fillMaxWidth(0.5f) - } - .graphicsLayer { - translationX = unfoldTranslations.start - }, + Modifier.fillMaxWidth().graphicsLayer { + translationX = unfoldTranslations.start + }, ) } if (isShadeLayoutWide && !isBypassEnabled) { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt index eae46e90557a..fb01e7039edd 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt @@ -81,10 +81,7 @@ constructor( .padding(horizontal = dimensionResource(R.dimen.clock_padding_start)) .padding(top = { smallTopMargin }) .onTopPlacementChanged(onTopChanged) - .burnInAware( - viewModel = aodBurnInViewModel, - params = burnInParams, - ) + .burnInAware(viewModel = aodBurnInViewModel, params = burnInParams) .element(smallClockElementKey), ) } @@ -114,10 +111,7 @@ constructor( val dir = if (transition.toContent == splitShadeLargeClockScene) -1f else 1f val distance = dir * getClockCenteringDistance() val largeClock = checkNotNull(currentClock).largeClock - largeClock.animations.onPositionUpdated( - distance = distance, - fraction = progress, - ) + largeClock.animations.onPositionUpdated(distance = distance, fraction = progress) } Element(key = largeClockElementKey, modifier = modifier) { @@ -125,6 +119,16 @@ constructor( AndroidView( factory = { context -> FrameLayout(context).apply { + // By default, ViewGroups like FrameLayout clip their children. Turning + // off the clipping allows the child view to render outside of its + // bounds - letting the step animation of the clock push the digits out + // when needed. + // + // Note that, in Compose, clipping is actually disabled by default so + // there's no need to propagate this up the composable hierarchy. + clipChildren = false + clipToPadding = false + ensureClockViewExists(checkNotNull(currentClock).largeClock.view) } }, @@ -136,8 +140,8 @@ constructor( .burnInAware( viewModel = aodBurnInViewModel, params = burnInParams, - isClock = true - ) + isClock = true, + ), ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt index cfbe6671db02..ffdf509174d5 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt @@ -58,7 +58,7 @@ import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.ContentScope import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.LowestZIndexContentPicker -import com.android.compose.animation.scene.effect.rememberOffsetOverscrollEffect +import com.android.compose.gesture.effect.rememberOffsetOverscrollEffect import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.systemui.res.R diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt index 1480db9de701..5f991fbb50df 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt @@ -101,20 +101,21 @@ fun VolumeSlider( Column(modifier) { Row( horizontalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth().height(40.dp), + verticalAlignment = Alignment.CenterVertically, ) { state.icon?.let { Icon( icon = it, tint = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.size(40.dp).padding(8.dp), + modifier = Modifier.size(24.dp), ) } Text( text = state.label, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.weight(1f).align(Alignment.CenterVertically), + modifier = Modifier.weight(1f), ) button?.invoke() } @@ -125,43 +126,47 @@ fun VolumeSlider( onValueChangeFinished = onValueChangeFinished, enabled = state.isEnabled, modifier = - Modifier.height(40.dp).sysuiResTag(state.label).clearAndSetSemantics { - if (state.isEnabled) { - contentDescription = state.label - state.a11yClickDescription?.let { - customActions = - listOf( - CustomAccessibilityAction(it) { - onIconTapped() - true - } - ) - } - - state.a11yStateDescription?.let { stateDescription = it } - progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange) - } else { - disabled() - contentDescription = - state.disabledMessage?.let { "${state.label}, $it" } ?: state.label - } - setProgress { targetValue -> - val targetDirection = - when { - targetValue > value -> 1 - targetValue < value -> -1 - else -> 0 + Modifier.height(40.dp) + .padding(vertical = 8.dp) + .sysuiResTag(state.label) + .clearAndSetSemantics { + if (state.isEnabled) { + contentDescription = state.label + state.a11yClickDescription?.let { + customActions = + listOf( + CustomAccessibilityAction(it) { + onIconTapped() + true + } + ) } - val newValue = - (value + targetDirection * state.a11yStep).coerceIn( - state.valueRange.start, - state.valueRange.endInclusive, - ) - onValueChange(newValue) - true - } - }, + state.a11yStateDescription?.let { stateDescription = it } + progressBarRangeInfo = + ProgressBarRangeInfo(state.value, state.valueRange) + } else { + disabled() + contentDescription = + state.disabledMessage?.let { "${state.label}, $it" } ?: state.label + } + setProgress { targetValue -> + val targetDirection = + when { + targetValue > value -> 1 + targetValue < value -> -1 + else -> 0 + } + + val newValue = + (value + targetDirection * state.a11yStep).coerceIn( + state.valueRange.start, + state.valueRange.endInclusive, + ) + onValueChange(newValue) + true + } + }, ) } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 7b30a2a475e3..c704a3e96467 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -35,7 +35,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.LayoutDirection -import com.android.compose.animation.scene.effect.ContentOverscrollEffect +import com.android.compose.gesture.effect.ContentOverscrollEffect /** * [SceneTransitionLayout] is a container that automatically animates its content whenever its state diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt index 8794df0cf884..fda6fab6229a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.transformation.Transformation -import kotlin.math.tanh /** Define the [transitions][SceneTransitions] to be used with a [SceneTransitionLayout]. */ fun transitions(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions { @@ -476,35 +475,3 @@ interface PropertyTransformationBuilder { /** Apply a [transformation] to the element(s) matching [matcher]. */ fun transformation(matcher: ElementMatcher, transformation: Transformation.Factory) } - -/** This converter lets you change a linear progress into a function of your choice. */ -fun interface ProgressConverter { - fun convert(progress: Float): Float - - companion object { - /** Starts linearly with some resistance and slowly approaches to 0.2f */ - val Default = tanh(maxProgress = 0.2f, tilt = 3f) - - /** - * The scroll stays linear, with [factor] you can control how much resistance there is. - * - * @param factor If you choose a value between 0f and 1f, the progress will grow more - * slowly, like there's resistance. A value of 1f means there's no resistance. - */ - fun linear(factor: Float = 1f) = ProgressConverter { it * factor } - - /** - * This function starts linear and slowly approaches [maxProgress]. - * - * See a [visual representation](https://www.desmos.com/calculator/usgvvf0z1u) of this - * function. - * - * @param maxProgress is the maximum progress value. - * @param tilt behaves similarly to the factor in the [linear] function, and allows you to - * control how quickly you get to the [maxProgress]. - */ - fun tanh(maxProgress: Float, tilt: Float = 1f) = ProgressConverter { - maxProgress * tanh(x = it / (maxProgress * tilt)) - } - } -} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt index 8c5a72738a41..4c15f7a4534f 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt @@ -53,10 +53,10 @@ import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.ValueKey import com.android.compose.animation.scene.animateSharedValueAsState import com.android.compose.animation.scene.effect.GestureEffect -import com.android.compose.animation.scene.effect.OffsetOverscrollEffect import com.android.compose.animation.scene.effect.VisualEffect import com.android.compose.animation.scene.element import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions +import com.android.compose.gesture.effect.OffsetOverscrollEffect import com.android.compose.modifiers.thenIf import com.android.compose.ui.graphics.ContainerState import com.android.compose.ui.graphics.container diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.kt new file mode 100644 index 000000000000..2db45aa3dd58 --- /dev/null +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.kt @@ -0,0 +1,75 @@ +/* + * 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.compose.animation.scene.effect + +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.unit.Velocity +import com.android.compose.gesture.effect.ContentOverscrollEffect + +/** An overscroll effect that ensures only a single fling animation is triggered. */ +internal class GestureEffect(private val delegate: ContentOverscrollEffect) : + ContentOverscrollEffect by delegate { + private var shouldFling = false + + override fun applyToScroll( + delta: Offset, + source: NestedScrollSource, + performScroll: (Offset) -> Offset, + ): Offset { + shouldFling = true + return delegate.applyToScroll(delta, source, performScroll) + } + + override suspend fun applyToFling( + velocity: Velocity, + performFling: suspend (Velocity) -> Velocity, + ) { + if (!shouldFling) { + performFling(velocity) + return + } + shouldFling = false + delegate.applyToFling(velocity, performFling) + } + + suspend fun ensureApplyToFlingIsCalled() { + applyToFling(Velocity.Zero) { Velocity.Zero } + } +} + +/** + * An overscroll effect that only applies visual effects and does not interfere with the actual + * scrolling or flinging behavior. + */ +internal class VisualEffect(private val delegate: ContentOverscrollEffect) : + ContentOverscrollEffect by delegate { + override fun applyToScroll( + delta: Offset, + source: NestedScrollSource, + performScroll: (Offset) -> Offset, + ): Offset { + return performScroll(delta) + } + + override suspend fun applyToFling( + velocity: Velocity, + performFling: suspend (Velocity) -> Velocity, + ) { + performFling(velocity) + } +} 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 676903274b38..53495be7b02a 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 @@ -70,8 +70,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.animation.scene.TestScenes.SceneA import com.android.compose.animation.scene.TestScenes.SceneB import com.android.compose.animation.scene.TestScenes.SceneC -import com.android.compose.animation.scene.effect.OffsetOverscrollEffect import com.android.compose.animation.scene.subjects.assertThat +import com.android.compose.gesture.effect.OffsetOverscrollEffect import com.android.compose.test.assertSizeIsEqualTo import com.android.compose.test.setContentAndCreateMainScope import com.android.compose.test.transition diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java index 80de087971c5..266591028efb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java @@ -29,6 +29,7 @@ import android.view.accessibility.AccessibilityManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.utils.TestUtils; @@ -58,13 +59,15 @@ public class DragToInteractAnimationControllerTest extends SysuiTestCase { @Mock private AccessibilityManager mAccessibilityManager; + @Mock + private HearingAidDeviceManager mHearingAidDeviceManager; @Before public void setUp() throws Exception { final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class); final SecureSettings mockSecureSettings = TestUtils.mockSecureSettings(); final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager, - mockSecureSettings); + mockSecureSettings, mHearingAidDeviceManager); final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext, stubWindowManager); final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java index 24f3a29e64ee..785493faf957 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java @@ -16,6 +16,7 @@ package com.android.systemui.accessibility.floatingmenu; +import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static org.mockito.ArgumentMatchers.anyInt; @@ -25,11 +26,13 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.res.Configuration; +import android.platform.test.annotations.EnableFlags; import android.view.accessibility.AccessibilityManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.settings.SecureSettings; @@ -45,6 +48,7 @@ import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.concurrent.Executor; /** Tests for {@link MenuInfoRepository}. */ @RunWith(AndroidJUnit4.class) @@ -55,9 +59,10 @@ public class MenuInfoRepositoryTest extends SysuiTestCase { @Mock private AccessibilityManager mAccessibilityManager; - @Mock - private MenuInfoRepository.OnSettingsContentsChanged mMockSettingsContentsChanged; + private HearingAidDeviceManager mHearingAidDeviceManager; + @Mock + private MenuInfoRepository.OnContentsChanged mMockSettingsContentsChanged; @Mock private SecureSettings mSecureSettings; @@ -72,7 +77,7 @@ public class MenuInfoRepositoryTest extends SysuiTestCase { anyInt()); mMenuInfoRepository = new MenuInfoRepository(mContext, mAccessibilityManager, - mMockSettingsContentsChanged, mSecureSettings); + mMockSettingsContentsChanged, mSecureSettings, mHearingAidDeviceManager); } @After @@ -103,4 +108,16 @@ public class MenuInfoRepositoryTest extends SysuiTestCase { verify(mMockSettingsContentsChanged).onTargetFeaturesChanged(any()); } + + @Test + @EnableFlags( + com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT) + public void registerObservers_addHearingDeviceTarget_verifyRegisterConnectionStatusListener() { + mShortcutTargets.add(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()); + mMenuInfoRepository.registerObserversAndCallbacks(); + + verify(mHearingAidDeviceManager).registerConnectionStatusListener( + any(HearingAidDeviceManager.ConnectionStatusListener.class), any( + Executor.class)); + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java index 157cccc3d62f..241da5fbc444 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java @@ -41,6 +41,7 @@ import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.utils.TestUtils; @@ -68,6 +69,8 @@ public class MenuItemAccessibilityDelegateTest extends SysuiTestCase { @Mock private AccessibilityManager mAccessibilityManager; + @Mock + private HearingAidDeviceManager mHearingAidDeviceManager; private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings(); private RecyclerView mStubListView; private MenuView mMenuView; @@ -84,7 +87,7 @@ public class MenuItemAccessibilityDelegateTest extends SysuiTestCase { final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext, stubWindowManager); final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager, - mSecureSettings); + mSecureSettings, mHearingAidDeviceManager); final int halfScreenHeight = stubWindowManager.getCurrentWindowMetrics().getBounds().height() / 2; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java index 46f076a75116..fbd8a713a249 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java @@ -41,6 +41,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.accessibility.dialog.AccessibilityTarget; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.MotionEventHelper; @@ -82,13 +83,15 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase { @Mock private AccessibilityManager mAccessibilityManager; + @Mock + private HearingAidDeviceManager mHearingAidDeviceManager; @Before public void setUp() throws Exception { final WindowManager windowManager = mContext.getSystemService(WindowManager.class); final SecureSettings secureSettings = TestUtils.mockSecureSettings(); final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager, - secureSettings); + secureSettings, mHearingAidDeviceManager); final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext, windowManager); mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java index fcdeff9ab683..4f043109a534 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java @@ -41,6 +41,7 @@ import androidx.test.filters.SmallTest; import com.android.app.viewcapture.ViewCapture; import com.android.app.viewcapture.ViewCaptureAwareWindowManager; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.SysuiTestCase; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.util.settings.SecureSettings; @@ -68,6 +69,8 @@ public class MenuViewLayerControllerTest extends SysuiTestCase { @Mock private AccessibilityManager mAccessibilityManager; + @Mock + private HearingAidDeviceManager mHearingAidDeviceManager; @Mock private SecureSettings mSecureSettings; @@ -93,7 +96,7 @@ public class MenuViewLayerControllerTest extends SysuiTestCase { when(mWindowMetrics.getWindowInsets()).thenReturn(stubDisplayInsets()); mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager, viewCaptureAwareWm, mAccessibilityManager, mSecureSettings, - mock(NavigationModeController.class)); + mock(NavigationModeController.class), mHearingAidDeviceManager); } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java index ee8ce17cecd4..cb7c205742fc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java @@ -37,6 +37,7 @@ import android.view.accessibility.AccessibilityManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.Flags; import com.android.systemui.Prefs; import com.android.systemui.SysuiTestCase; @@ -70,6 +71,8 @@ public class MenuViewTest extends SysuiTestCase { @Mock private AccessibilityManager mAccessibilityManager; + @Mock + private HearingAidDeviceManager mHearingAidDeviceManager; private SysuiTestableContext mSpyContext; @@ -90,7 +93,7 @@ public class MenuViewTest extends SysuiTestCase { final SecureSettings secureSettings = TestUtils.mockSecureSettings(); final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager, - secureSettings); + secureSettings, mHearingAidDeviceManager); final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class); mStubMenuViewAppearance = new MenuViewAppearance(mSpyContext, stubWindowManager); mMenuView = spy(new MenuView(mSpyContext, stubMenuViewModel, mStubMenuViewAppearance, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt index f68a1b5a17e9..eae5728f586d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.display.data.repository -import android.content.testableContext +import android.content.Context import android.platform.test.annotations.EnableFlags import android.view.Display import android.view.layoutInflater @@ -24,6 +24,7 @@ import android.view.mockWindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.SysuiTestableContext import com.android.systemui.display.shared.model.DisplayWindowProperties import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope @@ -36,8 +37,12 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.doAnswer +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.whenever @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) @RunWith(AndroidJUnit4::class) @@ -48,7 +53,8 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { private val fakeDisplayRepository = kosmos.displayRepository private val testScope = kosmos.testScope - private val applicationContext = kosmos.testableContext + private val applicationContext = spy(context) + private val applicationWindowManager = kosmos.mockWindowManager private val applicationLayoutInflater = kosmos.layoutInflater @@ -64,6 +70,22 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { } @Before + fun setUpContext() { + doAnswer { createContextForDisplay(it.arguments[0] as Display) } + .whenever(applicationContext) + .createWindowContext(any(), any(), any()) + } + + private fun createContextForDisplay(display: Display): Context { + if (display.displayId == BEING_REMOVED_DISPLAY_ID) { + // Simulate what happens when a display is being removed. + // Return a context with the same display id as the original context. + return mContext + } + return SysuiTestableContext(mContext).also { it.display = display } + } + + @Before fun start() { repo.start() } @@ -72,6 +94,7 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(createDisplay(DEFAULT_DISPLAY_ID)) fakeDisplayRepository.addDisplay(createDisplay(NON_DEFAULT_DISPLAY_ID)) + fakeDisplayRepository.addDisplay(createDisplay(BEING_REMOVED_DISPLAY_ID)) } @Test @@ -94,7 +117,7 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { @Test fun get_nonDefaultDisplayId_returnsNewStatusBarContext() = testScope.runTest { - val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO) + val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)!! assertThat(displayContext.context).isNotSameInstanceAs(applicationContext) } @@ -102,7 +125,7 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { @Test fun get_nonDefaultDisplayId_returnsNewWindowManager() = testScope.runTest { - val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO) + val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)!! assertThat(displayContext.windowManager).isNotSameInstanceAs(applicationWindowManager) } @@ -110,7 +133,7 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { @Test fun get_nonDefaultDisplayId_returnsNewLayoutInflater() = testScope.runTest { - val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO) + val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)!! assertThat(displayContext.layoutInflater).isNotSameInstanceAs(applicationLayoutInflater) } @@ -154,17 +177,26 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { .isNotSameInstanceAs(displayContext) } - @Test(expected = IllegalArgumentException::class) - fun get_nonExistingDisplayId_throws() = - testScope.runTest { repo.get(NON_EXISTING_DISPLAY_ID, WINDOW_TYPE_FOO) } + @Test + fun get_nonExistingDisplayId_returnsNull() = + testScope.runTest { + assertThat(repo.get(NON_EXISTING_DISPLAY_ID, WINDOW_TYPE_FOO)).isNull() + } + + @Test + fun get_displayBeingRemoved_returnsNull() = + testScope.runTest { + assertThat(repo.get(BEING_REMOVED_DISPLAY_ID, WINDOW_TYPE_FOO)).isNull() + } private fun createDisplay(displayId: Int) = - mock<Display> { on { getDisplayId() } doReturn displayId } + mock<Display> { on { getDisplayId() } doReturn (displayId) } companion object { private const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY private const val NON_DEFAULT_DISPLAY_ID = DEFAULT_DISPLAY_ID + 1 private const val NON_EXISTING_DISPLAY_ID = DEFAULT_DISPLAY_ID + 2 + private const val BEING_REMOVED_DISPLAY_ID = DEFAULT_DISPLAY_ID + 4 private const val WINDOW_TYPE_FOO = 123 private const val WINDOW_TYPE_BAR = 321 } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt index 6a0781b3580f..73957eb488d0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt @@ -80,9 +80,9 @@ class PerDisplayStoreImplTest : SysuiTestCase() { assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(instance) } - @Test(expected = IllegalArgumentException::class) - fun forDisplay_nonExistingDisplayId_throws() = - testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) } + @Test + fun forDisplay_nonExistingDisplayId_returnsNull() = + testScope.runTest { assertThat(store.forDisplay(NON_EXISTING_DISPLAY_ID)).isNull() } @Test fun forDisplay_afterDisplayRemoved_onDisplayRemovalActionInvoked() = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/QSSettingsPackageRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/QSSettingsPackageRepositoryTest.kt new file mode 100644 index 000000000000..765c02afbb41 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/QSSettingsPackageRepositoryTest.kt @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.shared + +import android.content.Context +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import com.android.systemui.user.data.repository.fakeUserRepository +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.anyInt +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.whenever + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class QSSettingsPackageRepositoryTest : SysuiTestCase() { + + @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var context: Context + @Mock private lateinit var packageManager: PackageManager + @Mock private lateinit var resolveInfo: ResolveInfo + @Mock private lateinit var activityInfo: ActivityInfo + + private val kosmos = testKosmos() + private val scope = kosmos.testScope + private val userRepository = kosmos.fakeUserRepository + + private lateinit var underTest: QSSettingsPackageRepository + + @Before + fun setUp() { + whenever(context.createContextAsUser(any(), anyInt())).thenReturn(context) + whenever(context.packageManager).thenReturn(packageManager) + whenever(packageManager.queryIntentActivities(any(Intent::class.java), anyInt())) + .thenReturn(listOf(resolveInfo)) + resolveInfo.activityInfo = activityInfo + + underTest = QSSettingsPackageRepository(context, scope, userRepository) + } + + @Test + fun getSettingsPackageName_noInit_returnsDefaultPackageName() { + assertThat(underTest.getSettingsPackageName()).isEqualTo(DEFAULT_SETTINGS_PACKAGE_NAME) + } + + @Test + fun getSettingsPackageName_repositoryWithCustomPackage_returnsCustomPackageName() { + scope.runTest { + activityInfo.packageName = CUSTOM_SETTINGS_PACKAGE_NAME + + underTest.init() + runCurrent() + + assertThat(underTest.getSettingsPackageName()).isEqualTo(CUSTOM_SETTINGS_PACKAGE_NAME) + } + } + + @Test + fun getSettingsPackageName_noMatchingActivity_returnsDefaultPackageName() { + scope.runTest { + whenever(packageManager.queryIntentActivities(any(Intent::class.java), anyInt())) + .thenReturn(emptyList()) + + underTest.init() + runCurrent() + + assertThat(underTest.getSettingsPackageName()).isEqualTo(DEFAULT_SETTINGS_PACKAGE_NAME) + } + } + + @Test + fun getSettingsPackageName_nullActivityInfo_returnsDefaultPackageName() { + scope.runTest { + resolveInfo.activityInfo = null + + underTest.init() + runCurrent() + + assertThat(underTest.getSettingsPackageName()).isEqualTo(DEFAULT_SETTINGS_PACKAGE_NAME) + } + } + + companion object { + private const val DEFAULT_SETTINGS_PACKAGE_NAME = "com.android.settings" + private const val CUSTOM_SETTINGS_PACKAGE_NAME = "com.android.test.settings" + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java index 028beb599644..e5e8d4a6ed32 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java @@ -39,6 +39,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.shared.QSSettingsPackageRepository; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; @@ -55,6 +56,7 @@ import org.mockito.MockitoAnnotations; @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class ColorCorrectionTileTest extends SysuiTestCase { + private static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; @Mock private QSHost mHost; @@ -70,6 +72,8 @@ public class ColorCorrectionTileTest extends SysuiTestCase { private QsEventLogger mUiEventLogger; @Mock private UserTracker mUserTracker; + @Mock + private QSSettingsPackageRepository mQSSettingsPackageRepository; private TestableLooper mTestableLooper; private SecureSettings mSecureSettings; @@ -83,6 +87,8 @@ public class ColorCorrectionTileTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); when(mHost.getContext()).thenReturn(mContext); + when(mQSSettingsPackageRepository.getSettingsPackageName()) + .thenReturn(SETTINGS_PACKAGE_NAME); mTile = new ColorCorrectionTile( mHost, @@ -95,7 +101,8 @@ public class ColorCorrectionTileTest extends SysuiTestCase { mActivityStarter, mQSLogger, mUserTracker, - mSecureSettings + mSecureSettings, + mQSSettingsPackageRepository ); mTile.initialize(); @@ -119,5 +126,6 @@ public class ColorCorrectionTileTest extends SysuiTestCase { anyInt(), any()); assertThat(IntentCaptor.getValue().getAction()).isEqualTo( Settings.ACTION_COLOR_CORRECTION_SETTINGS); + assertThat(IntentCaptor.getValue().getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME); } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java index a58dd6301e07..cbde99847770 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java @@ -45,6 +45,7 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.flags.QsInCompose; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.shared.QSSettingsPackageRepository; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; @@ -59,15 +60,16 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; +import java.util.List; + @RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class ColorInversionTileTest extends SysuiTestCase { + private static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; private static final Integer COLOR_INVERSION_DISABLED = 0; private static final Integer COLOR_INVERSION_ENABLED = 1; @@ -90,6 +92,8 @@ public class ColorInversionTileTest extends SysuiTestCase { private QsEventLogger mUiEventLogger; @Mock private UserTracker mUserTracker; + @Mock + private QSSettingsPackageRepository mQSSettingsPackageRepository; private TestableLooper mTestableLooper; private SecureSettings mSecureSettings; @@ -108,6 +112,8 @@ public class ColorInversionTileTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); when(mHost.getContext()).thenReturn(mContext); + when(mQSSettingsPackageRepository.getSettingsPackageName()) + .thenReturn(SETTINGS_PACKAGE_NAME); mTile = new ColorInversionTile( mHost, @@ -120,7 +126,8 @@ public class ColorInversionTileTest extends SysuiTestCase { mActivityStarter, mQSLogger, mUserTracker, - mSecureSettings + mSecureSettings, + mQSSettingsPackageRepository ); mTile.initialize(); @@ -144,6 +151,7 @@ public class ColorInversionTileTest extends SysuiTestCase { anyInt(), any()); assertThat(IntentCaptor.getValue().getAction()).isEqualTo( Settings.ACTION_COLOR_INVERSION_SETTINGS); + assertThat(IntentCaptor.getValue().getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME); } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt index c854920cbf1f..ae4da9de5e48 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt @@ -32,11 +32,10 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.qs.shared.QSSettingsPackageRepository import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.After @@ -49,8 +48,10 @@ import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @@ -68,21 +69,24 @@ class FontScalingTileTest : SysuiTestCase() { @Mock private lateinit var dialog: SystemUIDialog @Mock private lateinit var expandable: Expandable @Mock private lateinit var controller: DialogTransitionAnimator.Controller + @Mock private lateinit var settingsPackageRepository: QSSettingsPackageRepository + + @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable> private lateinit var testableLooper: TestableLooper private lateinit var systemClock: FakeSystemClock private lateinit var backgroundDelayableExecutor: FakeExecutor private lateinit var fontScalingTile: FontScalingTile - @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable> - @Before fun setUp() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) - `when`(qsHost.getContext()).thenReturn(mContext) - `when`(fontScalingDialogDelegate.createDialog()).thenReturn(dialog) - `when`(expandable.dialogTransitionController(any())).thenReturn(controller) + whenever(qsHost.getContext()).thenReturn(mContext) + whenever(fontScalingDialogDelegate.createDialog()).thenReturn(dialog) + whenever(expandable.dialogTransitionController(any())).thenReturn(controller) + whenever(settingsPackageRepository.getSettingsPackageName()) + .thenReturn(SETTINGS_PACKAGE_NAME) systemClock = FakeSystemClock() backgroundDelayableExecutor = FakeExecutor(systemClock) @@ -100,6 +104,7 @@ class FontScalingTileTest : SysuiTestCase() { keyguardStateController, mDialogTransitionAnimator, { fontScalingDialogDelegate }, + settingsPackageRepository, ) fontScalingTile.initialize() testableLooper.processAllMessages() @@ -120,7 +125,7 @@ class FontScalingTileTest : SysuiTestCase() { @Test fun clickTile_screenUnlocked_showDialogAnimationFromView() { - `when`(keyguardStateController.isShowing).thenReturn(false) + whenever(keyguardStateController.isShowing).thenReturn(false) fontScalingTile.click(expandable) testableLooper.processAllMessages() @@ -130,7 +135,7 @@ class FontScalingTileTest : SysuiTestCase() { eq(null), eq(true), eq(true), - eq(false) + eq(false), ) argumentCaptor.value.run() verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean()) @@ -138,7 +143,7 @@ class FontScalingTileTest : SysuiTestCase() { @Test fun clickTile_onLockScreen_neverShowDialogAnimationFromView() { - `when`(keyguardStateController.isShowing).thenReturn(true) + whenever(keyguardStateController.isShowing).thenReturn(true) fontScalingTile.click(expandable) testableLooper.processAllMessages() @@ -148,7 +153,7 @@ class FontScalingTileTest : SysuiTestCase() { eq(null), eq(true), eq(true), - eq(false) + eq(false), ) argumentCaptor.value.run() verify(mDialogTransitionAnimator, never()).show(any(), any(), anyBoolean()) @@ -159,5 +164,10 @@ class FontScalingTileTest : SysuiTestCase() { val intent: Intent? = fontScalingTile.getLongClickIntent() assertThat(intent!!.action).isEqualTo(Settings.ACTION_TEXT_READING_SETTINGS) + assertThat(intent.getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME) + } + + companion object { + private const val SETTINGS_PACKAGE_NAME = "com.android.settings" } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java index 5c6657b98801..cfbc8128282c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java @@ -1,6 +1,6 @@ package com.android.systemui.qs.tiles.dialog; -import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT; +import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT; import static com.google.common.truth.Truth.assertThat; @@ -63,7 +63,7 @@ public class InternetAdapterTest extends SysuiTestCase { @Mock private WifiEntry mWifiEntry; @Mock - private InternetDialogController mInternetDialogController; + private InternetDetailsContentController mInternetDetailsContentController; @Mock private Drawable mWifiDrawable; @Mock @@ -86,7 +86,7 @@ public class InternetAdapterTest extends SysuiTestCase { when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE); when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY); - mInternetAdapter = new InternetAdapter(mInternetDialogController, mScope); + mInternetAdapter = new InternetAdapter(mInternetDetailsContentController, mScope); mViewHolder = mInternetAdapter.onCreateViewHolder(new LinearLayout(mContext), 0); mInternetAdapter.setWifiEntries(Arrays.asList(mWifiEntry), 1 /* wifiEntriesCount */); } @@ -124,7 +124,7 @@ public class InternetAdapterTest extends SysuiTestCase { @Test public void onBindViewHolder_getWifiDrawableNull_noCrash() { - when(mInternetDialogController.getWifiDrawable(any())).thenReturn(null); + when(mInternetDetailsContentController.getWifiDrawable(any())).thenReturn(null); mInternetAdapter.onBindViewHolder(mViewHolder, 0); @@ -133,7 +133,7 @@ public class InternetAdapterTest extends SysuiTestCase { @Test public void onBindViewHolder_getWifiDrawableNotNull_setWifiIconDrawable() { - when(mInternetDialogController.getWifiDrawable(any())).thenReturn(mWifiDrawable); + when(mInternetDetailsContentController.getWifiDrawable(any())).thenReturn(mWifiDrawable); mInternetAdapter.onBindViewHolder(mViewHolder, 0); @@ -232,7 +232,7 @@ public class InternetAdapterTest extends SysuiTestCase { mViewHolder.onWifiClick(mWifiEntry, mock(View.class)); - verify(mInternetDialogController).startActivityForDialog(any()); + verify(mInternetDetailsContentController).startActivityForDialog(any()); verify(mSpyContext, never()).startActivity(any()); } @@ -242,7 +242,7 @@ public class InternetAdapterTest extends SysuiTestCase { mViewHolder.onWifiClick(mWifiEntry, mock(View.class)); - verify(mInternetDialogController).connect(mWifiEntry); + verify(mInternetDetailsContentController).connect(mWifiEntry); } @Test @@ -252,7 +252,7 @@ public class InternetAdapterTest extends SysuiTestCase { mViewHolder.onWifiClick(mWifiEntry, mock(View.class)); - verify(mInternetDialogController).launchWifiDetailsSetting(anyString(), any()); + verify(mInternetDetailsContentController).launchWifiDetailsSetting(anyString(), any()); } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt index 3bc53b273e89..0cf3734dd92b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt @@ -23,29 +23,45 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository +import com.android.systemui.qs.shared.QSSettingsPackageRepository import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.whenever @SmallTest @EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class ColorCorrectionTileUserActionInteractorTest : SysuiTestCase() { + @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var settingsPackageRepository: QSSettingsPackageRepository + private val testUser = UserHandle.CURRENT private val repository = FakeColorCorrectionRepository() private val inputHandler = FakeQSTileIntentUserInputHandler() - private val underTest = - ColorCorrectionUserActionInteractor( - repository, - inputHandler, - ) + private lateinit var underTest: ColorCorrectionUserActionInteractor + + @Before + fun setUp() { + whenever(settingsPackageRepository.getSettingsPackageName()) + .thenReturn(SETTINGS_PACKAGE_NAME) + + underTest = + ColorCorrectionUserActionInteractor(repository, inputHandler, settingsPackageRepository) + } @Test fun handleClickWhenEnabled() = runTest { @@ -86,6 +102,11 @@ class ColorCorrectionTileUserActionInteractorTest : SysuiTestCase() { QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_CORRECTION_SETTINGS) + assertThat(it.intent.getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME) } } + + companion object { + private const val SETTINGS_PACKAGE_NAME = "com.android.settings" + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt index d3095542e90f..9bd4895ac21c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.animation.LaunchableView import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.qs.shared.QSSettingsPackageRepository import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.actions.intentInputs import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx @@ -36,11 +37,7 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel import com.android.systemui.statusbar.phone.FakeKeyguardStateController import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -51,15 +48,14 @@ import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) class FontScalingUserActionInteractorTest : SysuiTestCase() { - private val kosmos = Kosmos() - private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler() - private val keyguardStateController = FakeKeyguardStateController() - - private lateinit var underTest: FontScalingTileUserActionInteractor @Mock private lateinit var fontScalingDialogDelegate: FontScalingDialogDelegate @Mock private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator @@ -67,35 +63,47 @@ class FontScalingUserActionInteractorTest : SysuiTestCase() { @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var expandable: Expandable @Mock private lateinit var controller: DialogTransitionAnimator.Controller + @Mock private lateinit var settingsPackageRepository: QSSettingsPackageRepository @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable> + private val kosmos = Kosmos() + private val scope = kosmos.testScope + private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler() + private val keyguardStateController = FakeKeyguardStateController() + + private lateinit var underTest: FontScalingTileUserActionInteractor + @Before fun setup() { activityStarter = mock<ActivityStarter>() mDialogTransitionAnimator = mock<DialogTransitionAnimator>() dialog = mock<SystemUIDialog>() - fontScalingDialogDelegate = - mock<FontScalingDialogDelegate> { whenever(createDialog()).thenReturn(dialog) } + fontScalingDialogDelegate = mock<FontScalingDialogDelegate>() + whenever(fontScalingDialogDelegate.createDialog()).thenReturn(dialog) controller = mock<DialogTransitionAnimator.Controller>() - expandable = - mock<Expandable> { whenever(dialogTransitionController(any())).thenReturn(controller) } + expandable = mock<Expandable>() + whenever(expandable.dialogTransitionController(any())).thenReturn(controller) + settingsPackageRepository = mock<QSSettingsPackageRepository>() + whenever(settingsPackageRepository.getSettingsPackageName()) + .thenReturn(SETTINGS_PACKAGE_NAME) argumentCaptor = ArgumentCaptor.forClass(Runnable::class.java) underTest = FontScalingTileUserActionInteractor( - kosmos.testScope.coroutineContext, + scope.coroutineContext, qsTileIntentUserActionHandler, { fontScalingDialogDelegate }, keyguardStateController, mDialogTransitionAnimator, - activityStarter + activityStarter, + settingsPackageRepository, ) } @Test fun clickTile_screenUnlocked_showDialogAnimationFromView() = - kosmos.testScope.runTest { + scope.runTest { keyguardStateController.isShowing = false underTest.handleInput(click(FontScalingTileModel, expandable = expandable)) @@ -106,7 +114,7 @@ class FontScalingUserActionInteractorTest : SysuiTestCase() { eq(null), eq(true), eq(true), - eq(false) + eq(false), ) argumentCaptor.value.run() verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean()) @@ -114,7 +122,7 @@ class FontScalingUserActionInteractorTest : SysuiTestCase() { @Test fun clickTile_onLockScreen_neverShowDialogAnimationFromView_butShowsDialog() = - kosmos.testScope.runTest { + scope.runTest { keyguardStateController.isShowing = true underTest.handleInput(click(FontScalingTileModel, expandable = expandable)) @@ -125,7 +133,7 @@ class FontScalingUserActionInteractorTest : SysuiTestCase() { eq(null), eq(true), eq(true), - eq(false) + eq(false), ) argumentCaptor.value.run() verify(mDialogTransitionAnimator, never()).show(any(), any(), anyBoolean()) @@ -134,17 +142,20 @@ class FontScalingUserActionInteractorTest : SysuiTestCase() { @Test fun handleLongClick() = - kosmos.testScope.runTest { + scope.runTest { underTest.handleInput(QSTileInputTestKtx.longClick(FontScalingTileModel)) - Truth.assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1) - val intentInput = qsTileIntentUserActionHandler.intentInputs.last() - val actualIntentAction = intentInput.intent.action - val expectedIntentAction = Settings.ACTION_TEXT_READING_SETTINGS - Truth.assertThat(actualIntentAction).isEqualTo(expectedIntentAction) + assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1) + val it = qsTileIntentUserActionHandler.intentInputs.last() + assertThat(it.intent.action).isEqualTo(Settings.ACTION_TEXT_READING_SETTINGS) + assertThat(it.intent.getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME) } private class FontScalingTileTestView(context: Context) : View(context), LaunchableView { override fun setShouldBlockVisibilityChanges(block: Boolean) {} } + + companion object { + private const val SETTINGS_PACKAGE_NAME = "com.android.settings" + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt index f574f793fbf2..3f77b86971e1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt @@ -23,29 +23,45 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.accessibility.data.repository.FakeColorInversionRepository +import com.android.systemui.qs.shared.QSSettingsPackageRepository import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.whenever @SmallTest @EnabledOnRavenwood @RunWith(AndroidJUnit4::class) class ColorInversionUserActionInteractorTest : SysuiTestCase() { + @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var settingsPackageRepository: QSSettingsPackageRepository + private val testUser = UserHandle.CURRENT private val repository = FakeColorInversionRepository() private val inputHandler = FakeQSTileIntentUserInputHandler() - private val underTest = - ColorInversionUserActionInteractor( - repository, - inputHandler, - ) + private lateinit var underTest: ColorInversionUserActionInteractor + + @Before + fun setUp() { + whenever(settingsPackageRepository.getSettingsPackageName()) + .thenReturn(SETTINGS_PACKAGE_NAME) + + underTest = + ColorInversionUserActionInteractor(repository, inputHandler, settingsPackageRepository) + } @Test fun handleClickWhenEnabled() = runTest { @@ -86,6 +102,11 @@ class ColorInversionUserActionInteractorTest : SysuiTestCase() { QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_INVERSION_SETTINGS) + assertThat(it.intent.getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME) } } + + companion object { + private const val SETTINGS_PACKAGE_NAME = "com.android.settings" + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerStateTest.kt new file mode 100644 index 000000000000..e68045fe470f --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerStateTest.kt @@ -0,0 +1,110 @@ +/* + * 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.chips.ui.compose + +import android.text.format.DateUtils.formatElapsedTime +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.cancelAndJoin +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class ChronometerStateTest : SysuiTestCase() { + + private lateinit var mockTimeSource: MutableTimeSource + + @Before + fun setup() { + mockTimeSource = MutableTimeSource() + } + + @Test + fun initialText_isCorrect() = runTest { + val state = ChronometerState(mockTimeSource, 0L) + assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(0)) + } + + @Test + fun textUpdates_withTime() = runTest { + val startTime = 1000L + val state = ChronometerState(mockTimeSource, startTime) + val job = launch { state.run() } + + val elapsedTime = 5000L + mockTimeSource.time = startTime + elapsedTime + advanceTimeBy(elapsedTime) + assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(elapsedTime / 1000)) + + job.cancelAndJoin() + } + + @Test + fun textUpdates_toLargerValue() = runTest { + val startTime = 1000L + val state = ChronometerState(mockTimeSource, startTime) + val job = launch { state.run() } + + val elapsedTime = 15000L + mockTimeSource.time = startTime + elapsedTime + advanceTimeBy(elapsedTime) + assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(elapsedTime / 1000)) + + job.cancelAndJoin() + } + + @Test + fun textUpdates_afterResettingBase() = runTest { + val initialElapsedTime = 30000L + val startTime = 50000L + val state = ChronometerState(mockTimeSource, startTime) + val job = launch { state.run() } + + mockTimeSource.time = startTime + initialElapsedTime + advanceTimeBy(initialElapsedTime) + assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(initialElapsedTime / 1000)) + + job.cancelAndJoin() + + val newElapsedTime = 5000L + val newStartTime = 100000L + val newState = ChronometerState(mockTimeSource, newStartTime) + val newJob = launch { newState.run() } + + mockTimeSource.time = newStartTime + newElapsedTime + advanceTimeBy(newElapsedTime) + assertThat(newState.currentTimeText).isEqualTo(formatElapsedTime(newElapsedTime / 1000)) + + newJob.cancelAndJoin() + } +} + +/** A fake implementation of [TimeSource] that allows the caller to set the current time */ +class MutableTimeSource(var time: Long = 0L) : TimeSource { + override fun getCurrentTime(): Long { + return time + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt index 009b33b9f808..3515c5649ca0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt @@ -26,10 +26,12 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.fragments.FragmentHostManager import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.plugins.fakeDarkIconDispatcher import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent import com.android.systemui.statusbar.pipeline.shared.ui.composable.StatusBarRootFactory +import com.android.systemui.statusbar.policy.statusBarConfigurationController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.testKosmos @@ -77,6 +79,8 @@ class StatusBarInitializerTest : SysuiTestCase() { componentFactory = mock(HomeStatusBarComponent.Factory::class.java), creationListeners = setOf(), statusBarModePerDisplayRepository = statusBarModePerDisplayRepository, + darkIconDispatcher = kosmos.fakeDarkIconDispatcher, + statusBarConfigurationController = kosmos.statusBarConfigurationController, ) @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt index 18eef33813f6..884c35c3457d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt @@ -51,7 +51,7 @@ class LightBarControllerStoreImplTest : SysuiTestCase() { @Test fun forDisplay_startsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance).start() } @@ -59,7 +59,7 @@ class LightBarControllerStoreImplTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -67,7 +67,7 @@ class LightBarControllerStoreImplTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt index a2c3c66f4448..f37648a639df 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt @@ -56,7 +56,7 @@ class MultiDisplayDarkIconDispatcherStoreTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -64,7 +64,7 @@ class MultiDisplayDarkIconDispatcherStoreTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt index 4a26fdf20e6e..e0a1f273aa44 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt @@ -51,7 +51,7 @@ class MultiDisplayStatusBarContentInsetsProviderStoreTest : SysuiTestCase() { @Test fun forDisplay_startsInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance).start() } @@ -59,7 +59,7 @@ class MultiDisplayStatusBarContentInsetsProviderStoreTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -67,7 +67,7 @@ class MultiDisplayStatusBarContentInsetsProviderStoreTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt index a9920ec5e29b..11fd902fc50c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt @@ -53,7 +53,7 @@ class MultiDisplayStatusBarModeRepositoryStoreTest : SysuiTestCase() { @Test fun forDisplay_startsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance).start() } @@ -61,7 +61,7 @@ class MultiDisplayStatusBarModeRepositoryStoreTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt index e65c04c45382..3cc592c94678 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt @@ -56,7 +56,7 @@ class SystemEventChipAnimationControllerStoreImplTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -64,7 +64,7 @@ class SystemEventChipAnimationControllerStoreImplTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt index d38fb5050c5f..5f154acbaca0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt @@ -48,7 +48,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.SeenNotific import com.android.systemui.statusbar.notification.domain.interactor.lockScreenShowOnlyUnseenNotificationsSetting import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener -import com.android.systemui.statusbar.notification.headsup.headsUpManager +import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.testKosmos import com.android.systemui.util.settings.FakeSettings @@ -625,7 +625,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu val keyguardCoordinator = OriginalUnseenKeyguardCoordinator( dumpManager = kosmos.dumpManager, - headsUpManager = kosmos.headsUpManager, + headsUpManager = kosmos.mockHeadsUpManager, keyguardRepository = kosmos.keyguardRepository, keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor, logger = KeyguardCoordinatorLogger(logcatLogBuffer()), @@ -663,7 +663,8 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu val onHeadsUpChangedListener: OnHeadsUpChangedListener get() = - argumentCaptor { verify(kosmos.headsUpManager).addListener(capture()) }.lastValue + argumentCaptor { verify(kosmos.mockHeadsUpManager).addListener(capture()) } + .lastValue } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt index be20bc1bf9d4..d86c6efce284 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt @@ -225,7 +225,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati val displayId = 123 darkIconRepository.darkState(displayId).value = SysuiDarkIconDispatcher.DarkChange(emptyList(), 0f, 0xAABBCC) - val iconColors by collectLastValue(underTest.iconColors(displayId)) + val iconColors by collectLastValue(underTest.iconColors(displayId)!!) assertThat(iconColors).isNotNull() assertThat(iconColors!!.tint).isEqualTo(0xAABBCC) @@ -241,7 +241,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati val displayId = 321 darkIconRepository.darkState(displayId).value = SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC) - val iconColors by collectLastValue(underTest.iconColors(displayId)) + val iconColors by collectLastValue(underTest.iconColors(displayId)!!) val staticDrawableColor = iconColors?.staticDrawableColor(Rect(6, 6, 7, 7)) assertThat(staticDrawableColor).isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT) } @@ -252,7 +252,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati val displayId = 987 darkIconRepository.darkState(displayId).value = SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC) - val iconColors by collectLastValue(underTest.iconColors(displayId)) + val iconColors by collectLastValue(underTest.iconColors(displayId)!!) assertThat(iconColors!!.staticDrawableColor(Rect(6, 6, 7, 7))) .isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt index 87abd0a4494a..256da253588c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt @@ -22,6 +22,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.shared.NotificationMinimalism +import com.android.systemui.statusbar.notification.shelf.NotificationShelfIconContainer import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState import com.android.systemui.util.mockito.mock import junit.framework.Assert.assertEquals @@ -134,89 +135,208 @@ open class NotificationShelfTest : SysuiTestCase() { @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testAlignment_splitShade_LTR() { // Given: LTR mode, split shade + val width = 100 + val actualWidth = 40 + val iconContainerPadding = 16f val shelfSpy = - prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = false, + splitShade = true, + width = width, + actualWidth = actualWidth, + iconContainerPadding = iconContainerPadding, + ) // Then: shelf should align to end assertTrue(shelfSpy.isAlignedToEnd) assertTrue(shelfSpy.isAlignedToRight) assertTrue(shelfSpy.mBackgroundNormal.alignToEnd) - assertTrue(shelfSpy.mShelfIcons.alignToEnd) + + // Then: icon container should align to end, right + val iconContainer = shelfSpy.shelfIcons as NotificationShelfIconContainer + assertTrue(iconContainer.alignToEnd) + assertTrue(iconContainer.isAlignedToRight) + + // Then: icon container bounds are updated based on the widths and paddings + val actualPaddingStart = iconContainerPadding + val actualPaddingEnd = iconContainerPadding + val expectedLeftBound = width - actualWidth + actualPaddingStart + val expectedRightBound = width - actualPaddingEnd + assertEquals(expectedLeftBound, iconContainer.leftBound) + assertEquals(expectedRightBound, iconContainer.rightBound) } @Test @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testAlignment_nonSplitShade_LTR() { // Given: LTR mode, non split shade + val width = 100 + val actualWidth = 40 + val iconContainerPadding = 16f val shelfSpy = - prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = false, + splitShade = false, + width = width, + actualWidth = actualWidth, + iconContainerPadding = iconContainerPadding, + ) // Then: shelf should not align to end + // left bound of icon container should be 16f (actualPaddingStart) + // right bound of icon container should be 24f (actualWidth - actualPaddingEnd) assertFalse(shelfSpy.isAlignedToEnd) assertFalse(shelfSpy.isAlignedToRight) assertFalse(shelfSpy.mBackgroundNormal.alignToEnd) - assertFalse(shelfSpy.mShelfIcons.alignToEnd) + + // Then: icon container should align to start, left + + val iconContainer = shelfSpy.shelfIcons as NotificationShelfIconContainer + assertFalse(iconContainer.alignToEnd) + assertFalse(iconContainer.isAlignedToRight) + + // Then: icon container bounds are updated based on the widths and paddings + val actualPaddingStart = iconContainerPadding + val actualPaddingEnd = iconContainerPadding + val expectedLeftBound = actualPaddingStart + val expectedRightBound = actualWidth - actualPaddingEnd + assertEquals(expectedLeftBound, iconContainer.leftBound) + assertEquals(expectedRightBound, iconContainer.rightBound) } @Test @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testAlignment_splitShade_RTL() { // Given: RTL mode, split shade + val width = 100 + val actualWidth = 40 + val iconContainerPadding = 16f val shelfSpy = - prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = true, + splitShade = true, + width = width, + actualWidth = actualWidth, + iconContainerPadding = iconContainerPadding, + ) // Then: shelf should align to end, but to left due to RTL + // left bound of icon container should be 16f (actualPaddingStart) + // right bound of icon container should be 24f (actualWidth - actualPaddingEnd) assertTrue(shelfSpy.isAlignedToEnd) assertFalse(shelfSpy.isAlignedToRight) assertTrue(shelfSpy.mBackgroundNormal.alignToEnd) - assertTrue(shelfSpy.mShelfIcons.alignToEnd) + + // Then: icon container should align to end, left + val iconContainer = shelfSpy.shelfIcons as NotificationShelfIconContainer + assertTrue(iconContainer.alignToEnd) + assertFalse(iconContainer.isAlignedToRight) + + // Then: icon container bounds are updated based on the widths and paddings + val actualPaddingStart = iconContainerPadding + val actualPaddingEnd = iconContainerPadding + val expectedLeftBound = actualPaddingStart + val expectedRightBound = actualWidth - actualPaddingEnd + assertEquals(expectedLeftBound, iconContainer.leftBound) + assertEquals(expectedRightBound, iconContainer.rightBound) } @Test @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testAlignment_nonSplitShade_RTL() { // Given: RTL mode, non split shade + val width = 100 + val actualWidth = 40 + val iconContainerPadding = 16f val shelfSpy = - prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = true, + splitShade = false, + width = width, + actualWidth = actualWidth, + iconContainerPadding = iconContainerPadding, + ) // Then: shelf should not align to end, but to right due to RTL assertFalse(shelfSpy.isAlignedToEnd) assertTrue(shelfSpy.isAlignedToRight) assertFalse(shelfSpy.mBackgroundNormal.alignToEnd) - assertFalse(shelfSpy.mShelfIcons.alignToEnd) + + // Then: icon container should align to start, right + val iconContainer = shelfSpy.shelfIcons as NotificationShelfIconContainer + assertFalse(iconContainer.alignToEnd) + assertTrue(iconContainer.isAlignedToRight) + + // Then: icon container bounds are updated based on the widths and paddings + val actualPaddingStart = iconContainerPadding + val actualPaddingEnd = iconContainerPadding + val expectedLeftBound = width - actualWidth + actualPaddingStart + val expectedRightBound = width - actualPaddingEnd + assertEquals(expectedLeftBound, iconContainer.leftBound) + assertEquals(expectedRightBound, iconContainer.rightBound) } @Test @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testGetShelfLeftBound_splitShade_LTR() { // Given: LTR mode, split shade + val width = 100 + val actualWidth = 40 val shelfSpy = - prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = false, + splitShade = true, + width = width, + actualWidth = actualWidth, + ) // When: get the left bound of the shelf val shelfLeftBound = shelfSpy.shelfLeftBound // Then: should be equal to shelf's width - actual width - assertEquals(60f, shelfLeftBound) + val expectedLeftBound = (width - actualWidth).toFloat() + assertEquals(expectedLeftBound, shelfLeftBound) } @Test @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testGetShelfRightBound_splitShade_LTR() { // Given: LTR mode, split shade, width 100, actual width 40 + val width = 100 + val actualWidth = 40 val shelfSpy = - prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = false, + splitShade = true, + width = width, + actualWidth = actualWidth, + ) // Then: the right bound of the shelf should be equal to shelf's width - assertEquals(100f, shelfSpy.shelfRightBound) + val expectedRightBound = width.toFloat() + assertEquals(expectedRightBound, shelfSpy.shelfRightBound) } @Test @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testGetShelfLeftBound_nonSplitShade_LTR() { // Given: LTR mode, non split shade + val width = 100 + val actualWidth = 40 val shelfSpy = - prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = false, + splitShade = false, + width = width, + actualWidth = actualWidth, + ) // When: get the left bound of the shelf val shelfLeftBound = shelfSpy.shelfLeftBound @@ -229,19 +349,35 @@ open class NotificationShelfTest : SysuiTestCase() { @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testGetShelfRightBound_nonSplitShade_LTR() { // Given: LTR mode, non split shade, width 100, actual width 40 + val width = 100 + val actualWidth = 40 val shelfSpy = - prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = false, + splitShade = false, + width = width, + actualWidth = actualWidth, + ) // Then: the right bound of the shelf should be equal to shelf's actual width - assertEquals(40f, shelfSpy.shelfRightBound) + assertEquals(actualWidth.toFloat(), shelfSpy.shelfRightBound) } @Test @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testGetShelfLeftBound_splitShade_RTL() { // Given: RTL mode, split shade + val width = 100 + val actualWidth = 40 val shelfSpy = - prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = true, + splitShade = true, + width = width, + actualWidth = actualWidth, + ) // When: get the left bound of the shelf val shelfLeftBound = shelfSpy.shelfLeftBound @@ -254,36 +390,61 @@ open class NotificationShelfTest : SysuiTestCase() { @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testGetShelfRightBound_splitShade_RTL() { // Given: RTL mode, split shade, width 100, actual width 40 + val width = 100 + val actualWidth = 40 val shelfSpy = - prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = true, + splitShade = true, + width = width, + actualWidth = actualWidth, + ) // Then: the right bound of the shelf should be equal to shelf's actual width - assertEquals(40f, shelfSpy.shelfRightBound) + assertEquals(actualWidth.toFloat(), shelfSpy.shelfRightBound) } @Test @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testGetShelfLeftBound_nonSplitShade_RTL() { // Given: RTL mode, non split shade + val width = 100 + val actualWidth = 40 val shelfSpy = - prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = true, + splitShade = false, + width = width, + actualWidth = actualWidth, + ) // When: get the left bound of the shelf val shelfLeftBound = shelfSpy.shelfLeftBound // Then: should be equal to shelf's width - actual width - assertEquals(60f, shelfLeftBound) + val expectedLeftBound = (width - actualWidth).toFloat() + assertEquals(expectedLeftBound, shelfLeftBound) } @Test @EnableFlags(NotificationMinimalism.FLAG_NAME) fun testGetShelfRightBound_nonSplitShade_RTL() { // Given: LTR mode, non split shade, width 100, actual width 40 + val width = 100 + val actualWidth = 40 val shelfSpy = - prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40) + prepareShelfSpy( + shelf, + rtl = true, + splitShade = false, + width = width, + actualWidth = actualWidth, + ) // Then: the right bound of the shelf should be equal to shelf's width - assertEquals(100f, shelfSpy.shelfRightBound) + assertEquals(width.toFloat(), shelfSpy.shelfRightBound) } private fun prepareShelfSpy( @@ -292,12 +453,23 @@ open class NotificationShelfTest : SysuiTestCase() { splitShade: Boolean, width: Int, actualWidth: Int, + iconContainerPadding: Float? = null, ): NotificationShelf { val shelfSpy = spy(shelf) whenever(shelfSpy.isLayoutRtl).thenReturn(rtl) whenever(ambientState.useSplitShade).thenReturn(splitShade) - whenever(shelfSpy.width).thenReturn(width) + shelfSpy.layout(0, 0, width, 5) + shelfSpy.mShelfIcons.layout(0, 0, width, 5) + iconContainerPadding?.let { + shelfSpy.mShelfIcons.actualPaddingStart = it + shelfSpy.mShelfIcons.setActualPaddingEnd(it) + } shelfSpy.setActualWidth(actualWidth.toFloat()) + + val iconContainerSpy = spy(shelf.mShelfIcons) + whenever(iconContainerSpy.isLayoutRtl).thenReturn(rtl) + whenever(shelfSpy.shelfIcons).thenReturn(iconContainerSpy) + return shelfSpy } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt index f4c254562420..216f51d992d0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt @@ -38,7 +38,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor import com.android.systemui.statusbar.notification.headsup.PinnedStatus -import com.android.systemui.statusbar.notification.headsup.headsUpManager +import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation @@ -75,7 +75,7 @@ class HeadsUpAppearanceControllerTest : SysuiTestCase() { private val keyguardStateController = kosmos.keyguardStateController private val commandQueue = kosmos.commandQueue private val notificationRoundnessManager = mock<NotificationRoundnessManager>() - private var headsUpManager = kosmos.headsUpManager + private var headsUpManager = kosmos.mockHeadsUpManager private lateinit var testHelper: NotificationTestHelper private lateinit var row: ExpandableNotificationRow diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt index 90506a1b9a7f..d16372611e88 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt @@ -56,7 +56,7 @@ class MultiDisplayAutoHideControllerStoreTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -64,7 +64,7 @@ class MultiDisplayAutoHideControllerStoreTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt index 2d9880a48f80..659d91a95aa8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt @@ -39,7 +39,7 @@ class LightsOutInteractorTest : SysuiTestCase() { fun isLowProfile_lightsOutStatusBarMode_false() = runTest { statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.LIGHTS_OUT - val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)!!) assertThat(actual).isTrue() } @@ -49,7 +49,7 @@ class LightsOutInteractorTest : SysuiTestCase() { statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.LIGHTS_OUT_TRANSPARENT - val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)!!) assertThat(actual).isTrue() } @@ -58,7 +58,7 @@ class LightsOutInteractorTest : SysuiTestCase() { fun isLowProfile_transparentStatusBarMode_false() = runTest { statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT - val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)!!) assertThat(actual).isFalse() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt index 7a9d0179b239..769f012bfdf7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt @@ -53,7 +53,7 @@ class MultiDisplayStatusBarWindowControllerStoreTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -61,7 +61,7 @@ class MultiDisplayStatusBarWindowControllerStoreTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt index 8972f3ec71af..8b526bb1d55b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt @@ -30,12 +30,14 @@ import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecogn import com.android.systemui.touchpad.ui.gesture.FakeVelocityTracker import com.google.common.truth.Truth.assertThat import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @SmallTest +@Ignore("b/386412866") @RunWith(ParameterizedAndroidJunit4::class) class ThreeFingerGestureRecognizerTest( private val recognizer: GestureRecognizer, diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index bad5711a1ccc..327075967dba 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -33,15 +33,25 @@ app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container" app:layout_constraintTop_toTopOf="@id/volume_ringer_drawer" /> + <View + android:id="@+id/volume_ringer_horizontal_background" + android:layout_width="0dp" + android:layout_height="0dp" + android:background="@drawable/volume_dialog_background" + app:layout_constraintBottom_toTopOf="@id/volume_dialog_main_slider_container" + app:layout_constraintEnd_toEndOf="@id/volume_ringer_drawer" + app:layout_constraintStart_toStartOf="@id/volume_ringer_drawer" + app:layout_constraintTop_toTopOf="@id/volume_dialog_background" /> + <include android:id="@id/volume_ringer_drawer" layout="@layout/volume_ringer_drawer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/volume_dialog_components_spacing" + android:layout_marginRight="@dimen/volume_dialog_ringer_drawer_diff_right_margin" app:layout_constraintBottom_toTopOf="@id/volume_dialog_main_slider_container" app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container" - app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="1" /> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 6994a55cdbcd..11327b648ddc 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -2116,6 +2116,8 @@ <dimen name="volume_dialog_background_square_corner_radius">12dp</dimen> + <dimen name="volume_dialog_ringer_drawer_left_margin">10dp</dimen> + <dimen name="volume_dialog_ringer_drawer_diff_right_margin">6dp</dimen> <dimen name="volume_dialog_ringer_drawer_button_size">@dimen/volume_dialog_button_size</dimen> <dimen name="volume_dialog_ringer_drawer_button_icon_radius">10dp</dimen> <dimen name="volume_dialog_ringer_selected_button_background_radius">20dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java index 41b9d3372392..5f0acfa644dc 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java @@ -19,6 +19,7 @@ package com.android.systemui.accessibility.floatingmenu; import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; +import android.annotation.Nullable; import android.content.Context; import android.hardware.display.DisplayManager; import android.os.Handler; @@ -35,6 +36,7 @@ import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.AccessibilityButtonModeObserver.AccessibilityButtonMode; import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver; @@ -61,6 +63,7 @@ public class AccessibilityFloatingMenuController implements private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager; private final DisplayManager mDisplayManager; private final AccessibilityManager mAccessibilityManager; + private final HearingAidDeviceManager mHearingAidDeviceManager; private final SecureSettings mSecureSettings; private final DisplayTracker mDisplayTracker; @@ -107,6 +110,7 @@ public class AccessibilityFloatingMenuController implements AccessibilityManager accessibilityManager, AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver, AccessibilityButtonModeObserver accessibilityButtonModeObserver, + @Nullable HearingAidDeviceManager hearingAidDeviceManager, KeyguardUpdateMonitor keyguardUpdateMonitor, SecureSettings secureSettings, DisplayTracker displayTracker, @@ -119,6 +123,7 @@ public class AccessibilityFloatingMenuController implements mAccessibilityManager = accessibilityManager; mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver; mAccessibilityButtonModeObserver = accessibilityButtonModeObserver; + mHearingAidDeviceManager = hearingAidDeviceManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mSecureSettings = secureSettings; mDisplayTracker = displayTracker; @@ -201,7 +206,7 @@ public class AccessibilityFloatingMenuController implements TYPE_NAVIGATION_BAR_PANEL, /* options= */ null); mFloatingMenu = new MenuViewLayerController(windowContext, mWindowManager, mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings, - mNavigationModeController); + mNavigationModeController, mHearingAidDeviceManager); } mFloatingMenu.show(); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java index ffb5f3d47bcc..121b51f768e7 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java @@ -30,6 +30,7 @@ import static com.android.systemui.accessibility.floatingmenu.MenuViewAppearance import android.annotation.FloatRange; import android.annotation.IntDef; +import android.annotation.Nullable; import android.content.ComponentCallbacks; import android.content.Context; import android.content.pm.ActivityInfo; @@ -49,6 +50,8 @@ import androidx.annotation.NonNull; import com.android.internal.accessibility.dialog.AccessibilityTarget; import com.android.internal.annotations.VisibleForTesting; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; +import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Prefs; import com.android.systemui.util.settings.SecureSettings; @@ -80,8 +83,11 @@ class MenuInfoRepository { private final AccessibilityManager mAccessibilityManager; private final AccessibilityManager.AccessibilityServicesStateChangeListener mA11yServicesStateChangeListener = manager -> onTargetFeaturesChanged(); + private final HearingAidDeviceManager mHearingAidDeviceManager; + private final HearingAidDeviceManager.ConnectionStatusListener + mHearingDeviceStatusListener = this::onDevicesConnectionStatusChanged; private final Handler mHandler = new Handler(Looper.getMainLooper()); - private final OnSettingsContentsChanged mSettingsContentsCallback; + private final OnContentsChanged mSettingsContentsCallback; private final SecureSettings mSecureSettings; private Position mPercentagePosition; @@ -148,12 +154,14 @@ class MenuInfoRepository { }; MenuInfoRepository(Context context, AccessibilityManager accessibilityManager, - OnSettingsContentsChanged settingsContentsChanged, SecureSettings secureSettings) { + OnContentsChanged settingsContentsChanged, SecureSettings secureSettings, + @Nullable HearingAidDeviceManager hearingAidDeviceManager) { mContext = context; mAccessibilityManager = accessibilityManager; mConfiguration = new Configuration(context.getResources().getConfiguration()); mSettingsContentsCallback = settingsContentsChanged; mSecureSettings = secureSettings; + mHearingAidDeviceManager = hearingAidDeviceManager; mPercentagePosition = getStartPosition(); } @@ -185,6 +193,14 @@ class MenuInfoRepository { callback.onReady(getTargets(mContext, SOFTWARE)); } + void loadHearingDeviceStatus(OnInfoReady<Integer> callback) { + if (mHearingAidDeviceManager != null) { + callback.onReady(mHearingAidDeviceManager.getDevicesConnectionStatus()); + } else { + callback.onReady(HearingAidDeviceManager.ConnectionStatus.NO_DEVICE_BONDED); + } + } + void loadMenuSizeType(OnInfoReady<Integer> callback) { callback.onReady(getMenuSizeTypeFromSettings()); } @@ -222,8 +238,8 @@ class MenuInfoRepository { } private void onTargetFeaturesChanged() { - mSettingsContentsCallback.onTargetFeaturesChanged( - getTargets(mContext, SOFTWARE)); + List<AccessibilityTarget> targets = getTargets(mContext, SOFTWARE); + mSettingsContentsCallback.onTargetFeaturesChanged(targets); } private Position getStartPosition() { @@ -269,6 +285,24 @@ class MenuInfoRepository { mAccessibilityManager.addAccessibilityServicesStateChangeListener( mA11yServicesStateChangeListener); } + + if (com.android.settingslib.flags.Flags.hearingDeviceSetConnectionStatusReport()) { + registerConnectionStatusListener(); + } + } + + private void registerConnectionStatusListener() { + if (mHearingAidDeviceManager != null) { + mHearingAidDeviceManager.registerConnectionStatusListener( + mHearingDeviceStatusListener, ThreadUtils.getBackgroundExecutor()); + } + } + + private void unregisterConnectionStatusListener() { + if (mHearingAidDeviceManager != null) { + mHearingAidDeviceManager.unregisterConnectionStatusListener( + mHearingDeviceStatusListener); + } } void unregisterObserversAndCallbacks() { @@ -281,14 +315,18 @@ class MenuInfoRepository { mAccessibilityManager.removeAccessibilityServicesStateChangeListener( mA11yServicesStateChangeListener); } + + unregisterConnectionStatusListener(); } - interface OnSettingsContentsChanged { + interface OnContentsChanged { void onTargetFeaturesChanged(List<AccessibilityTarget> newTargetFeatures); void onSizeTypeChanged(int newSizeType); void onFadeEffectInfoChanged(MenuFadeEffectInfo fadeEffectInfo); + + void onDevicesConnectionStatusChanged(@HearingAidDeviceManager.ConnectionStatus int status); } interface OnInfoReady<T> { @@ -311,4 +349,9 @@ class MenuInfoRepository { ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY_VALUE, UserHandle.USER_CURRENT); } + + private void onDevicesConnectionStatusChanged( + @HearingAidDeviceManager.ConnectionStatus int status) { + mSettingsContentsCallback.onDevicesConnectionStatusChanged(status); + } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java index 9d9e7dfb7032..23fc546dd9b3 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java @@ -37,6 +37,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.internal.accessibility.dialog.AccessibilityTarget; import com.android.modules.expresslog.Counter; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.Flags; import com.android.systemui.util.settings.SecureSettings; @@ -65,8 +66,11 @@ class MenuView extends FrameLayout implements private final Observer<Integer> mSizeTypeObserver = this::onSizeTypeChanged; private final Observer<List<AccessibilityTarget>> mTargetFeaturesObserver = this::onTargetFeaturesChanged; + private final Observer<Integer> mHearingDeviceStatusObserver = + this::updateHearingDeviceStatus; + private final Observer<Integer> mHearingDeviceTargetIndexObserver = + this::updateHearingDeviceTargetIndex; private final MenuViewAppearance mMenuViewAppearance; - private boolean mIsMoveToTucked; private final MenuAnimationController mMenuAnimationController; @@ -357,6 +361,11 @@ class MenuView extends FrameLayout implements mMenuViewModel.getTargetFeaturesData().observeForever(mTargetFeaturesObserver); mMenuViewModel.getSizeTypeData().observeForever(mSizeTypeObserver); mMenuViewModel.getMoveToTuckedData().observeForever(mMoveToTuckedObserver); + if (com.android.settingslib.flags.Flags.hearingDeviceSetConnectionStatusReport()) { + mMenuViewModel.loadHearingDeviceStatus().observeForever(mHearingDeviceStatusObserver); + mMenuViewModel.getHearingDeviceTargetIndexData().observeForever( + mHearingDeviceTargetIndexObserver); + } setVisibility(VISIBLE); mMenuViewModel.registerObserversAndCallbacks(); getViewTreeObserver().addOnComputeInternalInsetsListener(this); @@ -371,6 +380,9 @@ class MenuView extends FrameLayout implements mMenuViewModel.getTargetFeaturesData().removeObserver(mTargetFeaturesObserver); mMenuViewModel.getSizeTypeData().removeObserver(mSizeTypeObserver); mMenuViewModel.getMoveToTuckedData().removeObserver(mMoveToTuckedObserver); + mMenuViewModel.getHearingDeviceStatusData().removeObserver(mHearingDeviceStatusObserver); + mMenuViewModel.getHearingDeviceTargetIndexData().removeObserver( + mHearingDeviceTargetIndexObserver); mMenuViewModel.unregisterObserversAndCallbacks(); getViewTreeObserver().removeOnComputeInternalInsetsListener(this); getViewTreeObserver().removeOnDrawListener(mSystemGestureExcludeUpdater); @@ -421,6 +433,14 @@ class MenuView extends FrameLayout implements parentView.setSystemGestureExclusionRects(Collections.singletonList(mBoundsInParent)); } + private void updateHearingDeviceStatus(@HearingAidDeviceManager.ConnectionStatus int status) { + // TODO: b/357882387 - To update status drawable according to the status here. + } + + private void updateHearingDeviceTargetIndex(int position) { + // TODO: b/357882387 - To update status drawable according to the status here. + } + /** * Interface definition for the {@link AccessibilityTarget} list changes. */ diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java index cb96e7859fba..184518ac35eb 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java @@ -24,6 +24,7 @@ import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import com.android.app.viewcapture.ViewCaptureAwareWindowManager; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.util.settings.SecureSettings; @@ -39,11 +40,12 @@ class MenuViewLayerController implements IAccessibilityFloatingMenu { MenuViewLayerController(Context context, WindowManager windowManager, ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, AccessibilityManager accessibilityManager, SecureSettings secureSettings, - NavigationModeController navigationModeController) { + NavigationModeController navigationModeController, + HearingAidDeviceManager hearingAidDeviceManager) { mWindowManager = viewCaptureAwareWindowManager; MenuViewModel menuViewModel = new MenuViewModel( - context, accessibilityManager, secureSettings); + context, accessibilityManager, secureSettings, hearingAidDeviceManager); MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context, windowManager); mMenuViewLayer = new MenuViewLayer(context, windowManager, accessibilityManager, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java index f924784a5535..8b7d6a18bb85 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java @@ -16,13 +16,20 @@ package com.android.systemui.accessibility.floatingmenu; +import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; + +import static java.util.Collections.emptyList; + +import android.content.ComponentName; import android.content.Context; import android.view.accessibility.AccessibilityManager; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Transformations; import com.android.internal.accessibility.dialog.AccessibilityTarget; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.util.settings.SecureSettings; import java.util.List; @@ -31,9 +38,9 @@ import java.util.List; * The view model provides the menu information from the repository{@link MenuInfoRepository} for * the menu view{@link MenuView}. */ -class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged { +class MenuViewModel implements MenuInfoRepository.OnContentsChanged { private final MutableLiveData<List<AccessibilityTarget>> mTargetFeaturesData = - new MutableLiveData<>(); + new MutableLiveData<>(emptyList()); private final MutableLiveData<Integer> mSizeTypeData = new MutableLiveData<>(); private final MutableLiveData<MenuFadeEffectInfo> mFadeEffectInfoData = new MutableLiveData<>(); @@ -41,12 +48,18 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged { private final MutableLiveData<Boolean> mDockTooltipData = new MutableLiveData<>(); private final MutableLiveData<Boolean> mMigrationTooltipData = new MutableLiveData<>(); private final MutableLiveData<Position> mPercentagePositionData = new MutableLiveData<>(); + private final MutableLiveData<Integer> mHearingDeviceStatusData = new MutableLiveData<>( + HearingAidDeviceManager.ConnectionStatus.NO_DEVICE_BONDED); + private final LiveData<Integer> mHearingDeviceTargetIndex = Transformations.map( + mTargetFeaturesData, this::getHearingDeviceTargetIndex); + private final MenuInfoRepository mInfoRepository; MenuViewModel(Context context, AccessibilityManager accessibilityManager, - SecureSettings secureSettings) { + SecureSettings secureSettings, HearingAidDeviceManager hearingAidDeviceManager) { mInfoRepository = new MenuInfoRepository(context, - accessibilityManager, /* settingsContentsChanged= */ this, secureSettings); + accessibilityManager, /* settingsContentsChanged= */ this, secureSettings, + hearingAidDeviceManager); } @Override @@ -64,6 +77,12 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged { mFadeEffectInfoData.setValue(fadeEffectInfo); } + @Override + public void onDevicesConnectionStatusChanged( + @HearingAidDeviceManager.ConnectionStatus int status) { + mHearingDeviceStatusData.postValue(status); + } + void updateMenuMoveToTucked(boolean isMoveToTucked) { mInfoRepository.updateMoveToTucked(isMoveToTucked); } @@ -115,6 +134,19 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged { return mTargetFeaturesData; } + LiveData<Integer> loadHearingDeviceStatus() { + mInfoRepository.loadHearingDeviceStatus(mHearingDeviceStatusData::setValue); + return mHearingDeviceStatusData; + } + + LiveData<Integer> getHearingDeviceStatusData() { + return mHearingDeviceStatusData; + } + + LiveData<Integer> getHearingDeviceTargetIndexData() { + return mHearingDeviceTargetIndex; + } + void registerObserversAndCallbacks() { mInfoRepository.registerObserversAndCallbacks(); } @@ -122,4 +154,16 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged { void unregisterObserversAndCallbacks() { mInfoRepository.unregisterObserversAndCallbacks(); } + + private int getHearingDeviceTargetIndex(List<AccessibilityTarget> targetList) { + final int listSize = targetList.size(); + for (int index = 0; index < listSize; index++) { + AccessibilityTarget target = targetList.get(index); + if (ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.equals( + ComponentName.unflattenFromString(target.getId()))) { + return index; + } + } + return -1; + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SettingsLibraryModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SettingsLibraryModule.java index 14626e1e6515..e72dfadd620f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SettingsLibraryModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SettingsLibraryModule.java @@ -22,6 +22,7 @@ import android.content.Context; import android.os.Handler; import android.os.UserHandle; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.dagger.qualifiers.Background; @@ -41,4 +42,16 @@ public class SettingsLibraryModule { @Background Handler bgHandler) { return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL); } + + @SuppressLint("MissingPermission") + @SysUISingleton + @Provides + @Nullable + static HearingAidDeviceManager provideHearingAidDeviceManager( + @Nullable LocalBluetoothManager localBluetoothManager) { + if (localBluetoothManager == null) { + return null; + } + return localBluetoothManager.getCachedDeviceManager().getHearingAidDeviceManager(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt index f310b30c1a81..3390640fa6c6 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt @@ -18,6 +18,8 @@ package com.android.systemui.display.data.repository import android.annotation.SuppressLint import android.content.Context +import android.os.Bundle +import android.util.Log import android.view.Display import android.view.LayoutInflater import android.view.WindowManager @@ -39,14 +41,13 @@ import kotlinx.coroutines.CoroutineScope interface DisplayWindowPropertiesRepository { /** - * Returns a [DisplayWindowProperties] instance for a given display id and window type. - * - * @throws IllegalArgumentException if no display with the given display id exists. + * Returns a [DisplayWindowProperties] instance for a given display id and window type, or null + * if no display with the given display id exists. */ fun get( displayId: Int, @WindowManager.LayoutParams.WindowType windowType: Int, - ): DisplayWindowProperties + ): DisplayWindowProperties? } @SysUISingleton @@ -72,12 +73,10 @@ constructor( override fun get( displayId: Int, @WindowManager.LayoutParams.WindowType windowType: Int, - ): DisplayWindowProperties { - val display = - displayRepository.getDisplay(displayId) - ?: throw IllegalArgumentException("Display with id $displayId doesn't exist") + ): DisplayWindowProperties? { + val display = displayRepository.getDisplay(displayId) ?: return null return properties.get(displayId, windowType) - ?: create(display, windowType).also { properties.put(displayId, windowType, it) } + ?: create(display, windowType)?.also { properties.put(displayId, windowType, it) } } override fun start() { @@ -88,7 +87,7 @@ constructor( } } - private fun create(display: Display, windowType: Int): DisplayWindowProperties { + private fun create(display: Display, windowType: Int): DisplayWindowProperties? { val displayId = display.displayId return if (displayId == Display.DEFAULT_DISPLAY) { // For the default display, we can just reuse the global/application properties. @@ -102,6 +101,14 @@ constructor( ) } else { val context = createWindowContext(display, windowType) + if (context.displayId != display.displayId) { + Log.e( + TAG, + "Returning null because the new context doesn't have the desired display id " + + "${display.displayId}. Display was already removed.", + ) + return null + } @SuppressLint("NonInjectedService") // Need to manually get the service val windowManager = context.getSystemService(WindowManager::class.java) as WindowManager val layoutInflater = LayoutInflater.from(context) @@ -110,11 +117,15 @@ constructor( } private fun createWindowContext(display: Display, windowType: Int): Context = - globalContext.createWindowContext(display, windowType, /* options= */ null).also { + globalContext.createWindowContext(display, windowType, /* options= */ Bundle.EMPTY).also { it.setTheme(R.style.Theme_SystemUI) } override fun dump(pw: PrintWriter, args: Array<out String>) { pw.write("perDisplayContexts: $properties") } + + private companion object { + const val TAG = "DisplayWindowPropsRepo" + } } diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt index 711534f9dbf0..564588c159bd 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt @@ -16,6 +16,7 @@ package com.android.systemui.display.data.repository +import android.util.Log import android.view.Display import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable @@ -36,12 +37,10 @@ interface PerDisplayStore<T> { val defaultDisplay: T /** - * Returns an instance for a specific display id. - * - * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing - * displays. + * Returns an instance for a specific display id, or null if [displayId] doesn't match the id of + * any existing displays. */ - fun forDisplay(displayId: Int): T + fun forDisplay(displayId: Int): T? } abstract class PerDisplayStoreImpl<T>( @@ -58,7 +57,7 @@ abstract class PerDisplayStoreImpl<T>( * Note that the id of the default display is [Display.DEFAULT_DISPLAY]. */ override val defaultDisplay: T - get() = forDisplay(Display.DEFAULT_DISPLAY) + get() = forDisplay(Display.DEFAULT_DISPLAY)!! /** * Returns an instance for a specific display id. @@ -66,16 +65,30 @@ abstract class PerDisplayStoreImpl<T>( * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing * displays. */ - override fun forDisplay(displayId: Int): T { + override fun forDisplay(displayId: Int): T? { if (displayRepository.getDisplay(displayId) == null) { - throw IllegalArgumentException("Display with id $displayId doesn't exist.") + Log.e(TAG, "<${instanceClass.simpleName}>: Display with id $displayId doesn't exist.") + return null } - return perDisplayInstances.computeIfAbsent(displayId) { - createInstanceForDisplay(displayId) + synchronized(perDisplayInstances) { + val existingInstance = perDisplayInstances[displayId] + if (existingInstance != null) { + return existingInstance + } + val newInstance = createInstanceForDisplay(displayId) + if (newInstance == null) { + Log.e( + TAG, + "<${instanceClass.simpleName}> returning null because createInstanceForDisplay($displayId) returned null.", + ) + } else { + perDisplayInstances[displayId] = newInstance + } + return newInstance } } - protected abstract fun createInstanceForDisplay(displayId: Int): T + protected abstract fun createInstanceForDisplay(displayId: Int): T? override fun start() { val instanceType = instanceClass.simpleName @@ -98,6 +111,10 @@ abstract class PerDisplayStoreImpl<T>( override fun dump(pw: PrintWriter, args: Array<out String>) { pw.println(perDisplayInstances) } + + private companion object { + const val TAG = "PerDisplayStore" + } } class SingleDisplayStore<T>(defaultInstance: T) : PerDisplayStore<T> { diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt index 22e467bd5e3c..99c9ca98bae9 100644 --- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt @@ -33,7 +33,7 @@ interface DisplayWindowPropertiesInteractor { * * @throws IllegalArgumentException if no display with the given display id exists. */ - fun getForStatusBar(displayId: Int): DisplayWindowProperties + fun getForStatusBar(displayId: Int): DisplayWindowProperties? } @SysUISingleton @@ -42,7 +42,7 @@ class DisplayWindowPropertiesInteractorImpl constructor(private val repo: DisplayWindowPropertiesRepository) : DisplayWindowPropertiesInteractor { - override fun getForStatusBar(displayId: Int): DisplayWindowProperties { + override fun getForStatusBar(displayId: Int): DisplayWindowProperties? { return repo.get(displayId, TYPE_STATUS_BAR) } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java index c895732f79f6..f9df67661b49 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java @@ -720,9 +720,24 @@ public class NavigationBar extends ViewController<NavigationBarView> implements if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView); - mViewCaptureAwareWindowManager.addView(mFrame, - getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration - .getRotation())); + try { + mViewCaptureAwareWindowManager.addView( + mFrame, + getBarLayoutParams( + mContext.getResources() + .getConfiguration() + .windowConfiguration + .getRotation())); + } catch (WindowManager.InvalidDisplayException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + Log.e( + TAG, + "Unable to add view to WindowManager. Display with id " + + mDisplayId + + " does not exist anymore", + e); + } mDisplayId = mContext.getDisplayId(); mIsOnDefaultDisplay = mDisplayId == mDisplayTracker.getDefaultDisplayId(); @@ -764,6 +779,15 @@ public class NavigationBar extends ViewController<NavigationBarView> implements Trace.beginSection("NavigationBar#removeViewImmediate"); try { mViewCaptureAwareWindowManager.removeViewImmediate(mView.getRootView()); + } catch (IllegalArgumentException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + // When that happens, adding the View to WindowManager fails, and therefore removing + // it here will fail too, since it wasn't added in the first place. + Log.e( + TAG, + "Failed to removed view from WindowManager. The View wasn't attached.", + e); } finally { Trace.endSection(); } @@ -859,7 +883,15 @@ public class NavigationBar extends ViewController<NavigationBarView> implements if (mOrientationHandle != null) { resetSecondaryHandle(); getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener); - mViewCaptureAwareWindowManager.removeView(mOrientationHandle); + try { + mViewCaptureAwareWindowManager.removeView(mOrientationHandle); + } catch (IllegalArgumentException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + // When that happens, adding the View to WindowManager fails, and therefore removing + // it here will fail too, since it wasn't added in the first place. + Log.e(TAG, "Trying to remove a View that is not attached", e); + } mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener( mOrientationHandleGlobalLayoutListener); } @@ -930,7 +962,18 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId()); mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; - mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams); + try { + mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams); + } catch (WindowManager.InvalidDisplayException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + Log.e( + TAG, + "Unable to add view to WindowManager. Display with id " + + mDisplayId + + " does not exist anymore", + e); + } mOrientationHandle.setVisibility(View.GONE); logNavbarOrientation("initSecondaryHomeHandleForRotation"); diff --git a/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt b/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt index ffeec4e0480c..c302cb21f77f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt @@ -79,6 +79,14 @@ object QsDetailedView { @JvmStatic inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) + /** + * Called to ensure code is only run when the flag is enabled. This will throw an exception if + * the flag is not enabled to ensure that the refactor author catches issues in testing. + * Caution!! Using this check incorrectly will cause crashes in nextfood builds! + */ + @JvmStatic + inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME) + /** Returns a developer-readable string that describes the current requirement list. */ @JvmStatic fun requirementDescription(): String { diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt index 9677d47b38e8..3b3fb9b10052 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt @@ -23,6 +23,7 @@ import com.android.systemui.qs.pipeline.domain.interactor.AutoAddInteractor import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractor import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository +import com.android.systemui.qs.shared.QSSettingsPackageRepository import javax.inject.Inject @SysUISingleton @@ -33,12 +34,14 @@ constructor( private val accessibilityTilesInteractor: AccessibilityTilesInteractor, private val autoAddInteractor: AutoAddInteractor, private val featureFlags: QSPipelineFlagsRepository, + private val settingsPackageRepository: QSSettingsPackageRepository, private val restoreReconciliationInteractor: RestoreReconciliationInteractor, ) : CoreStartable { override fun start() { accessibilityTilesInteractor.init(currentTilesInteractor) autoAddInteractor.init(currentTilesInteractor) + settingsPackageRepository.init() restoreReconciliationInteractor.start() } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/shared/QSSettingsPackageRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/shared/QSSettingsPackageRepository.kt new file mode 100644 index 000000000000..592e9dadcb92 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/shared/QSSettingsPackageRepository.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.shared + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.UserHandle +import android.provider.Settings +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.user.data.repository.UserRepository +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +/** + * Provides the cached package name of the default Settings application. + * + * This repository retrieves and stores the package name to avoid repeated lookups. The package name + * is retrieved in a background thread when the `init()` method is called. + */ +@SysUISingleton +@Suppress("ShadeDisplayAwareContextChecker") +class QSSettingsPackageRepository +@Inject +constructor( + private val context: Context, + @Background private val backgroundScope: CoroutineScope, + private val userRepository: UserRepository, +) { + private var settingsPackageName: String? = null + + /** + * Initializes the repository by determining and caching the package name of the Settings app. + */ + fun init() { + backgroundScope.launch { + val mainUserId = userRepository.mainUserId + val mainUserContext = + context.createContextAsUser(UserHandle.of(mainUserId), /* flags */ 0) + val pm = mainUserContext.packageManager + settingsPackageName = + pm.queryIntentActivities( + Intent(Settings.ACTION_SETTINGS), + PackageManager.MATCH_SYSTEM_ONLY or PackageManager.MATCH_DEFAULT_ONLY, + ) + .firstOrNull() + ?.activityInfo + ?.packageName ?: DEFAULT_SETTINGS_PACKAGE_NAME + } + } + + /** + * Returns the cached package name of the Settings app. + * + * If the package name has not been initialized yet, this method will return the default + * Settings package name. + * + * @return The package name of the Settings app. + */ + fun getSettingsPackageName(): String { + return settingsPackageName ?: DEFAULT_SETTINGS_PACKAGE_NAME + } + + companion object { + private const val DEFAULT_SETTINGS_PACKAGE_NAME = "com.android.settings" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java index c2e609ddfc3a..1f936810da28 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java @@ -38,6 +38,7 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.shared.QSSettingsPackageRepository; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; @@ -53,6 +54,7 @@ public class ColorCorrectionTile extends QSTileImpl<BooleanState> { @Nullable private Icon mIcon = null; private final UserSettingObserver mSetting; + private final QSSettingsPackageRepository mQSSettingsPackageRepository; @Inject public ColorCorrectionTile( @@ -66,11 +68,13 @@ public class ColorCorrectionTile extends QSTileImpl<BooleanState> { ActivityStarter activityStarter, QSLogger qsLogger, UserTracker userTracker, - SecureSettings secureSettings + SecureSettings secureSettings, + QSSettingsPackageRepository qsSettingsPackageRepository ) { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); + mQSSettingsPackageRepository = qsSettingsPackageRepository; mSetting = new UserSettingObserver(secureSettings, mHandler, Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, userTracker.getUserId()) { @Override @@ -106,7 +110,8 @@ public class ColorCorrectionTile extends QSTileImpl<BooleanState> { @Override public Intent getLongClickIntent() { - return new Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS); + return new Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS) + .setPackage(mQSSettingsPackageRepository.getSettingsPackageName()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index ce80133e67a2..38e9a205e9ed 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -39,6 +39,7 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.UserSettingObserver; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.shared.QSSettingsPackageRepository; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; @@ -51,6 +52,7 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> { public static final String TILE_SPEC = "inversion"; private final UserSettingObserver mSetting; + private final QSSettingsPackageRepository mQSSettingsPackageRepository; @Inject public ColorInversionTile( @@ -64,11 +66,13 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> { ActivityStarter activityStarter, QSLogger qsLogger, UserTracker userTracker, - SecureSettings secureSettings + SecureSettings secureSettings, + QSSettingsPackageRepository qsSettingsPackageRepository ) { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); + mQSSettingsPackageRepository = qsSettingsPackageRepository; mSetting = new UserSettingObserver(secureSettings, mHandler, Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, userTracker.getUserId()) { @Override @@ -104,7 +108,8 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> { @Override public Intent getLongClickIntent() { - return new Intent(Settings.ACTION_COLOR_INVERSION_SETTINGS); + return new Intent(Settings.ACTION_COLOR_INVERSION_SETTINGS) + .setPackage(mQSSettingsPackageRepository.getSettingsPackageName()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt index 43e84a0ee2b4..4050f2a1cb4e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt @@ -34,6 +34,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.qs.shared.QSSettingsPackageRepository import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialog @@ -56,6 +57,7 @@ constructor( private val keyguardStateController: KeyguardStateController, private val dialogTransitionAnimator: DialogTransitionAnimator, private val fontScalingDialogDelegateProvider: Provider<FontScalingDialogDelegate>, + private val settingsPackageRepository: QSSettingsPackageRepository, ) : QSTileImpl<QSTile.State?>( host, @@ -118,6 +120,7 @@ constructor( override fun getLongClickIntent(): Intent? { return Intent(Settings.ACTION_TEXT_READING_SETTINGS) + .setPackage(settingsPackageRepository.getSettingsPackageName()) } override fun getTileLabel(): CharSequence { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java index 7516ca030d4b..b21c3e4e44e1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java @@ -54,20 +54,21 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern private static final String TAG = "InternetAdapter"; - private final InternetDialogController mInternetDialogController; + private final InternetDetailsContentController mInternetDetailsContentController; private final CoroutineScope mCoroutineScope; @Nullable private List<WifiEntry> mWifiEntries; @VisibleForTesting protected int mWifiEntriesCount; @VisibleForTesting - protected int mMaxEntriesCount = InternetDialogController.MAX_WIFI_ENTRY_COUNT; + protected int mMaxEntriesCount = InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT; protected View mHolderView; protected Context mContext; - public InternetAdapter(InternetDialogController controller, CoroutineScope coroutineScope) { - mInternetDialogController = controller; + public InternetAdapter(InternetDetailsContentController controller, + CoroutineScope coroutineScope) { + mInternetDetailsContentController = controller; mCoroutineScope = coroutineScope; } @@ -77,7 +78,8 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern mContext = viewGroup.getContext(); mHolderView = LayoutInflater.from(mContext).inflate(R.layout.internet_list_item, viewGroup, false); - return new InternetViewHolder(mHolderView, mInternetDialogController, mCoroutineScope); + return new InternetViewHolder(mHolderView, mInternetDetailsContentController, + mCoroutineScope); } @Override @@ -137,16 +139,17 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern final TextView mWifiSummaryText; final ImageView mWifiEndIcon; final Context mContext; - final InternetDialogController mInternetDialogController; + final InternetDetailsContentController mInternetDetailsContentController; final CoroutineScope mCoroutineScope; @Nullable private Job mJob; - InternetViewHolder(View view, InternetDialogController internetDialogController, + InternetViewHolder(View view, + InternetDetailsContentController internetDetailsContentController, CoroutineScope coroutineScope) { super(view); mContext = view.getContext(); - mInternetDialogController = internetDialogController; + mInternetDetailsContentController = internetDetailsContentController; mCoroutineScope = coroutineScope; mContainerLayout = view.requireViewById(R.id.internet_container); mWifiListLayout = view.requireViewById(R.id.wifi_list); @@ -169,7 +172,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern mWifiListLayout.setEnabled(shouldEnabled(wifiEntry)); if (connectedState != WifiEntry.CONNECTED_STATE_DISCONNECTED) { mWifiListLayout.setOnClickListener( - v -> mInternetDialogController.launchWifiDetailsSetting( + v -> mInternetDetailsContentController.launchWifiDetailsSetting( wifiEntry.getKey(), v)); return; } @@ -193,7 +196,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern if (mJob == null) { mJob = WifiUtils.checkWepAllowed(mContext, mCoroutineScope, wifiEntry.getSsid(), WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, intent -> { - mInternetDialogController.startActivityForDialog(intent); + mInternetDetailsContentController.startActivityForDialog(intent); return null; }, () -> { wifiConnect(wifiEntry, view); @@ -211,19 +214,20 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern true /* connectForCaller */); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - mInternetDialogController.startActivityForDialog(intent); + mInternetDetailsContentController.startActivityForDialog(intent); return; } if (wifiEntry.canConnect()) { - mInternetDialogController.connect(wifiEntry); + mInternetDetailsContentController.connect(wifiEntry); return; } if (wifiEntry.isSaved()) { Log.w(TAG, "The saved Wi-Fi network does not allow to connect. SSID:" + wifiEntry.getSsid()); - mInternetDialogController.launchWifiDetailsSetting(wifiEntry.getKey(), view); + mInternetDetailsContentController.launchWifiDetailsSetting(wifiEntry.getKey(), + view); } } @@ -239,7 +243,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern @Nullable Drawable getWifiDrawable(@NonNull WifiEntry wifiEntry) { - Drawable drawable = mInternetDialogController.getWifiDrawable(wifiEntry); + Drawable drawable = mInternetDetailsContentController.getWifiDrawable(wifiEntry); if (drawable == null) { return null; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java index 7036ef914a1c..23210ef0e688 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java @@ -117,9 +117,9 @@ import javax.inject.Inject; /** * Controller for Internet Dialog. */ -public class InternetDialogController implements AccessPointController.AccessPointCallback { +public class InternetDetailsContentController implements AccessPointController.AccessPointCallback { - private static final String TAG = "InternetDialogController"; + private static final String TAG = "InternetDetailsContentController"; private static final String ACTION_WIFI_SCANNING_SETTINGS = "android.settings.WIFI_SCANNING_SETTINGS"; /** @@ -244,7 +244,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi } @Inject - public InternetDialogController(@ShadeDisplayAware Context context, UiEventLogger uiEventLogger, + public InternetDetailsContentController(@ShadeDisplayAware Context context, + UiEventLogger uiEventLogger, ActivityStarter starter, AccessPointController accessPointController, SubscriptionManager subscriptionManager, TelephonyManager telephonyManager, @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager, @@ -260,7 +261,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi FeatureFlags featureFlags ) { if (DEBUG) { - Log.d(TAG, "Init InternetDialogController"); + Log.d(TAG, "Init InternetDetailsContentController"); } mHandler = handler; mWorkerHandler = workerHandler; @@ -1108,13 +1109,13 @@ public class InternetDialogController implements AccessPointController.AccessPoi static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback { final ActivityStarter mActivityStarter; final WifiEntry mWifiEntry; - final InternetDialogController mInternetDialogController; + final InternetDetailsContentController mInternetDetailsContentController; WifiEntryConnectCallback(ActivityStarter activityStarter, WifiEntry connectWifiEntry, - InternetDialogController internetDialogController) { + InternetDetailsContentController internetDetailsContentController) { mActivityStarter = activityStarter; mWifiEntry = connectWifiEntry; - mInternetDialogController = internetDialogController; + mInternetDetailsContentController = internetDetailsContentController; } @Override @@ -1129,7 +1130,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mActivityStarter.startActivity(intent, false /* dismissShade */); } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) { - mInternetDialogController.makeOverlayToast(R.string.wifi_failed_connect_message); + mInternetDetailsContentController.makeOverlayToast( + R.string.wifi_failed_connect_message); } else { if (DEBUG) { Log.d(TAG, "connect failure reason=" + status); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java index 5e9deec58c58..82367ebb8c76 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java @@ -17,7 +17,7 @@ package com.android.systemui.qs.tiles.dialog; import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI; import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA; -import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT; +import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT; import android.app.AlertDialog; import android.content.Context; @@ -90,9 +90,9 @@ import java.util.concurrent.Executor; /** * Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks. */ -public class InternetDialogDelegate implements +public class InternetDialogDelegateLegacy implements SystemUIDialog.Delegate, - InternetDialogController.InternetDialogCallback { + InternetDetailsContentController.InternetDialogCallback { private static final String TAG = "InternetDialog"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -120,7 +120,7 @@ public class InternetDialogDelegate implements @Nullable private AlertDialog mAlertDialog; private final UiEventLogger mUiEventLogger; - private final InternetDialogController mInternetDialogController; + private final InternetDetailsContentController mInternetDetailsContentController; private TextView mInternetDialogTitle; private TextView mInternetDialogSubTitle; private View mDivider; @@ -184,7 +184,7 @@ public class InternetDialogDelegate implements @AssistedFactory public interface Factory { - InternetDialogDelegate create( + InternetDialogDelegateLegacy create( @Assisted(ABOVE_STATUS_BAR) boolean aboveStatusBar, @Assisted(CAN_CONFIG_MOBILE_DATA) boolean canConfigMobileData, @Assisted(CAN_CONFIG_WIFI) boolean canConfigWifi, @@ -192,10 +192,10 @@ public class InternetDialogDelegate implements } @AssistedInject - public InternetDialogDelegate( + public InternetDialogDelegateLegacy( @ShadeDisplayAware Context context, InternetDialogManager internetDialogManager, - InternetDialogController internetDialogController, + InternetDetailsContentController internetDetailsContentController, @Assisted(CAN_CONFIG_MOBILE_DATA) boolean canConfigMobileData, @Assisted(CAN_CONFIG_WIFI) boolean canConfigWifi, @Assisted(ABOVE_STATUS_BAR) boolean aboveStatusBar, @@ -207,6 +207,8 @@ public class InternetDialogDelegate implements KeyguardStateController keyguardStateController, SystemUIDialog.Factory systemUIDialogFactory, ShadeDialogContextInteractor shadeDialogContextInteractor) { + // TODO: b/377388104 QsDetailedView.assertInLegacyMode(); + mAboveStatusBar = aboveStatusBar; mSystemUIDialogFactory = systemUIDialogFactory; mShadeDialogContextInteractor = shadeDialogContextInteractor; @@ -218,8 +220,8 @@ public class InternetDialogDelegate implements mHandler = handler; mBackgroundExecutor = executor; mInternetDialogManager = internetDialogManager; - mInternetDialogController = internetDialogController; - mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId(); + mInternetDetailsContentController = internetDetailsContentController; + mDefaultDataSubId = mInternetDetailsContentController.getDefaultDataSubscriptionId(); mCanConfigMobileData = canConfigMobileData; mCanConfigWifi = canConfigWifi; mCanChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context); @@ -227,7 +229,7 @@ public class InternetDialogDelegate implements mCoroutineScope = coroutineScope; mUiEventLogger = uiEventLogger; mDialogTransitionAnimator = dialogTransitionAnimator; - mAdapter = new InternetAdapter(mInternetDialogController, coroutineScope); + mAdapter = new InternetAdapter(mInternetDetailsContentController, coroutineScope); } @Override @@ -309,7 +311,8 @@ public class InternetDialogDelegate implements setOnClickListener(dialog); mTurnWifiOnLayout.setBackground(null); mAirplaneModeButton.setVisibility( - mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE); + mInternetDetailsContentController.isAirplaneModeEnabled() ? View.VISIBLE + : View.GONE); mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(context)); mWifiRecyclerView.setAdapter(mAdapter); @@ -324,7 +327,7 @@ public class InternetDialogDelegate implements mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED); - mInternetDialogController.onStart(this, mCanConfigWifi); + mInternetDetailsContentController.onStart(this, mCanConfigWifi); if (!mCanConfigWifi) { hideWifiViews(); } @@ -356,7 +359,7 @@ public class InternetDialogDelegate implements mDoneButton.setOnClickListener(null); mShareWifiButton.setOnClickListener(null); mAirplaneModeButton.setOnClickListener(null); - mInternetDialogController.onStop(); + mInternetDetailsContentController.onStop(); mInternetDialogManager.destroyDialog(); } @@ -413,18 +416,20 @@ public class InternetDialogDelegate implements internetContent.mInternetDialogSubTitle = getSubtitleText(); if (shouldUpdateMobileNetwork) { internetContent.mActiveNetworkIsCellular = - mInternetDialogController.activeNetworkIsCellular(); + mInternetDetailsContentController.activeNetworkIsCellular(); internetContent.mIsCarrierNetworkActive = - mInternetDialogController.isCarrierNetworkActive(); - } - internetContent.mIsAirplaneModeEnabled = mInternetDialogController.isAirplaneModeEnabled(); - internetContent.mHasEthernet = mInternetDialogController.hasEthernet(); - internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled(); - internetContent.mHasActiveSubIdOnDds = mInternetDialogController.hasActiveSubIdOnDds(); - internetContent.mIsDeviceLocked = mInternetDialogController.isDeviceLocked(); - internetContent.mIsWifiScanEnabled = mInternetDialogController.isWifiScanEnabled(); + mInternetDetailsContentController.isCarrierNetworkActive(); + } + internetContent.mIsAirplaneModeEnabled = + mInternetDetailsContentController.isAirplaneModeEnabled(); + internetContent.mHasEthernet = mInternetDetailsContentController.hasEthernet(); + internetContent.mIsWifiEnabled = mInternetDetailsContentController.isWifiEnabled(); + internetContent.mHasActiveSubIdOnDds = + mInternetDetailsContentController.hasActiveSubIdOnDds(); + internetContent.mIsDeviceLocked = mInternetDetailsContentController.isDeviceLocked(); + internetContent.mIsWifiScanEnabled = mInternetDetailsContentController.isWifiScanEnabled(); internetContent.mActiveAutoSwitchNonDdsSubId = - mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); + mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId(); return internetContent; } @@ -432,8 +437,8 @@ public class InternetDialogDelegate implements InternetContent internetContent = new InternetContent(); internetContent.mInternetDialogTitleString = getDialogTitleText(); internetContent.mInternetDialogSubTitle = getSubtitleText(); - internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled(); - internetContent.mIsDeviceLocked = mInternetDialogController.isDeviceLocked(); + internetContent.mIsWifiEnabled = mInternetDetailsContentController.isWifiEnabled(); + internetContent.mIsDeviceLocked = mInternetDetailsContentController.isDeviceLocked(); return internetContent; } @@ -447,15 +452,15 @@ public class InternetDialogDelegate implements if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { showTurnOffAutoDataSwitchDialog(dialog, autoSwitchNonDdsSubId); } - mInternetDialogController.connectCarrierNetwork(); + mInternetDetailsContentController.connectCarrierNetwork(); }); mMobileDataToggle.setOnClickListener(v -> { boolean isChecked = mMobileDataToggle.isChecked(); if (!isChecked && shouldShowMobileDialog()) { mMobileDataToggle.setChecked(true); showTurnOffMobileDialog(dialog); - } else if (mInternetDialogController.isMobileDataEnabled() != isChecked) { - mInternetDialogController.setMobileDataEnabled( + } else if (mInternetDetailsContentController.isMobileDataEnabled() != isChecked) { + mInternetDetailsContentController.setMobileDataEnabled( dialog.getContext(), mDefaultDataSubId, isChecked, false); } }); @@ -466,12 +471,13 @@ public class InternetDialogDelegate implements }); mDoneButton.setOnClickListener(v -> dialog.dismiss()); mShareWifiButton.setOnClickListener(v -> { - if (mInternetDialogController.mayLaunchShareWifiSettings(mConnectedWifiEntry, v)) { + if (mInternetDetailsContentController.mayLaunchShareWifiSettings(mConnectedWifiEntry, + v)) { mUiEventLogger.log(InternetDialogEvent.SHARE_WIFI_QS_BUTTON_CLICKED); } }); mAirplaneModeButton.setOnClickListener(v -> { - mInternetDialogController.setAirplaneModeDisabled(); + mInternetDetailsContentController.setAirplaneModeDisabled(); }); } @@ -495,10 +501,10 @@ public class InternetDialogDelegate implements } private void setWifiEnable(boolean isChecked) { - if (mInternetDialogController.isWifiEnabled() == isChecked) { + if (mInternetDetailsContentController.isWifiEnabled() == isChecked) { return; } - mInternetDialogController.setWifiEnabled(isChecked); + mInternetDetailsContentController.setWifiEnabled(isChecked); } @MainThread @@ -534,7 +540,7 @@ public class InternetDialogDelegate implements } } else { mMobileNetworkLayout.setVisibility(View.VISIBLE); - mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled()); + mMobileDataToggle.setChecked(mInternetDetailsContentController.isMobileDataEnabled()); mMobileTitleText.setText(getMobileNetworkTitle(mDefaultDataSubId)); String summary = getMobileNetworkSummary(mDefaultDataSubId); if (!TextUtils.isEmpty(summary)) { @@ -679,10 +685,10 @@ public class InternetDialogDelegate implements mConnectedWifiTitleText.setText(mConnectedWifiEntry.getTitle()); mConnectedWifiSummaryText.setText(mConnectedWifiEntry.getSummary(false)); mConnectedWifiIcon.setImageDrawable( - mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry)); + mInternetDetailsContentController.getInternetWifiDrawable(mConnectedWifiEntry)); mWifiSettingsIcon.setColorFilter( mDialog.getContext().getColor(R.color.connected_network_primary_color)); - if (mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull( + if (mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull( mConnectedWifiEntry) != null) { mShareWifiButton.setVisibility(View.VISIBLE); } else { @@ -748,7 +754,7 @@ public class InternetDialogDelegate implements if (TextUtils.isEmpty(mWifiScanNotifyText.getText())) { final AnnotationLinkSpan.LinkInfo linkInfo = new AnnotationLinkSpan.LinkInfo( AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION, - mInternetDialogController::launchWifiScanningSetting); + mInternetDetailsContentController::launchWifiScanningSetting); mWifiScanNotifyText.setText(AnnotationLinkSpan.linkify( mDialog.getContext().getText(R.string.wifi_scan_notify_message), linkInfo)); mWifiScanNotifyText.setMovementMethod(LinkMovementMethod.getInstance()); @@ -760,37 +766,38 @@ public class InternetDialogDelegate implements if (mConnectedWifiEntry == null) { return; } - mInternetDialogController.launchWifiDetailsSetting(mConnectedWifiEntry.getKey(), view); + mInternetDetailsContentController.launchWifiDetailsSetting(mConnectedWifiEntry.getKey(), + view); } /** For DSDS auto data switch **/ void onClickConnectedSecondarySub(View view) { - mInternetDialogController.launchMobileNetworkSettings(view); + mInternetDetailsContentController.launchMobileNetworkSettings(view); } void onClickSeeMoreButton(View view) { - mInternetDialogController.launchNetworkSetting(view); + mInternetDetailsContentController.launchNetworkSetting(view); } CharSequence getDialogTitleText() { - return mInternetDialogController.getDialogTitleText(); + return mInternetDetailsContentController.getDialogTitleText(); } @Nullable CharSequence getSubtitleText() { - return mInternetDialogController.getSubtitleText(mIsProgressBarVisible); + return mInternetDetailsContentController.getSubtitleText(mIsProgressBarVisible); } private Drawable getSignalStrengthDrawable(int subId) { - return mInternetDialogController.getSignalStrengthDrawable(subId); + return mInternetDetailsContentController.getSignalStrengthDrawable(subId); } CharSequence getMobileNetworkTitle(int subId) { - return mInternetDialogController.getMobileNetworkTitle(subId); + return mInternetDetailsContentController.getMobileNetworkTitle(subId); } String getMobileNetworkSummary(int subId) { - return mInternetDialogController.getMobileNetworkSummary(subId); + return mInternetDetailsContentController.getMobileNetworkSummary(subId); } private void setProgressBarVisible(boolean visible) { @@ -810,7 +817,7 @@ public class InternetDialogDelegate implements } boolean flag = Prefs.getBoolean(mDialog.getContext(), QS_HAS_TURNED_OFF_MOBILE_DATA, false); - if (mInternetDialogController.isMobileDataEnabled() && !flag) { + if (mInternetDetailsContentController.isMobileDataEnabled() && !flag) { return true; } return false; @@ -819,7 +826,8 @@ public class InternetDialogDelegate implements private void showTurnOffMobileDialog(SystemUIDialog dialog) { Context context = dialog.getContext(); CharSequence carrierName = getMobileNetworkTitle(mDefaultDataSubId); - boolean isInService = mInternetDialogController.isVoiceStateInService(mDefaultDataSubId); + boolean isInService = mInternetDetailsContentController.isVoiceStateInService( + mDefaultDataSubId); if (TextUtils.isEmpty(carrierName) || !isInService) { carrierName = context.getString(R.string.mobile_data_disable_message_default_carrier); } @@ -831,7 +839,7 @@ public class InternetDialogDelegate implements .setPositiveButton( com.android.internal.R.string.alert_windows_notification_turn_off_action, (d, w) -> { - mInternetDialogController.setMobileDataEnabled(context, + mInternetDetailsContentController.setMobileDataEnabled(context, mDefaultDataSubId, false, false); mMobileDataToggle.setChecked(false); Prefs.putBoolean(context, QS_HAS_TURNED_OFF_MOBILE_DATA, true); @@ -858,7 +866,7 @@ public class InternetDialogDelegate implements }) .setPositiveButton(R.string.auto_data_switch_dialog_positive_button, (d, w) -> { - mInternetDialogController + mInternetDetailsContentController .setAutoDataSwitchMobileDataPolicy(subId, false); if (mSecondaryMobileNetworkLayout != null) { mSecondaryMobileNetworkLayout.setVisibility(View.GONE); @@ -938,7 +946,7 @@ public class InternetDialogDelegate implements @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries) { // Should update the carrier network layout when it is connected under airplane mode ON. boolean shouldUpdateCarrierNetwork = mMobileNetworkLayout.getVisibility() == View.VISIBLE - && mInternetDialogController.isAirplaneModeEnabled(); + && mInternetDetailsContentController.isAirplaneModeEnabled(); mHandler.post(() -> { mConnectedWifiEntry = connectedEntry; mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt index f6749715e1fd..8a54648f4541 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt @@ -32,13 +32,13 @@ import kotlinx.coroutines.cancel private const val TAG = "InternetDialogFactory" private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) -/** Factory to create [InternetDialogDelegate] objects. */ +/** Factory to create [InternetDialogDelegateLegacy] objects. */ @SysUISingleton class InternetDialogManager @Inject constructor( private val dialogTransitionAnimator: DialogTransitionAnimator, - private val dialogFactory: InternetDialogDelegate.Factory, + private val dialogFactory: InternetDialogDelegateLegacy.Factory, @Background private val bgDispatcher: CoroutineDispatcher, ) { private lateinit var coroutineScope: CoroutineScope @@ -48,8 +48,8 @@ constructor( } /** - * Creates a [InternetDialogDelegate]. The dialog will be animated from [expandable] if it is - * not null. + * Creates a [InternetDialogDelegateLegacy]. The dialog will be animated from [expandable] if + * it is not null. */ fun create( aboveStatusBar: Boolean, @@ -64,6 +64,7 @@ constructor( return } else { coroutineScope = CoroutineScope(bgDispatcher + newTracingContext("InternetDialogScope")) + // TODO: b/377388104 check the QsDetailedView flag to use the correct dialogFactory dialog = dialogFactory .create(aboveStatusBar, canConfigMobileData, canConfigWifi, coroutineScope) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt index dfdec3b458a9..b774643eb21d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt @@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor import android.content.Intent import android.provider.Settings import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository +import com.android.systemui.qs.shared.QSSettingsPackageRepository import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.interactor.QSTileInput import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor @@ -32,21 +33,20 @@ class ColorCorrectionUserActionInteractor constructor( private val colorCorrectionRepository: ColorCorrectionRepository, private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, + private val settingsPackageRepository: QSSettingsPackageRepository, ) : QSTileUserActionInteractor<ColorCorrectionTileModel> { override suspend fun handleInput(input: QSTileInput<ColorCorrectionTileModel>): Unit = with(input) { when (action) { is QSTileUserAction.Click -> { - colorCorrectionRepository.setIsEnabled( - !data.isEnabled, - user, - ) + colorCorrectionRepository.setIsEnabled(!data.isEnabled, user) } is QSTileUserAction.LongClick -> { qsTileIntentUserActionHandler.handle( action.expandable, Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS) + .setPackage(settingsPackageRepository.getSettingsPackageName()), ) } is QSTileUserAction.ToggleClick -> {} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt index 6ab5796dceaa..0ebb51e722af 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt @@ -24,6 +24,7 @@ import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.qs.shared.QSSettingsPackageRepository import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.interactor.QSTileInput import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor @@ -46,6 +47,7 @@ constructor( private val keyguardStateController: KeyguardStateController, private val dialogTransitionAnimator: DialogTransitionAnimator, private val activityStarter: ActivityStarter, + private val settingsPackageRepository: QSSettingsPackageRepository, ) : QSTileUserActionInteractor<FontScalingTileModel> { override suspend fun handleInput(input: QSTileInput<FontScalingTileModel>): Unit = @@ -63,7 +65,7 @@ constructor( ?.dialogTransitionController( DialogCuj( InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, - INTERACTION_JANK_TAG + INTERACTION_JANK_TAG, ) ) ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show() @@ -78,7 +80,7 @@ constructor( /* cancelAction= */ null, /* dismissShade= */ true, /* afterKeyguardGone= */ true, - /* deferred= */ false + /* deferred= */ false, ) } } @@ -86,6 +88,7 @@ constructor( qsTileIntentUserActionHandler.handle( action.expandable, Intent(Settings.ACTION_TEXT_READING_SETTINGS) + .setPackage(settingsPackageRepository.getSettingsPackageName()), ) } is QSTileUserAction.ToggleClick -> {} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt index aa8387732160..f78349718697 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt @@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles.impl.inversion.domain.interactor import android.content.Intent import android.provider.Settings import com.android.systemui.accessibility.data.repository.ColorInversionRepository +import com.android.systemui.qs.shared.QSSettingsPackageRepository import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.interactor.QSTileInput import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor @@ -32,21 +33,20 @@ class ColorInversionUserActionInteractor constructor( private val colorInversionRepository: ColorInversionRepository, private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, + private val settingsPackageRepository: QSSettingsPackageRepository, ) : QSTileUserActionInteractor<ColorInversionTileModel> { override suspend fun handleInput(input: QSTileInput<ColorInversionTileModel>): Unit = with(input) { when (action) { is QSTileUserAction.Click -> { - colorInversionRepository.setIsEnabled( - !data.isEnabled, - user, - ) + colorInversionRepository.setIsEnabled(!data.isEnabled, user) } is QSTileUserAction.LongClick -> { qsTileIntentUserActionHandler.handle( action.expandable, Intent(Settings.ACTION_COLOR_INVERSION_SETTINGS) + .setPackage(settingsPackageRepository.getSettingsPackageName()), ) } is QSTileUserAction.ToggleClick -> {} diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java index 10ac2cf76763..0650f8606ba9 100644 --- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java @@ -16,8 +16,6 @@ package com.android.systemui.scrim; -import static com.android.systemui.Flags.notificationShadeBlur; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; @@ -216,7 +214,7 @@ public class ScrimDrawable extends Drawable { public void draw(@NonNull Canvas canvas) { mPaint.setColor(mMainColor); mPaint.setAlpha(mAlpha); - if (notificationShadeBlur() || WindowBlurFlag.isEnabled()) { + if (WindowBlurFlag.isEnabled()) { // TODO (b/381263600), wire this at ScrimController, move it to PrimaryBouncerTransition mPaint.setAlpha((int) (0.5f * mAlpha)); } diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java index 0f80e7432a54..03a8d17847f9 100644 --- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java @@ -16,8 +16,6 @@ package com.android.systemui.scrim; -import static com.android.systemui.Flags.notificationShadeBlur; - import static java.lang.Float.isNaN; import android.annotation.NonNull; @@ -44,6 +42,7 @@ import com.android.internal.colorextraction.ColorExtractor; import com.android.systemui.res.R; import com.android.systemui.shade.TouchLogger; import com.android.systemui.util.LargeScreenUtils; +import com.android.systemui.window.flag.WindowBlurFlag; import java.util.concurrent.Executor; @@ -253,7 +252,7 @@ public class ScrimView extends View { if (mBlendWithMainColor) { mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor, tintAmount); } - if (notificationShadeBlur()) { + if (WindowBlurFlag.isEnabled()) { int layerAbove = ColorUtils.setAlphaComponent( getResources().getColor(R.color.shade_panel, null), (int) (0.4f * 255)); diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt index 201dc0339a0a..4edba2785e89 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt @@ -77,7 +77,17 @@ constructor( private fun getContextOrDefault(displayId: Int): Context { return try { traceSection({ "Getting dialog context for displayId=$displayId" }) { - displayWindowPropertyRepository.get().get(displayId, DIALOG_WINDOW_TYPE).context + val displayWindowProperties = + displayWindowPropertyRepository.get().get(displayId, DIALOG_WINDOW_TYPE) + if (displayWindowProperties == null) { + Log.e( + TAG, + "DisplayWindowPropertiesRepository returned null for display $displayId. Returning default one", + ) + defaultContext + } else { + displayWindowProperties.context + } } } catch (e: Exception) { // This can happen if the display was disconnected in the meantime. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt new file mode 100644 index 000000000000..a747abbc6a6e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt @@ -0,0 +1,131 @@ +/* + * 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.chips.ui.compose + +import android.os.SystemClock +import android.text.format.DateUtils.formatElapsedTime +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.node.LayoutModifierNode +import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.constrain +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.delay + +/** Platform-optimized interface for getting current time */ +fun interface TimeSource { + fun getCurrentTime(): Long +} + +/** Holds and manages the state for a Chronometer */ +class ChronometerState(private val timeSource: TimeSource, private val startTimeMillis: Long) { + private var currentTimeMillis by mutableLongStateOf(0L) + private val elapsedTimeMillis: Long + get() = maxOf(0L, currentTimeMillis - startTimeMillis) + + val currentTimeText: String by derivedStateOf { formatElapsedTime(elapsedTimeMillis / 1000) } + + suspend fun run() { + while (true) { + currentTimeMillis = timeSource.getCurrentTime() + val delaySkewMillis = (currentTimeMillis - startTimeMillis) % 1000L + delay(1000L - delaySkewMillis) + } + } +} + +/** Remember and manage the ChronometerState */ +@Composable +fun rememberChronometerState(timeSource: TimeSource, startTimeMillis: Long): ChronometerState { + val state = + remember(timeSource, startTimeMillis) { ChronometerState(timeSource, startTimeMillis) } + val lifecycleOwner = LocalLifecycleOwner.current + LaunchedEffect(lifecycleOwner, timeSource, startTimeMillis) { + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { state.run() } + } + + return state +} + +/** + * A composable chronometer that displays elapsed time with constrained width. The width of the text + * is only allowed to increase. This ensures there is no visual jitter when individual digits in the + * text change due to the timer ticking. + */ +@Composable +fun ChronometerText( + startTimeMillis: Long, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + style: TextStyle = LocalTextStyle.current, + timeSource: TimeSource = remember { TimeSource { SystemClock.elapsedRealtime() } }, +) { + val state = rememberChronometerState(timeSource, startTimeMillis) + Text( + text = state.currentTimeText, + style = style, + color = color, + modifier = modifier.neverDecreaseWidth(), + ) +} + +/** A modifier that ensures the width of the content only increases and never decreases. */ +private fun Modifier.neverDecreaseWidth(): Modifier { + return this.then(neverDecreaseWidthElement) +} + +private data object neverDecreaseWidthElement : ModifierNodeElement<NeverDecreaseWidthNode>() { + override fun create(): NeverDecreaseWidthNode { + return NeverDecreaseWidthNode() + } + + override fun update(node: NeverDecreaseWidthNode) { + error("This should never be called") + } +} + +private class NeverDecreaseWidthNode : Modifier.Node(), LayoutModifierNode { + private var minWidth = 0 + + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints, + ): MeasureResult { + val placeable = measurable.measure(Constraints(minWidth = minWidth).constrain(constraints)) + val width = placeable.width + val height = placeable.height + + minWidth = maxOf(minWidth, width) + + return layout(width, height) { placeable.place(0, 0) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt index d24eddaf321f..d25ca285c53b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt @@ -72,7 +72,7 @@ constructor( private fun initializeStatusBarForDisplay(displayId: Int, result: RegisterStatusBarResult) { if ((result.mTransientBarTypes and WindowInsets.Type.statusBars()) != 0) { - statusBarModeRepository.forDisplay(displayId).showTransient() + statusBarModeRepository.forDisplay(displayId)?.showTransient() } val commandQueueCallbacks = commandQueueCallbacksLazy.get() commandQueueCallbacks.onSystemBarAttributesChanged( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt index 9e9a38e87924..b057fb0433fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt @@ -89,21 +89,26 @@ constructor( } private fun createAndStartOrchestratorForDisplay(displayId: Int) { + val statusBarModeRepository = statusBarModeRepositoryStore.forDisplay(displayId) ?: return + val statusBarInitializer = initializerStore.forDisplay(displayId) ?: return + val statusBarWindowController = + statusBarWindowControllerStore.forDisplay(displayId) ?: return + val autoHideController = autoHideControllerStore.forDisplay(displayId) ?: return statusBarOrchestratorFactory .create( displayId, displayScopeRepository.scopeForDisplay(displayId), statusBarWindowStateRepositoryStore.forDisplay(displayId), - statusBarModeRepositoryStore.forDisplay(displayId), - initializerStore.forDisplay(displayId), - statusBarWindowControllerStore.forDisplay(displayId), - autoHideControllerStore.forDisplay(displayId), + statusBarModeRepository, + statusBarInitializer, + statusBarWindowController, + autoHideController, ) .start() } private fun createAndStartInitializerForDisplay(displayId: Int) { - statusBarInitializerStore.forDisplay(displayId).start() + statusBarInitializerStore.forDisplay(displayId)?.start() } private fun startPrivacyDotForDisplay(displayId: Int) { @@ -111,6 +116,6 @@ constructor( // For the default display, privacy dot is started via ScreenDecorations return } - privacyDotWindowControllerStore.forDisplay(displayId).start() + privacyDotWindowControllerStore.forDisplay(displayId)?.start() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt index 4c54fc49e536..1e127ee054e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt @@ -20,9 +20,11 @@ import android.view.ViewGroup import androidx.annotation.VisibleForTesting import com.android.systemui.CoreStartable import com.android.systemui.fragments.FragmentHostManager +import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.res.R import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions import com.android.systemui.statusbar.phone.PhoneStatusBarView @@ -34,7 +36,6 @@ import com.android.systemui.statusbar.window.StatusBarWindowController import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import java.lang.IllegalStateException import javax.inject.Provider /** @@ -75,6 +76,8 @@ interface StatusBarInitializer : CoreStartable { fun create( statusBarWindowController: StatusBarWindowController, statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository, + statusBarConfigurationController: StatusBarConfigurationController, + darkIconDispatcher: DarkIconDispatcher, ): StatusBarInitializer } } @@ -84,6 +87,8 @@ class StatusBarInitializerImpl constructor( @Assisted private val statusBarWindowController: StatusBarWindowController, @Assisted private val statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository, + @Assisted private val statusBarConfigurationController: StatusBarConfigurationController, + @Assisted private val darkIconDispatcher: DarkIconDispatcher, private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>, private val statusBarRootFactory: StatusBarRootFactory, private val componentFactory: HomeStatusBarComponent.Factory, @@ -131,23 +136,32 @@ constructor( -> val phoneStatusBarView = cv.findViewById<PhoneStatusBarView>(R.id.status_bar) component = - componentFactory.create(phoneStatusBarView).also { component -> - // CollapsedStatusBarFragment used to be responsible initializing - component.init() - - statusBarViewUpdatedListener?.onStatusBarViewUpdated( - component.phoneStatusBarViewController, - component.phoneStatusBarTransitions, + componentFactory + .create( + phoneStatusBarView, + statusBarConfigurationController, + statusBarWindowController, + darkIconDispatcher, ) - - if (StatusBarConnectedDisplays.isEnabled) { - statusBarModePerDisplayRepository.onStatusBarViewInitialized(component) - } else { - creationListeners.forEach { listener -> - listener.onStatusBarViewInitialized(component) + .also { component -> + // CollapsedStatusBarFragment used to be responsible initializing + component.init() + + statusBarViewUpdatedListener?.onStatusBarViewUpdated( + component.phoneStatusBarViewController, + component.phoneStatusBarTransitions, + ) + + if (StatusBarConnectedDisplays.isEnabled) { + statusBarModePerDisplayRepository.onStatusBarViewInitialized( + component + ) + } else { + creationListeners.forEach { listener -> + listener.onStatusBarViewInitialized(component) + } } } - } } // Add the new compose view to the hierarchy because we don't use fragment transactions @@ -163,9 +177,11 @@ constructor( CollapsedStatusBarFragment.TAG, object : FragmentHostManager.FragmentListener { override fun onFragmentViewCreated(tag: String, fragment: Fragment) { - component = - (fragment as CollapsedStatusBarFragment).homeStatusBarComponent - ?: throw IllegalStateException() + val statusBarFragment = fragment as CollapsedStatusBarFragment + if (statusBarFragment.homeStatusBarComponent == null) { + return + } + component = fragment.homeStatusBarComponent statusBarViewUpdatedListener?.onStatusBarViewUpdated( component!!.phoneStatusBarViewController, component!!.phoneStatusBarTransitions, @@ -195,6 +211,8 @@ constructor( override fun create( statusBarWindowController: StatusBarWindowController, statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository, + statusBarConfigurationController: StatusBarConfigurationController, + darkIconDispatcher: DarkIconDispatcher, ): StatusBarInitializerImpl } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt index 4f815c1f0b31..de6cd072afd7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt @@ -22,6 +22,8 @@ import com.android.systemui.display.data.repository.DisplayRepository import com.android.systemui.display.data.repository.PerDisplayStore import com.android.systemui.display.data.repository.PerDisplayStoreImpl import com.android.systemui.display.data.repository.SingleDisplayStore +import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import javax.inject.Inject @@ -39,6 +41,8 @@ constructor( private val factory: StatusBarInitializer.Factory, private val statusBarWindowControllerStore: StatusBarWindowControllerStore, private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore, + private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, + private val darkIconDispatcherStore: DarkIconDispatcherStore, ) : StatusBarInitializerStore, PerDisplayStoreImpl<StatusBarInitializer>(backgroundApplicationScope, displayRepository) { @@ -47,10 +51,19 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): StatusBarInitializer { + override fun createInstanceForDisplay(displayId: Int): StatusBarInitializer? { + val statusBarWindowController = + statusBarWindowControllerStore.forDisplay(displayId) ?: return null + val statusBarModePerDisplayRepository = + statusBarModeRepositoryStore.forDisplay(displayId) ?: return null + val statusBarConfigurationController = + statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null + val darkIconDispatcher = darkIconDispatcherStore.forDisplay(displayId) ?: return null return factory.create( - statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId), - statusBarModePerDisplayRepository = statusBarModeRepositoryStore.forDisplay(displayId), + statusBarWindowController, + statusBarModePerDisplayRepository, + statusBarConfigurationController, + darkIconDispatcher, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt index 8183a487cee2..041f3c816149 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt @@ -65,8 +65,9 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): SysuiDarkIconDispatcher { - val properties = displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) + override fun createInstanceForDisplay(displayId: Int): SysuiDarkIconDispatcher? { + val properties = + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null return factory.create(displayId, properties.context) } @@ -103,7 +104,7 @@ constructor(private val store: SysuiDarkIconDispatcherStore) : DarkIconDispatche override val defaultDisplay: DarkIconDispatcher get() = store.defaultDisplay - override fun forDisplay(displayId: Int): DarkIconDispatcher = store.forDisplay(displayId) + override fun forDisplay(displayId: Int): DarkIconDispatcher? = store.forDisplay(displayId) } @Module diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt index e4987555833b..c629d10b90b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt @@ -49,13 +49,16 @@ constructor( LightBarControllerStore, PerDisplayStoreImpl<LightBarController>(backgroundApplicationScope, displayRepository) { - override fun createInstanceForDisplay(displayId: Int): LightBarController { + override fun createInstanceForDisplay(displayId: Int): LightBarController? { + val darkIconDispatcher = darkIconDispatcherStore.forDisplay(displayId) ?: return null + val statusBarModePerDisplayRepository = + statusBarModeRepositoryStore.forDisplay(displayId) ?: return null return factory .create( displayId, displayScopeRepository.scopeForDisplay(displayId), - darkIconDispatcherStore.forDisplay(displayId), - statusBarModeRepositoryStore.forDisplay(displayId), + darkIconDispatcher, + statusBarModePerDisplayRepository, ) .also { it.start() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt index bd61c44e3d9b..d48c94bd0893 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt @@ -52,11 +52,14 @@ constructor( PrivacyDotViewControllerStore, PerDisplayStoreImpl<PrivacyDotViewController>(backgroundApplicationScope, displayRepository) { - override fun createInstanceForDisplay(displayId: Int): PrivacyDotViewController { + override fun createInstanceForDisplay(displayId: Int): PrivacyDotViewController? { + val configurationController = + statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null + val contentInsetsProvider = contentInsetsProviderStore.forDisplay(displayId) ?: return null return factory.create( displayScopeRepository.scopeForDisplay(displayId), - statusBarConfigurationControllerStore.forDisplay(displayId), - contentInsetsProviderStore.forDisplay(displayId), + configurationController, + contentInsetsProvider, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt index a1f56552629b..086cc99957ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt @@ -58,15 +58,18 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): PrivacyDotWindowController { + override fun createInstanceForDisplay(displayId: Int): PrivacyDotWindowController? { if (displayId == Display.DEFAULT_DISPLAY) { throw IllegalArgumentException("This class should only be used for connected displays") } val displayWindowProperties = displayWindowPropertiesRepository.get(displayId, TYPE_NAVIGATION_BAR_PANEL) + ?: return null + val privacyDotViewController = + privacyDotViewControllerStore.forDisplay(displayId) ?: return null return windowControllerFactory.create( displayId = displayId, - privacyDotViewController = privacyDotViewControllerStore.forDisplay(displayId), + privacyDotViewController = privacyDotViewController, viewCaptureAwareWindowManager = viewCaptureAwareWindowManagerFactory.create(displayWindowProperties.windowManager), inflater = displayWindowProperties.layoutInflater, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt index 6cf2c73a7138..38cea832ad76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt @@ -62,9 +62,9 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): StatusBarConfigurationController { + override fun createInstanceForDisplay(displayId: Int): StatusBarConfigurationController? { val displayWindowProperties = - displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null return configurationControllerFactory.create(displayWindowProperties.context) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt index e471b12c1d58..554c46f6c219 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt @@ -59,13 +59,17 @@ constructor( displayRepository, ) { - override fun createInstanceForDisplay(displayId: Int): StatusBarContentInsetsProvider { - val context = displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR).context + override fun createInstanceForDisplay(displayId: Int): StatusBarContentInsetsProvider? { + val displayWindowProperties = + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null + val context = displayWindowProperties.context + val configurationController = + statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null val cameraProtectionLoader = cameraProtectionLoaderFactory.create(context) return factory .create( context, - statusBarConfigurationControllerStore.forDisplay(displayId), + configurationController, sysUICutoutProviderFactory.create(context, cameraProtectionLoader), ) .also { it.start() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt index 7760f58805c9..ffc125539521 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt @@ -62,11 +62,17 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): SystemEventChipAnimationController { + override fun createInstanceForDisplay(displayId: Int): SystemEventChipAnimationController? { + val displayWindowProperties = + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null + val statusBarWindowController = + statusBarWindowControllerStore.forDisplay(displayId) ?: return null + val contentInsetsProvider = + statusBarContentInsetsProviderStore.forDisplay(displayId) ?: return null return factory.create( - displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR).context, - statusBarWindowControllerStore.forDisplay(displayId), - statusBarContentInsetsProviderStore.forDisplay(displayId), + displayWindowProperties.context, + statusBarWindowController, + contentInsetsProvider, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt index f2bb7b16439d..4b9721ea4fe5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt @@ -72,5 +72,5 @@ constructor( } private fun controllersForAllDisplays() = - displayRepository.displays.value.map { controllerStore.forDisplay(it.displayId) } + displayRepository.displays.value.mapNotNull { controllerStore.forDisplay(it.displayId) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt index 9928ac67f185..f7799bb75ae0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.events +import android.util.Log import android.view.Display import android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM import android.view.DisplayCutout.BOUNDS_POSITION_LEFT @@ -23,6 +24,7 @@ import android.view.DisplayCutout.BOUNDS_POSITION_RIGHT import android.view.DisplayCutout.BOUNDS_POSITION_TOP import android.view.LayoutInflater import android.view.View +import android.view.WindowManager.InvalidDisplayException import android.view.WindowManager.LayoutParams.WRAP_CONTENT import android.widget.FrameLayout import com.android.app.viewcapture.ViewCaptureAwareWindowManager @@ -97,7 +99,17 @@ constructor( // PrivacyDotViewController expects the dot view to have a FrameLayout parent. val rootView = FrameLayout(context) rootView.addView(this) - viewCaptureAwareWindowManager.addView(rootView, params) + try { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + viewCaptureAwareWindowManager.addView(rootView, params) + } catch (e: InvalidDisplayException) { + Log.e( + TAG, + "Unable to add view to WM. Display with id $displayId does not exist anymore", + e, + ) + } } @AssistedFactory @@ -109,4 +121,8 @@ constructor( inflater: LayoutInflater, ): PrivacyDotWindowController } + + private companion object { + const val TAG = "PrivacyDotWindowController" + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt index 227a1fefb982..eb55856d994b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt @@ -62,8 +62,10 @@ constructor( override fun iconView(key: String): StatusBarIconView? { val entry = notifCollection.getEntry(key) ?: return null + val displayWindowProperties = + displayWindowPropertiesInteractor.getForStatusBar(displayId) ?: return null return cachedIcons.computeIfAbsent(key) { - val context = displayWindowPropertiesInteractor.getForStatusBar(displayId).context + val context = displayWindowProperties.context iconManager.createSbIconView(context, entry) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt index 2ba28a660116..e1032820fb71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt @@ -68,13 +68,14 @@ constructor( .distinctUntilChanged() /** The colors with which to display the notification icons. */ - fun iconColors(displayId: Int): Flow<NotificationIconColors> = - darkIconInteractor + fun iconColors(displayId: Int): Flow<NotificationIconColors> { + return darkIconInteractor .darkState(displayId) .map { (areas: Collection<Rect>, tint: Int) -> IconColorsImpl(tint, areas) } .flowOn(bgContext) .conflate() .distinctUntilChanged() + } /** [NotificationIconsViewData] indicating which icons to display in the view. */ val icons: Flow<NotificationIconsViewData> = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt index 64d165402759..5106cccc1755 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.shelf import android.content.Context import android.util.AttributeSet import android.view.View +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.statusbar.notification.shared.NotificationMinimalism import com.android.systemui.statusbar.phone.NotificationIconContainer import kotlin.math.max @@ -35,7 +36,8 @@ constructor(context: Context, attrs: AttributeSet? = null) : /** * @return The left boundary (not the RTL compatible start) of the area that icons can be added. */ - override fun getLeftBound(): Float { + @VisibleForTesting + public override fun getLeftBound(): Float { if (!NotificationMinimalism.isEnabled) { return super.getLeftBound() } @@ -49,7 +51,8 @@ constructor(context: Context, attrs: AttributeSet? = null) : /** * @return The right boundary (not the RTL compatible end) of the area that icons can be added. */ - override fun getRightBound(): Float { + @VisibleForTesting + public override fun getRightBound(): Float { if (!NotificationMinimalism.isEnabled) { return super.getRightBound() } @@ -80,7 +83,8 @@ constructor(context: Context, attrs: AttributeSet? = null) : return actualWidth - iconState.xTranslation - iconView.width } - private val isAlignedToRight: Boolean + @VisibleForTesting + val isAlignedToRight: Boolean get() { if (!NotificationMinimalism.isEnabled) { return isLayoutRtl diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt index 744f96918eef..2ae38dd488bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt @@ -47,9 +47,9 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): AutoHideController { + override fun createInstanceForDisplay(displayId: Int): AutoHideController? { val displayWindowProperties = - displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null return autoHideControllerFactory.create(displayWindowProperties.context) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java index ea67f1cdb60a..ca0c1ac9ce7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java @@ -499,9 +499,9 @@ public class LightBarControllerImpl implements /** Creates a {@link LightBarControllerImpl}. */ LightBarControllerImpl create( int displayId, - CoroutineScope coroutineScope, - DarkIconDispatcher darkIconDispatcher, - StatusBarModePerDisplayRepository statusBarModePerDisplayRepository); + @NonNull CoroutineScope coroutineScope, + @NonNull DarkIconDispatcher darkIconDispatcher, + @NonNull StatusBarModePerDisplayRepository statusBarModePerDisplayRepository); } public static class LegacyFactory implements LightBarController.Factory { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt index 394502b2d31f..031754db3c57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt @@ -33,6 +33,7 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.res.R +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange @@ -54,7 +55,7 @@ constructor( ) { /** Creates listener always using the same light color for overlay */ - fun createListener(view: View) = + fun createListener(view: View): StatusOverlayHoverListener = StatusOverlayHoverListener( view, configurationController, @@ -65,8 +66,10 @@ constructor( /** * Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay */ - fun createDarkAwareListener(view: View) = - createDarkAwareListener(view, view.darkIconDispatcher.darkChangeFlow()) + fun createDarkAwareListener(view: View): StatusOverlayHoverListener? { + val darkIconDispatcher = view.darkIconDispatcher ?: return null + return createDarkAwareListener(view, darkIconDispatcher.darkChangeFlow()) + } /** * Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay @@ -78,27 +81,34 @@ constructor( rightHoverMargin: Int = 0, topHoverMargin: Int = 0, bottomHoverMargin: Int = 0, - ) = - createDarkAwareListener( + ): StatusOverlayHoverListener? { + val darkIconDispatcher = view.darkIconDispatcher ?: return null + return createDarkAwareListener( view, - view.darkIconDispatcher.darkChangeFlow(), + darkIconDispatcher.darkChangeFlow(), leftHoverMargin, rightHoverMargin, topHoverMargin, bottomHoverMargin, ) + } /** * Creates listener using provided [DarkChange] producer to determine light or dark color of the * overlay */ - fun createDarkAwareListener(view: View, darkFlow: StateFlow<DarkChange>) = - StatusOverlayHoverListener( + fun createDarkAwareListener( + view: View, + darkFlow: StateFlow<DarkChange>, + ): StatusOverlayHoverListener? { + val configurationController = view.statusBarConfigurationController ?: return null + return StatusOverlayHoverListener( view, - view.statusBarConfigurationController, + configurationController, view.resources, darkFlow.map { toHoverTheme(view, it) }, ) + } private fun createDarkAwareListener( view: View, @@ -107,10 +117,11 @@ constructor( rightHoverMargin: Int = 0, topHoverMargin: Int = 0, bottomHoverMargin: Int = 0, - ) = - StatusOverlayHoverListener( + ): StatusOverlayHoverListener? { + val configurationController = view.statusBarConfigurationController ?: return null + return StatusOverlayHoverListener( view, - view.statusBarConfigurationController, + configurationController, view.resources, darkFlow.map { toHoverTheme(view, it) }, leftHoverMargin, @@ -118,11 +129,12 @@ constructor( topHoverMargin, bottomHoverMargin, ) + } - private val View.statusBarConfigurationController + private val View.statusBarConfigurationController: StatusBarConfigurationController? get() = statusBarConfigurationControllerStore.forDisplay(context.displayId) - private val View.darkIconDispatcher + private val View.darkIconDispatcher: SysuiDarkIconDispatcher? get() = darkIconDispatcherStore.forDisplay(context.displayId) private fun toHoverTheme(view: View, darkChange: DarkChange): HoverTheme { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt index 4f32aaa2654e..037dda91fb00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt @@ -31,8 +31,10 @@ import com.android.systemui.statusbar.core.StatusBarInitializerImpl import com.android.systemui.statusbar.core.StatusBarInitializerStore import com.android.systemui.statusbar.core.StatusBarOrchestrator import com.android.systemui.statusbar.core.StatusBarRootModernization +import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerStoreModule import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStoreModule +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.events.PrivacyDotViewControllerModule import com.android.systemui.statusbar.phone.AutoHideControllerStore @@ -107,10 +109,14 @@ interface StatusBarPhoneModule { implFactory: StatusBarInitializerImpl.Factory, statusBarWindowControllerStore: StatusBarWindowControllerStore, statusBarModeRepositoryStore: StatusBarModeRepositoryStore, + statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, + darkIconDispatcherStore: DarkIconDispatcherStore, ): StatusBarInitializerImpl { return implFactory.create( statusBarWindowControllerStore.defaultDisplay, statusBarModeRepositoryStore.defaultDisplay, + statusBarConfigurationControllerStore.defaultDisplay, + darkIconDispatcherStore.defaultDisplay, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt index 49356eba2842..04646549bfdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt @@ -15,12 +15,14 @@ */ package com.android.systemui.statusbar.phone.data.repository +import android.util.Log import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange import dagger.Binds import dagger.Module import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow /** Dark-mode state for tinting icons. */ @@ -33,8 +35,22 @@ class DarkIconRepositoryImpl @Inject constructor(private val darkIconDispatcherStore: SysuiDarkIconDispatcherStore) : DarkIconRepository { - override fun darkState(displayId: Int): StateFlow<DarkChange> = - darkIconDispatcherStore.forDisplay(displayId).darkChangeFlow() + override fun darkState(displayId: Int): StateFlow<DarkChange> { + val perDisplayDakIconDispatcher = darkIconDispatcherStore.forDisplay(displayId) + if (perDisplayDakIconDispatcher == null) { + Log.e( + TAG, + "DarkIconDispatcher for display $displayId is null. Returning flow of " + + "DarkChange.EMPTY", + ) + return MutableStateFlow(DarkChange.EMPTY) + } + return perDisplayDakIconDispatcher.darkChangeFlow() + } + + private companion object { + const val TAG = "DarkIconRepositoryImpl" + } } @Module diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt index ed8b3e8922f3..b15fffb8dd29 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt @@ -34,8 +34,8 @@ class LightsOutInteractor @Inject constructor(private val repository: StatusBarModeRepositoryStore) { - fun isLowProfile(displayId: Int): Flow<Boolean> = - repository.forDisplay(displayId).statusBarMode.map { + fun isLowProfile(displayId: Int): Flow<Boolean>? = + repository.forDisplay(displayId)?.statusBarMode?.map { when (it) { StatusBarMode.LIGHTS_OUT, StatusBarMode.LIGHTS_OUT_TRANSPARENT -> true diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index d257288637df..c31e34c50b06 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -47,6 +47,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; +import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlag; @@ -59,6 +60,9 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.core.StatusBarConnectedDisplays; import com.android.systemui.statusbar.core.StatusBarRootModernization; +import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; @@ -78,6 +82,8 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarVie import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; import com.android.systemui.util.CarrierConfigTracker; @@ -156,6 +162,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final NotificationIconContainerStatusBarViewBinder mNicViewBinder; private final DemoModeController mDemoModeController; + private final StatusBarWindowControllerStore mStatusBarWindowControllerStore; + private final StatusBarConfigurationControllerStore mStatusBarConfigurationControllerStore; + private final DarkIconDispatcherStore mDarkIconDispatcherStore; private List<String> mBlockedIcons = new ArrayList<>(); private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>(); @@ -263,7 +272,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue DumpManager dumpManager, StatusBarWindowStateController statusBarWindowStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, - DemoModeController demoModeController) { + DemoModeController demoModeController, + StatusBarWindowControllerStore statusBarWindowControllerStore, + StatusBarConfigurationControllerStore statusBarConfigurationControllerStore, + DarkIconDispatcherStore darkIconDispatcherStore) { mHomeStatusBarComponentFactory = homeStatusBarComponentFactory; mOngoingCallController = ongoingCallController; mAnimationScheduler = animationScheduler; @@ -287,6 +299,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mStatusBarWindowStateController = statusBarWindowStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mDemoModeController = demoModeController; + mStatusBarWindowControllerStore = statusBarWindowControllerStore; + mStatusBarConfigurationControllerStore = statusBarConfigurationControllerStore; + mDarkIconDispatcherStore = darkIconDispatcherStore; } private final DemoMode mDemoModeCallback = new DemoMode() { @@ -337,8 +352,27 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mDumpManager.registerDumpable(getDumpableName(), this); - mHomeStatusBarComponent = mHomeStatusBarComponentFactory.create( - (PhoneStatusBarView) getView()); + int displayId = view.getContext().getDisplayId(); + StatusBarConfigurationController configurationController = + mStatusBarConfigurationControllerStore.forDisplay(displayId); + if (configurationController == null) { + return; + } + StatusBarWindowController statusBarWindowController = + mStatusBarWindowControllerStore.forDisplay(displayId); + if (statusBarWindowController == null) { + return; + } + DarkIconDispatcher darkIconDispatcher = mDarkIconDispatcherStore.forDisplay(displayId); + if (darkIconDispatcher == null) { + return; + } + mHomeStatusBarComponent = + mHomeStatusBarComponentFactory.create( + (PhoneStatusBarView) getView(), + configurationController, + statusBarWindowController, + darkIconDispatcher); mHomeStatusBarComponent.init(); mStartableStates.clear(); for (Startable startable : mHomeStatusBarComponent.getStartables()) { @@ -453,6 +487,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onResume() { super.onResume(); + if (mHomeStatusBarComponent == null) { + return; + } mCommandQueue.addCallback(this); mStatusBarStateController.addCallback(this); initOngoingCallChip(); @@ -468,6 +505,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onPause() { super.onPause(); + if (mHomeStatusBarComponent == null) { + return; + } mCommandQueue.removeCallback(this); mStatusBarStateController.removeCallback(this); if (!StatusBarRootModernization.isEnabled()) { @@ -480,6 +520,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onDestroyView() { super.onDestroyView(); + if (mHomeStatusBarComponent == null) { + return; + } mStatusBarIconController.removeIconGroup(mDarkIconManager); mCarrierConfigTracker.removeCallback(mCarrierConfigCallback); mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java index f8ad0f2324bc..5837752abdaf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java @@ -20,6 +20,7 @@ import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.dagger.qualifiers.DisplaySpecific; import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.plugins.DarkIconDispatcher; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.LegacyLightsOutNotifController; @@ -29,6 +30,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; import com.android.systemui.statusbar.phone.StatusBarDemoMode; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; +import com.android.systemui.statusbar.window.StatusBarWindowController; import dagger.BindsInstance; import dagger.Subcomponent; @@ -57,7 +59,10 @@ public interface HomeStatusBarComponent { interface Factory { /** */ HomeStatusBarComponent create( - @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView); + @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView, + @BindsInstance StatusBarConfigurationController configurationController, + @BindsInstance StatusBarWindowController statusBarWindowController, + @BindsInstance @DisplaySpecific DarkIconDispatcher darkIconDispatcher); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java index 182f8d7e2fd6..6a331b938cf1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java @@ -22,19 +22,14 @@ import android.view.ViewStub; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.dagger.qualifiers.DisplaySpecific; import com.android.systemui.dagger.qualifiers.RootView; -import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.res.R; import com.android.systemui.statusbar.HeadsUpStatusBarView; -import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore; -import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; -import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.window.StatusBarWindowController; -import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import dagger.Module; import dagger.Provides; @@ -149,29 +144,4 @@ public interface HomeStatusBarModule { static int displayId(@RootView PhoneStatusBarView view) { return view.getContext().getDisplayId(); } - - /** */ - @Provides - @HomeStatusBarScope - static StatusBarConfigurationController configurationController( - @DisplaySpecific int displayId, StatusBarConfigurationControllerStore store) { - return store.forDisplay(displayId); - } - - /** */ - @Provides - @HomeStatusBarScope - static StatusBarWindowController provideWindowController( - @DisplaySpecific int displayId, StatusBarWindowControllerStore store) { - return store.forDisplay(displayId); - } - - /** */ - @Provides - @HomeStatusBarScope - @DisplaySpecific - static DarkIconDispatcher darkIconDispatcher( - @DisplaySpecific int displayId, DarkIconDispatcherStore store) { - return store.forDisplay(displayId); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt index c3299bbd40e6..7243ba7def58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt @@ -70,6 +70,8 @@ constructor( ) { fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView { val composeView = ComposeView(root.context) + val darkIconDispatcher = + darkIconDispatcherStore.forDisplay(root.context.displayId) ?: return composeView composeView.apply { setContent { StatusBarRoot( @@ -80,7 +82,7 @@ constructor( darkIconManagerFactory = darkIconManagerFactory, iconController = iconController, ongoingCallController = ongoingCallController, - darkIconDispatcher = darkIconDispatcherStore.forDisplay(root.context.displayId), + darkIconDispatcher = darkIconDispatcher, eventAnimationInteractor = eventAnimationInteractor, onViewCreated = andThen, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt index dcfbc5d6432d..c9cc17389c17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt @@ -63,6 +63,7 @@ import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -216,7 +217,7 @@ constructor( } else { combine( notificationsInteractor.areAnyNotificationsPresent, - lightsOutInteractor.isLowProfile(displayId), + lightsOutInteractor.isLowProfile(displayId) ?: flowOf(false), ) { hasNotifications, isLowProfile -> hasNotifications && isLowProfile } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java index 811a2ec44ccc..848e91d6f896 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java @@ -163,7 +163,18 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController mLp = getBarLayoutParams(mContext.getDisplay().getRotation()); Trace.endSection(); - mWindowManager.addView(mStatusBarWindowView, mLp); + try { + mWindowManager.addView(mStatusBarWindowView, mLp); + } catch (WindowManager.InvalidDisplayException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + Log.e( + TAG, + "Unable to add view to WindowManager. Display with id " + + mContext.getDisplayId() + + " doesn't exist anymore.", + e); + } mLpChanged.copyFrom(mLp); mContentInsetsProvider.addCallback(this::calculateStatusBarLocationsForAllRotations); @@ -176,7 +187,15 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController public void stop() { StatusBarConnectedDisplays.assertInNewMode(); - mWindowManager.removeView(mStatusBarWindowView); + try { + mWindowManager.removeView(mStatusBarWindowView); + } catch (IllegalArgumentException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + // When that happens, adding the View to WindowManager fails, and therefore removing + // it here will fail too, since it wasn't added in the first place. + Log.e(TAG, "Failed to remove View from WindowManager. View was not attached", e); + } if (StatusBarRootModernization.isEnabled()) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt index 74031612f28e..f7688d2feab5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt @@ -54,19 +54,23 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): StatusBarWindowController { + override fun createInstanceForDisplay(displayId: Int): StatusBarWindowController? { val statusBarDisplayContext = displayWindowPropertiesRepository.get( displayId = displayId, windowType = WindowManager.LayoutParams.TYPE_STATUS_BAR, - ) + ) ?: return null + val statusBarConfigurationController = + statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null + val contentInsetsProvider = + statusBarContentInsetsProviderStore.forDisplay(displayId) ?: return null val viewCaptureAwareWindowManager = viewCaptureAwareWindowManagerFactory.create(statusBarDisplayContext.windowManager) return controllerFactory.create( statusBarDisplayContext.context, viewCaptureAwareWindowManager, - statusBarConfigurationControllerStore.forDisplay(displayId), - statusBarContentInsetsProviderStore.forDisplay(displayId), + statusBarConfigurationController, + contentInsetsProvider, ) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt index 8bb02793ecc5..8733eeb2bd05 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt @@ -71,6 +71,8 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { fun CoroutineScope.bind(view: View) { val volumeDialogBackgroundView = view.requireViewById<View>(R.id.volume_dialog_background) + val ringerHBackgroundView = + view.requireViewById<View>(R.id.volume_ringer_horizontal_background) val drawerContainer = view.requireViewById<MotionLayout>(R.id.volume_ringer_drawer) val unselectedButtonUiModel = RingerButtonUiModel.getUnselectedButton(view.context) val selectedButtonUiModel = RingerButtonUiModel.getSelectedButton(view.context) @@ -84,6 +86,11 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { ) var backgroundAnimationProgress: Float by Delegates.observable(0F) { _, _, progress -> + ringerHBackgroundView.applyCorners( + fullRadius = volumeDialogBgFullRadius, + diff = volumeDialogBgFullRadius - volumeDialogBgSmallRadius, + progress, + ) volumeDialogBackgroundView.applyCorners( fullRadius = volumeDialogBgFullRadius, diff = volumeDialogBgFullRadius - volumeDialogBgSmallRadius, @@ -95,6 +102,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { } drawerContainer.setTransitionListener(ringerDrawerTransitionListener) volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate() + ringerHBackgroundView.background = ringerHBackgroundView.background.mutate() viewModel.ringerViewModel .mapLatest { ringerState -> @@ -184,6 +192,8 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { ) volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate() + ringerHBackgroundView.background = + ringerHBackgroundView.background.mutate() } } } @@ -193,6 +203,9 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) { volumeDialogBackgroundView.setBackgroundResource( R.drawable.volume_dialog_background ) + ringerHBackgroundView.setBackgroundResource( + R.drawable.volume_dialog_background + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt index 25ba1bd3a1d8..69ffa3890437 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt @@ -119,6 +119,15 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer( ) when (lastOrientation) { ORIENTATION_LANDSCAPE -> { + if (index == 0) { + setMargin( + button.id, + ConstraintSet.LEFT, + motionLayout.context.resources.getDimensionPixelSize( + R.dimen.volume_dialog_ringer_drawer_left_margin + ), + ) + } setButtonPositionLandscapeConstraints(motionLayout, index, button) if (index != motionLayout.childCount - 1) { setMargin( @@ -134,6 +143,9 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer( setMargin(button.id, ConstraintSet.BOTTOM, 0) } ORIENTATION_PORTRAIT -> { + if (index == 0) { + setMargin(button.id, ConstraintSet.LEFT, 0) + } setButtonPositionPortraitConstraints(motionLayout, index, button) if (index != motionLayout.childCount - 1) { setMargin( diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java index 51a7b5f6f979..bc9d4c7fa0e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java @@ -42,6 +42,7 @@ import com.android.app.viewcapture.ViewCapture; import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.AccessibilityButtonModeObserver; @@ -95,6 +96,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { private Lazy<ViewCapture> mLazyViewCapture; @Mock private NavigationModeController mNavigationModeController; + @Mock + private HearingAidDeviceManager mHearingAidDeviceManager; @Before public void setUp() throws Exception { @@ -170,7 +173,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { mController = setUpController(); mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager, mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings, - mNavigationModeController); + mNavigationModeController, mHearingAidDeviceManager); captureKeyguardUpdateMonitorCallback(); mKeyguardCallback.onUserUnlocked(); @@ -198,7 +201,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { mController = setUpController(); mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager, mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings, - mNavigationModeController); + mNavigationModeController, mHearingAidDeviceManager); captureKeyguardUpdateMonitorCallback(); mKeyguardCallback.onUserSwitching(fakeUserId); @@ -213,7 +216,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { mController = setUpController(); mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager, mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings, - mNavigationModeController); + mNavigationModeController, mHearingAidDeviceManager); captureKeyguardUpdateMonitorCallback(); mKeyguardCallback.onUserUnlocked(); mKeyguardCallback.onKeyguardVisibilityChanged(true); @@ -368,9 +371,9 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { final AccessibilityFloatingMenuController controller = new AccessibilityFloatingMenuController(mContextWrapper, windowManager, viewCaptureAwareWindowManager, displayManager, mAccessibilityManager, - mTargetsObserver, mModeObserver, mKeyguardUpdateMonitor, mSecureSettings, - displayTracker, mNavigationModeController, new Handler( - mTestableLooper.getLooper())); + mTargetsObserver, mModeObserver, mHearingAidDeviceManager, + mKeyguardUpdateMonitor, mSecureSettings, displayTracker, + mNavigationModeController, new Handler(mTestableLooper.getLooper())); controller.init(); return controller; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java index dddaabb66022..856c37934251 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.graphics.PointF; - import android.testing.TestableLooper; import android.view.View; import android.view.ViewPropertyAnimator; @@ -40,6 +39,7 @@ import androidx.dynamicanimation.animation.SpringForce; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.Prefs; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.utils.TestUtils; @@ -74,6 +74,8 @@ public class MenuAnimationControllerTest extends SysuiTestCase { @Mock private AccessibilityManager mAccessibilityManager; + @Mock + private HearingAidDeviceManager mHearingAidDeviceManager; @Before public void setUp() throws Exception { @@ -82,7 +84,7 @@ public class MenuAnimationControllerTest extends SysuiTestCase { stubWindowManager); final SecureSettings secureSettings = TestUtils.mockSecureSettings(); final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager, - secureSettings); + secureSettings, mHearingAidDeviceManager); mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance, secureSettings)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java index 400b3b388c31..33cfb3890e71 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java @@ -77,6 +77,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.accessibility.common.ShortcutConstants; import com.android.internal.accessibility.dialog.AccessibilityTarget; import com.android.internal.messages.nano.SystemMessageProto; +import com.android.settingslib.bluetooth.HearingAidDeviceManager; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestableContext; @@ -140,6 +141,8 @@ public class MenuViewLayerTest extends SysuiTestCase { @Mock private AccessibilityManager mStubAccessibilityManager; @Mock + private HearingAidDeviceManager mHearingAidDeviceManager; + @Mock private PackageManager mMockPackageManager; private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings(); @@ -160,7 +163,7 @@ public class MenuViewLayerTest extends SysuiTestCase { doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics(); mMenuViewModel = new MenuViewModel( - mSpyContext, mStubAccessibilityManager, mSecureSettings); + mSpyContext, mStubAccessibilityManager, mSecureSettings, mHearingAidDeviceManager); MenuViewAppearance menuViewAppearance = new MenuViewAppearance( mSpyContext, mStubWindowManager); mMenuView = spy( @@ -419,9 +422,10 @@ public class MenuViewLayerTest extends SysuiTestCase { @Test @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT) public void onDismissAction_incrementsTexMetricDismiss() { - mMenuViewModel.onTargetFeaturesChanged( - List.of(new TestAccessibilityTarget(mSpyContext, 1234), - new TestAccessibilityTarget(mSpyContext, 5678))); + List<AccessibilityTarget> testTargets = new ArrayList<>(); + testTargets.add(new TestAccessibilityTarget(mSpyContext, 1234)); + testTargets.add(new TestAccessibilityTarget(mSpyContext, 5678)); + mMenuViewModel.onTargetFeaturesChanged(testTargets); mMenuViewLayer.dispatchAccessibilityAction(R.id.action_remove_menu); @@ -431,9 +435,10 @@ public class MenuViewLayerTest extends SysuiTestCase { @Test @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT) public void onEditAction_incrementsTexMetricEdit() { - mMenuViewModel.onTargetFeaturesChanged( - List.of(new TestAccessibilityTarget(mSpyContext, 1234), - new TestAccessibilityTarget(mSpyContext, 5678))); + List<AccessibilityTarget> testTargets = new ArrayList<>(); + testTargets.add(new TestAccessibilityTarget(mSpyContext, 1234)); + testTargets.add(new TestAccessibilityTarget(mSpyContext, 5678)); + mMenuViewModel.onTargetFeaturesChanged(testTargets); mMenuViewLayer.dispatchAccessibilityAction(R.id.action_edit); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java index 782b24825bcf..e4a4953063bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java @@ -9,8 +9,8 @@ import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource; -import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT; -import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_VERTICAL_WEIGHT; +import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.TOAST_PARAMS_HORIZONTAL_WEIGHT; +import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.TOAST_PARAMS_VERTICAL_WEIGHT; import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX; import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN; import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE; @@ -105,7 +105,7 @@ import java.util.Map; @SmallTest @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) -public class InternetDialogDelegateControllerTest extends SysuiTestCase { +public class InternetDetailsContentControllerTest extends SysuiTestCase { private static final int SUB_ID = 1; private static final int SUB_ID2 = 2; @@ -160,7 +160,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Mock private WifiUtils.InternetIconInjector mWifiIconInjector; @Mock - InternetDialogController.InternetDialogCallback mInternetDialogCallback; + InternetDetailsContentController.InternetDialogCallback mInternetDialogCallback; @Mock private ViewCaptureAwareWindowManager mWindowManager; @Mock @@ -189,7 +189,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { private FakeFeatureFlags mFlags = new FakeFeatureFlags(); private TestableResources mTestableResources; - private InternetDialogController mInternetDialogController; + private InternetDetailsContentController mInternetDetailsContentController; private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); private List<WifiEntry> mAccessPoints = new ArrayList<>(); private List<WifiEntry> mWifiEntries = new ArrayList<>(); @@ -229,7 +229,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(mSystemUIToast.getInAnimation()).thenReturn(mAnimator); when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); - mInternetDialogController = new InternetDialogController(mContext, + mInternetDetailsContentController = new InternetDetailsContentController(mContext, mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController, mSubscriptionManager, mTelephonyManager, mWifiManager, mConnectivityManager, mHandler, mExecutor, mBroadcastDispatcher, @@ -238,11 +238,11 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { mCarrierConfigTracker, mLocationController, mDialogTransitionAnimator, mWifiStateWorker, mFlags); mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, - mInternetDialogController.mOnSubscriptionsChangedListener); - mInternetDialogController.onStart(mInternetDialogCallback, true); - mInternetDialogController.onAccessPointsChanged(mAccessPoints); - mInternetDialogController.mActivityStarter = mActivityStarter; - mInternetDialogController.mWifiIconInjector = mWifiIconInjector; + mInternetDetailsContentController.mOnSubscriptionsChangedListener); + mInternetDetailsContentController.onStart(mInternetDialogCallback, true); + mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints); + mInternetDetailsContentController.mActivityStarter = mActivityStarter; + mInternetDetailsContentController.mWifiIconInjector = mWifiIconInjector; mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, false); mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, false); @@ -260,7 +260,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Test public void connectCarrierNetwork_mergedCarrierEntryCanConnect_connectAndCreateSysUiToast() { - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); when(spyController.isMobileDataEnabled()).thenReturn(true); when(mKeyguardStateController.isUnlocked()).thenReturn(true); when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork); @@ -282,7 +282,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Test public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenSettingsOff() { - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); when(spyController.isMobileDataEnabled()).thenReturn(false); mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now, TOAST_MESSAGE_STRING); @@ -296,7 +296,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Test public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenKeyguardLocked() { - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); when(spyController.isMobileDataEnabled()).thenReturn(true); when(mKeyguardStateController.isUnlocked()).thenReturn(false); @@ -311,8 +311,6 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Test public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenMobileIsPrimary() { - InternetDialogController spyController = spy(mInternetDialogController); - when(spyController.isMobileDataEnabled()).thenReturn(true); when(mKeyguardStateController.isUnlocked()).thenReturn(true); when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork); when(mConnectivityManager.getNetworkCapabilities(mNetwork)) @@ -322,7 +320,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now, TOAST_MESSAGE_STRING); - mInternetDialogController.connectCarrierNetwork(); + mInternetDetailsContentController.connectCarrierNetwork(); verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */); verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(), @@ -333,7 +331,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void makeOverlayToast_withGravityFlags_addViewWithLayoutParams() { mTestableResources.addOverride(TOAST_MESSAGE_STRING_ID, TOAST_MESSAGE_STRING); - mInternetDialogController.makeOverlayToast(TOAST_MESSAGE_STRING_ID); + mInternetDetailsContentController.makeOverlayToast(TOAST_MESSAGE_STRING_ID); ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor = ArgumentCaptor.forClass( WindowManager.LayoutParams.class); @@ -349,7 +347,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void makeOverlayToast_withAnimation_verifyAnimatorStart() { mTestableResources.addOverride(TOAST_MESSAGE_STRING_ID, TOAST_MESSAGE_STRING); - mInternetDialogController.makeOverlayToast(TOAST_MESSAGE_STRING_ID); + mInternetDetailsContentController.makeOverlayToast(TOAST_MESSAGE_STRING_ID); verify(mAnimator).start(); } @@ -358,7 +356,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() { fakeAirplaneModeEnabled(true); - assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(), + assertTrue(TextUtils.equals(mInternetDetailsContentController.getDialogTitleText(), getResourcesString("airplane_mode"))); } @@ -366,7 +364,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void getDialogTitleText_withAirplaneModeOff_returnInternet() { fakeAirplaneModeEnabled(false); - assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(), + assertTrue(TextUtils.equals(mInternetDetailsContentController.getDialogTitleText(), getResourcesString("quick_settings_internet_label"))); } @@ -375,13 +373,13 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { fakeAirplaneModeEnabled(true); when(mWifiStateWorker.isWifiEnabled()).thenReturn(false); - assertThat(mInternetDialogController.getSubtitleText(false)) + assertThat(mInternetDetailsContentController.getSubtitleText(false)) .isEqualTo(getResourcesString("wifi_is_off")); // if the Wi-Fi disallow config, then don't return Wi-Fi related string. - mInternetDialogController.mCanConfigWifi = false; + mInternetDetailsContentController.mCanConfigWifi = false; - assertThat(mInternetDialogController.getSubtitleText(false)) + assertThat(mInternetDetailsContentController.getSubtitleText(false)) .isNotEqualTo(getResourcesString("wifi_is_off")); } @@ -390,13 +388,13 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { fakeAirplaneModeEnabled(false); when(mWifiStateWorker.isWifiEnabled()).thenReturn(false); - assertThat(mInternetDialogController.getSubtitleText(false)) + assertThat(mInternetDetailsContentController.getSubtitleText(false)) .isEqualTo(getResourcesString("wifi_is_off")); // if the Wi-Fi disallow config, then don't return Wi-Fi related string. - mInternetDialogController.mCanConfigWifi = false; + mInternetDetailsContentController.mCanConfigWifi = false; - assertThat(mInternetDialogController.getSubtitleText(false)) + assertThat(mInternetDetailsContentController.getSubtitleText(false)) .isNotEqualTo(getResourcesString("wifi_is_off")); } @@ -404,15 +402,15 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void getSubtitleText_withNoWifiEntry_returnSearchWifi() { fakeAirplaneModeEnabled(false); when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); - mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); + mInternetDetailsContentController.onAccessPointsChanged(null /* accessPoints */); - assertThat(mInternetDialogController.getSubtitleText(true)) + assertThat(mInternetDetailsContentController.getSubtitleText(true)) .isEqualTo(getResourcesString("wifi_empty_list_wifi_on")); // if the Wi-Fi disallow config, then don't return Wi-Fi related string. - mInternetDialogController.mCanConfigWifi = false; + mInternetDetailsContentController.mCanConfigWifi = false; - assertThat(mInternetDialogController.getSubtitleText(true)) + assertThat(mInternetDetailsContentController.getSubtitleText(true)) .isNotEqualTo(getResourcesString("wifi_empty_list_wifi_on")); } @@ -422,13 +420,13 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { fakeAirplaneModeEnabled(false); when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); - assertThat(mInternetDialogController.getSubtitleText(false)) + assertThat(mInternetDetailsContentController.getSubtitleText(false)) .isEqualTo(getResourcesString("tap_a_network_to_connect")); // if the Wi-Fi disallow config, then don't return Wi-Fi related string. - mInternetDialogController.mCanConfigWifi = false; + mInternetDetailsContentController.mCanConfigWifi = false; - assertThat(mInternetDialogController.getSubtitleText(false)) + assertThat(mInternetDetailsContentController.getSubtitleText(false)) .isNotEqualTo(getResourcesString("tap_a_network_to_connect")); } @@ -438,14 +436,14 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); when(mKeyguardStateController.isUnlocked()).thenReturn(false); - assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false), + assertTrue(TextUtils.equals(mInternetDetailsContentController.getSubtitleText(false), getResourcesString("unlock_to_view_networks"))); } @Test public void getSubtitleText_withNoService_returnNoNetworksAvailable() { mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true); - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); fakeAirplaneModeEnabled(false); when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); spyController.onAccessPointsChanged(null /* accessPoints */); @@ -466,7 +464,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Test public void getSubtitleText_withNoService_returnNoNetworksAvailable_flagOff() { - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); fakeAirplaneModeEnabled(false); when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); spyController.onAccessPointsChanged(null /* accessPoints */); @@ -488,22 +486,22 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() { fakeAirplaneModeEnabled(false); when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); - mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); - InternetDialogController spyController = spy(mInternetDialogController); + mInternetDetailsContentController.onAccessPointsChanged(null /* accessPoints */); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState(); spyController.mSubIdServiceState.put(SUB_ID, mServiceState); - assertThat(mInternetDialogController.getSubtitleText(false)) + assertThat(mInternetDetailsContentController.getSubtitleText(false)) .isEqualTo(getResourcesString("non_carrier_network_unavailable")); // if the Wi-Fi disallow config, then don't return Wi-Fi related string. - mInternetDialogController.mCanConfigWifi = false; + mInternetDetailsContentController.mCanConfigWifi = false; when(spyController.isMobileDataEnabled()).thenReturn(false); - assertThat(mInternetDialogController.getSubtitleText(false)) + assertThat(mInternetDetailsContentController.getSubtitleText(false)) .isNotEqualTo(getResourcesString("non_carrier_network_unavailable")); } @@ -511,21 +509,22 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void getSubtitleText_withCarrierNetworkActiveOnly_returnNoOtherAvailable() { fakeAirplaneModeEnabled(false); when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); - mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); + mInternetDetailsContentController.onAccessPointsChanged(null /* accessPoints */); when(mMergedCarrierEntry.isDefaultNetwork()).thenReturn(true); - assertThat(mInternetDialogController.getSubtitleText(false)) + assertThat(mInternetDetailsContentController.getSubtitleText(false)) .isEqualTo(getResourcesString("non_carrier_network_unavailable")); } @Test public void getWifiDetailsSettingsIntent_withNoKey_returnNull() { - assertThat(mInternetDialogController.getWifiDetailsSettingsIntent(null)).isNull(); + assertThat(mInternetDetailsContentController.getWifiDetailsSettingsIntent(null)).isNull(); } @Test public void getWifiDetailsSettingsIntent_withKey_returnIntent() { - assertThat(mInternetDialogController.getWifiDetailsSettingsIntent("test_key")).isNotNull(); + assertThat(mInternetDetailsContentController.getWifiDetailsSettingsIntent( + "test_key")).isNotNull(); } @Test @@ -533,7 +532,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { final Drawable drawable = mock(Drawable.class); when(mWifiIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(drawable); - mInternetDialogController.getInternetWifiDrawable(mConnectedEntry); + mInternetDetailsContentController.getInternetWifiDrawable(mConnectedEntry); verify(mWifiIconInjector).getIcon(eq(false), anyInt()); verify(drawable).setTint(mContext.getColor(R.color.connected_network_primary_color)); @@ -543,7 +542,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void getWifiDrawable_withWifiLevelUnreachable_returnNull() { when(mConnectedEntry.getLevel()).thenReturn(WIFI_LEVEL_UNREACHABLE); - assertThat(mInternetDialogController.getWifiDrawable(mConnectedEntry)).isNull(); + assertThat(mInternetDetailsContentController.getWifiDrawable(mConnectedEntry)).isNull(); } @Test @@ -553,19 +552,21 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { Drawable hotspotDrawable = mock(Drawable.class); mTestableResources.addOverride(getHotspotIconResource(DEVICE_TYPE_PHONE), hotspotDrawable); - assertThat(mInternetDialogController.getWifiDrawable(entry)).isEqualTo(hotspotDrawable); + assertThat(mInternetDetailsContentController.getWifiDrawable(entry)).isEqualTo( + hotspotDrawable); } @Test public void startActivityForDialog_always_startActivityWithoutDismissShade() { - mInternetDialogController.startActivityForDialog(mock(Intent.class)); + mInternetDetailsContentController.startActivityForDialog(mock(Intent.class)); verify(mActivityStarter).startActivity(any(Intent.class), eq(false) /* dismissShade */); } @Test public void launchWifiDetailsSetting_withNoWifiEntryKey_doNothing() { - mInternetDialogController.launchWifiDetailsSetting(null /* key */, mDialogLaunchView); + mInternetDetailsContentController.launchWifiDetailsSetting(null /* key */, + mDialogLaunchView); verify(mActivityStarter, never()) .postStartActivityDismissingKeyguard(any(Intent.class), anyInt()); @@ -573,7 +574,8 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Test public void launchWifiDetailsSetting_withWifiEntryKey_startActivity() { - mInternetDialogController.launchWifiDetailsSetting("wifi_entry_key", mDialogLaunchView); + mInternetDetailsContentController.launchWifiDetailsSetting("wifi_entry_key", + mDialogLaunchView); verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt(), any()); @@ -583,22 +585,22 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void isDeviceLocked_keyguardIsUnlocked_returnFalse() { when(mKeyguardStateController.isUnlocked()).thenReturn(true); - assertThat(mInternetDialogController.isDeviceLocked()).isFalse(); + assertThat(mInternetDetailsContentController.isDeviceLocked()).isFalse(); } @Test public void isDeviceLocked_keyguardIsLocked_returnTrue() { when(mKeyguardStateController.isUnlocked()).thenReturn(false); - assertThat(mInternetDialogController.isDeviceLocked()).isTrue(); + assertThat(mInternetDetailsContentController.isDeviceLocked()).isTrue(); } @Test public void onAccessPointsChanged_canNotConfigWifi_doNothing() { reset(mInternetDialogCallback); - mInternetDialogController.mCanConfigWifi = false; + mInternetDetailsContentController.mCanConfigWifi = false; - mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); + mInternetDetailsContentController.onAccessPointsChanged(null /* accessPoints */); verify(mInternetDialogCallback, never()).onAccessPointsChanged(any(), any(), anyBoolean()); } @@ -607,7 +609,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void onAccessPointsChanged_nullAccessPoints_callbackBothNull() { reset(mInternetDialogCallback); - mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); + mInternetDetailsContentController.onAccessPointsChanged(null /* accessPoints */); verify(mInternetDialogCallback).onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */, false /* hasMoreEntry */); @@ -619,7 +621,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { mAccessPoints.clear(); mAccessPoints.add(mConnectedEntry); - mInternetDialogController.onAccessPointsChanged(mAccessPoints); + mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints); mWifiEntries.clear(); verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry, @@ -632,7 +634,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { mAccessPoints.clear(); mAccessPoints.add(mWifiEntry1); - mInternetDialogController.onAccessPointsChanged(mAccessPoints); + mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints); mWifiEntries.clear(); mWifiEntries.add(mWifiEntry1); @@ -647,7 +649,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { mAccessPoints.add(mConnectedEntry); mAccessPoints.add(mWifiEntry1); - mInternetDialogController.onAccessPointsChanged(mAccessPoints); + mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints); mWifiEntries.clear(); mWifiEntries.add(mWifiEntry1); @@ -663,7 +665,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { mAccessPoints.add(mWifiEntry1); mAccessPoints.add(mWifiEntry2); - mInternetDialogController.onAccessPointsChanged(mAccessPoints); + mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints); mWifiEntries.clear(); mWifiEntries.add(mWifiEntry1); @@ -681,7 +683,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { mAccessPoints.add(mWifiEntry2); mAccessPoints.add(mWifiEntry3); - mInternetDialogController.onAccessPointsChanged(mAccessPoints); + mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints); mWifiEntries.clear(); mWifiEntries.add(mWifiEntry1); @@ -699,7 +701,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { mAccessPoints.add(mWifiEntry3); mAccessPoints.add(mWifiEntry4); - mInternetDialogController.onAccessPointsChanged(mAccessPoints); + mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints); mWifiEntries.clear(); mWifiEntries.add(mWifiEntry1); @@ -718,7 +720,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(mWifiEntry1.hasInternetAccess()).thenReturn(false); mAccessPoints.add(mWifiEntry1); - mInternetDialogController.onAccessPointsChanged(mAccessPoints); + mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints); mWifiEntries.clear(); mWifiEntries.add(mWifiEntry1); @@ -735,9 +737,10 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(mWifiEntry1.hasInternetAccess()).thenReturn(false); mAccessPoints.add(mWifiEntry1); - mInternetDialogController.onAccessPointsChanged(mAccessPoints); + mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints); - verify(mWifiEntry1).setListener(mInternetDialogController.mConnectedWifiInternetMonitor); + verify(mWifiEntry1).setListener( + mInternetDetailsContentController.mConnectedWifiInternetMonitor); } @Test @@ -746,8 +749,9 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED); when(mWifiEntry1.isDefaultNetwork()).thenReturn(true); when(mWifiEntry1.hasInternetAccess()).thenReturn(false); - InternetDialogController.ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor = - mInternetDialogController.mConnectedWifiInternetMonitor; + InternetDetailsContentController.ConnectedWifiInternetMonitor + mConnectedWifiInternetMonitor = + mInternetDetailsContentController.mConnectedWifiInternetMonitor; mConnectedWifiInternetMonitor.registerCallbackIfNeed(mWifiEntry1); // When the hasInternetAccess() changed to true, and call back the onUpdated() function. @@ -762,7 +766,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { reset(mInternetDialogCallback); when(mWifiStateWorker.isWifiEnabled()).thenReturn(false); - mInternetDialogController.onWifiScan(true); + mInternetDetailsContentController.onWifiScan(true); verify(mInternetDialogCallback).onWifiScan(false); } @@ -772,7 +776,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { reset(mInternetDialogCallback); when(mKeyguardStateController.isUnlocked()).thenReturn(false); - mInternetDialogController.onWifiScan(true); + mInternetDetailsContentController.onWifiScan(true); verify(mInternetDialogCallback).onWifiScan(false); } @@ -781,7 +785,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void onWifiScan_onWifiScanFalse_callbackOnWifiScanFalse() { reset(mInternetDialogCallback); - mInternetDialogController.onWifiScan(false); + mInternetDetailsContentController.onWifiScan(false); verify(mInternetDialogCallback).onWifiScan(false); } @@ -790,7 +794,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void onWifiScan_onWifiScanTrue_callbackOnWifiScanTrue() { reset(mInternetDialogCallback); - mInternetDialogController.onWifiScan(true); + mInternetDetailsContentController.onWifiScan(true); verify(mInternetDialogCallback).onWifiScan(true); } @@ -800,7 +804,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID)) .thenReturn(true); - mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true); + mInternetDetailsContentController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true); verify(mMergedCarrierEntry, never()).setEnabled(anyBoolean()); } @@ -811,7 +815,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { .thenReturn(false); when(mAccessPointController.getMergedCarrierEntry()).thenReturn(null); - mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true); + mInternetDetailsContentController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true); } @Test @@ -819,11 +823,11 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID)) .thenReturn(false); - mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true); + mInternetDetailsContentController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true); verify(mMergedCarrierEntry).setEnabled(true); - mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, false); + mInternetDetailsContentController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, false); verify(mMergedCarrierEntry).setEnabled(false); } @@ -833,11 +837,11 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(mLocationController.isLocationEnabled()).thenReturn(false); when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false); - assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse(); + assertThat(mInternetDetailsContentController.isWifiScanEnabled()).isFalse(); when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true); - assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse(); + assertThat(mInternetDetailsContentController.isWifiScanEnabled()).isFalse(); } @Test @@ -845,17 +849,17 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(mLocationController.isLocationEnabled()).thenReturn(true); when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false); - assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse(); + assertThat(mInternetDetailsContentController.isWifiScanEnabled()).isFalse(); when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true); - assertThat(mInternetDialogController.isWifiScanEnabled()).isTrue(); + assertThat(mInternetDetailsContentController.isWifiScanEnabled()).isTrue(); } @Test public void getSignalStrengthIcon_differentSubId() { mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true); - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); Drawable icons = spyController.getSignalStrengthIcon(SUB_ID, mContext, 1, 1, 0, false); Drawable icons2 = spyController.getSignalStrengthIcon(SUB_ID2, mContext, 1, 1, 0, false); @@ -870,12 +874,12 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { doReturn(SUB_ID2).when(info).getSubscriptionId(); when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info); - int subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); + int subId = mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId(); assertThat(subId).isEqualTo(SUB_ID2); // active on CBRS doReturn(true).when(info).isOpportunistic(); - subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); + subId = mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId(); assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID); // active on DDS @@ -883,7 +887,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { doReturn(SUB_ID).when(info).getSubscriptionId(); when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info); - subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); + subId = mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId(); assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID); } @@ -894,7 +898,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { doReturn(SUB_ID2).when(info).getSubscriptionId(); when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info); - int subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); + int subId = mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId(); assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID); } @@ -908,22 +912,24 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { doReturn(false).when(info).isOpportunistic(); when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info); - mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); + mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId(); // 1st time is onStart(), 2nd time is getActiveAutoSwitchNonDdsSubId() verify(mTelephonyManager, times(2)).registerTelephonyCallback(any(), any()); - assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size()).isEqualTo(2); + assertThat(mInternetDetailsContentController.mSubIdTelephonyCallbackMap.size()).isEqualTo( + 2); // Adds non DDS subId again doReturn(SUB_ID2).when(info).getSubscriptionId(); doReturn(false).when(info).isOpportunistic(); when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info); - mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); + mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId(); // Does not add due to cached subInfo in mSubIdTelephonyCallbackMap. verify(mTelephonyManager, times(2)).registerTelephonyCallback(any(), any()); - assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size()).isEqualTo(2); + assertThat(mInternetDetailsContentController.mSubIdTelephonyCallbackMap.size()).isEqualTo( + 2); } @Test @@ -936,7 +942,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(SubscriptionManager.getResourcesForSubId(any(), eq(SUB_ID))).thenReturn(res1); when(SubscriptionManager.getResourcesForSubId(any(), eq(SUB_ID2))).thenReturn(res2); - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); Map<Integer, TelephonyDisplayInfo> mSubIdTelephonyDisplayInfoMap = spyController.mSubIdTelephonyDisplayInfoMap; TelephonyDisplayInfo info1 = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_EDGE, @@ -961,7 +967,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Test public void getMobileNetworkSummary_flagOff() { - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); doReturn(true).when(spyController).isMobileDataEnabled(); doReturn(true).when(spyController).activeNetworkIsCellular(); String dds = spyController.getMobileNetworkSummary(SUB_ID); @@ -972,7 +978,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Test public void launchMobileNetworkSettings_validSubId() { mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true); - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); doReturn(SUB_ID2).when(spyController).getActiveAutoSwitchNonDdsSubId(); spyController.launchMobileNetworkSettings(mDialogLaunchView); @@ -983,7 +989,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Test public void launchMobileNetworkSettings_invalidSubId() { mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true); - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID) .when(spyController).getActiveAutoSwitchNonDdsSubId(); spyController.launchMobileNetworkSettings(mDialogLaunchView); @@ -995,7 +1001,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { @Test public void setAutoDataSwitchMobileDataPolicy() { mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true); - mInternetDialogController.setAutoDataSwitchMobileDataPolicy(SUB_ID, true); + mInternetDetailsContentController.setAutoDataSwitchMobileDataPolicy(SUB_ID, true); verify(mTelephonyManager).setMobileDataPolicyEnabled(eq( TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH), eq(true)); @@ -1006,9 +1012,9 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { // Fake mobile data level as SIGNAL_STRENGTH_POOR(1) when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_POOR); // Fake carrier network level as WIFI_LEVEL_MAX(4) - when(mInternetDialogController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX); + when(mInternetDetailsContentController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX); - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); spyController.getSignalStrengthDrawableWithLevel(false /* isCarrierNetworkActive */, 0); verify(spyController).getSignalStrengthIcon(eq(0), any(), eq(SIGNAL_STRENGTH_POOR), @@ -1020,9 +1026,9 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { // Fake mobile data level as SIGNAL_STRENGTH_POOR(1) when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_POOR); // Fake carrier network level as WIFI_LEVEL_MAX(4) - when(mInternetDialogController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX); + when(mInternetDetailsContentController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX); - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); spyController.getSignalStrengthDrawableWithLevel(true /* isCarrierNetworkActive */, 0); verify(spyController).getSignalStrengthIcon(eq(0), any(), eq(WIFI_LEVEL_MAX), @@ -1033,14 +1039,16 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void getCarrierNetworkLevel_mergedCarrierEntryIsNull_returnMinLevel() { when(mAccessPointController.getMergedCarrierEntry()).thenReturn(null); - assertThat(mInternetDialogController.getCarrierNetworkLevel()).isEqualTo(WIFI_LEVEL_MIN); + assertThat(mInternetDetailsContentController.getCarrierNetworkLevel()).isEqualTo( + WIFI_LEVEL_MIN); } @Test public void getCarrierNetworkLevel_getUnreachableLevel_returnMinLevel() { when(mMergedCarrierEntry.getLevel()).thenReturn(WIFI_LEVEL_UNREACHABLE); - assertThat(mInternetDialogController.getCarrierNetworkLevel()).isEqualTo(WIFI_LEVEL_MIN); + assertThat(mInternetDetailsContentController.getCarrierNetworkLevel()).isEqualTo( + WIFI_LEVEL_MIN); } @Test @@ -1048,7 +1056,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { for (int level = WIFI_LEVEL_MIN; level <= WIFI_LEVEL_MAX; level++) { when(mMergedCarrierEntry.getLevel()).thenReturn(level); - assertThat(mInternetDialogController.getCarrierNetworkLevel()).isEqualTo(level); + assertThat(mInternetDetailsContentController.getCarrierNetworkLevel()).isEqualTo(level); } } @@ -1057,7 +1065,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { Resources res = mock(Resources.class); doReturn("Carrier network changing").when(res).getString(anyInt()); when(SubscriptionManager.getResourcesForSubId(any(), eq(SUB_ID))).thenReturn(res); - InternetDialogController spyController = spy(mInternetDialogController); + InternetDetailsContentController spyController = spy(mInternetDetailsContentController); Map<Integer, TelephonyDisplayInfo> mSubIdTelephonyDisplayInfoMap = spyController.mSubIdTelephonyDisplayInfoMap; TelephonyDisplayInfo info = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE, @@ -1076,14 +1084,14 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { public void getConfiguratorQrCodeGeneratorIntentOrNull_wifiNotShareable_returnNull() { mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, true); when(mConnectedEntry.canShare()).thenReturn(false); - assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull( + assertThat(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull( mConnectedEntry)).isNull(); } @Test public void getConfiguratorQrCodeGeneratorIntentOrNull_flagOff_returnNull() { mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, false); when(mConnectedEntry.canShare()).thenReturn(true); - assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull( + assertThat(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull( mConnectedEntry)).isNull(); } @@ -1092,7 +1100,7 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, true); when(mConnectedEntry.canShare()).thenReturn(true); when(mConnectedEntry.getWifiConfiguration()).thenReturn(null); - assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull( + assertThat(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull( mConnectedEntry)).isNull(); } @@ -1101,30 +1109,34 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, true); when(mConnectedEntry.canShare()).thenReturn(true); when(mConnectedEntry.getWifiConfiguration()).thenReturn(mWifiConfiguration); - assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull( + assertThat(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull( mConnectedEntry)).isNotNull(); } @Test public void onStop_cleanUp() { doReturn(SUB_ID).when(mTelephonyManager).getSubscriptionId(); - assertThat(mInternetDialogController.mSubIdTelephonyManagerMap.get(SUB_ID)).isEqualTo( + assertThat( + mInternetDetailsContentController.mSubIdTelephonyManagerMap.get(SUB_ID)).isEqualTo( mTelephonyManager); - assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.get(SUB_ID)).isNotNull(); - assertThat(mInternetDialogController.mCallback).isNotNull(); + assertThat(mInternetDetailsContentController.mSubIdTelephonyCallbackMap.get( + SUB_ID)).isNotNull(); + assertThat(mInternetDetailsContentController.mCallback).isNotNull(); - mInternetDialogController.onStop(); + mInternetDetailsContentController.onStop(); verify(mTelephonyManager).unregisterTelephonyCallback(any(TelephonyCallback.class)); - assertThat(mInternetDialogController.mSubIdTelephonyDisplayInfoMap.isEmpty()).isTrue(); - assertThat(mInternetDialogController.mSubIdTelephonyManagerMap.isEmpty()).isTrue(); - assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.isEmpty()).isTrue(); - verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(mInternetDialogController - .mOnSubscriptionsChangedListener); - verify(mAccessPointController).removeAccessPointCallback(mInternetDialogController); + assertThat( + mInternetDetailsContentController.mSubIdTelephonyDisplayInfoMap.isEmpty()).isTrue(); + assertThat(mInternetDetailsContentController.mSubIdTelephonyManagerMap.isEmpty()).isTrue(); + assertThat(mInternetDetailsContentController.mSubIdTelephonyCallbackMap.isEmpty()).isTrue(); + verify(mSubscriptionManager).removeOnSubscriptionsChangedListener( + mInternetDetailsContentController + .mOnSubscriptionsChangedListener); + verify(mAccessPointController).removeAccessPointCallback(mInternetDetailsContentController); verify(mConnectivityManager).unregisterNetworkCallback( any(ConnectivityManager.NetworkCallback.class)); - assertThat(mInternetDialogController.mCallback).isNull(); + assertThat(mInternetDetailsContentController.mCallback).isNull(); } @Test @@ -1132,16 +1144,16 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(SubscriptionManager.getDefaultDataSubscriptionId()) .thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID); - mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + mInternetDetailsContentController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); - assertThat(mInternetDialogController.hasActiveSubIdOnDds()).isFalse(); + assertThat(mInternetDetailsContentController.hasActiveSubIdOnDds()).isFalse(); } @Test public void hasActiveSubIdOnDds_activeDds_returnTrue() { - mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + mInternetDetailsContentController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); - assertThat(mInternetDialogController.hasActiveSubIdOnDds()).isTrue(); + assertThat(mInternetDetailsContentController.hasActiveSubIdOnDds()).isTrue(); } @Test @@ -1153,9 +1165,9 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(info.getProfileClass()).thenReturn(PROFILE_CLASS_PROVISIONING); when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info); - mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + mInternetDetailsContentController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); - assertThat(mInternetDialogController.hasActiveSubIdOnDds()).isFalse(); + assertThat(mInternetDetailsContentController.hasActiveSubIdOnDds()).isFalse(); } @Test @@ -1167,9 +1179,9 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(info.isOnlyNonTerrestrialNetwork()).thenReturn(true); when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info); - mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + mInternetDetailsContentController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); - assertFalse(mInternetDialogController.hasActiveSubIdOnDds()); + assertFalse(mInternetDetailsContentController.hasActiveSubIdOnDds()); } @Test @@ -1181,9 +1193,9 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { when(info.isOnlyNonTerrestrialNetwork()).thenReturn(false); when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info); - mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + mInternetDetailsContentController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); - assertTrue(mInternetDialogController.hasActiveSubIdOnDds()); + assertTrue(mInternetDetailsContentController.hasActiveSubIdOnDds()); } private String getResourcesString(String name) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java index 8560b67dee33..238551589361 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java @@ -1,6 +1,6 @@ package com.android.systemui.qs.tiles.dialog; -import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT; +import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT; import static com.google.common.truth.Truth.assertThat; @@ -58,7 +58,7 @@ import kotlinx.coroutines.CoroutineScope; @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @UiThreadTest -public class InternetDialogDelegateTest extends SysuiTestCase { +public class InternetDialogDelegateLegacyTest extends SysuiTestCase { private static final String MOBILE_NETWORK_TITLE = "Mobile Title"; private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary"; @@ -78,7 +78,7 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Mock private InternetAdapter mInternetAdapter; @Mock - private InternetDialogController mInternetDialogController; + private InternetDetailsContentController mInternetDetailsContentController; @Mock private KeyguardStateController mKeyguard; @Mock @@ -91,7 +91,7 @@ public class InternetDialogDelegateTest extends SysuiTestCase { private Window mWindow; private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock()); - private InternetDialogDelegate mInternetDialogDelegate; + private InternetDialogDelegateLegacy mInternetDialogDelegateLegacy; private View mDialogView; private View mSubTitle; private LinearLayout mEthernet; @@ -118,12 +118,12 @@ public class InternetDialogDelegateTest extends SysuiTestCase { when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true); when(mWifiEntries.size()).thenReturn(1); - when(mInternetDialogController.getMobileNetworkTitle(anyInt())) + when(mInternetDetailsContentController.getMobileNetworkTitle(anyInt())) .thenReturn(MOBILE_NETWORK_TITLE); - when(mInternetDialogController.getMobileNetworkSummary(anyInt())) + when(mInternetDetailsContentController.getMobileNetworkSummary(anyInt())) .thenReturn(MOBILE_NETWORK_SUMMARY); - when(mInternetDialogController.isWifiEnabled()).thenReturn(true); - when(mInternetDialogController.getActiveAutoSwitchNonDdsSubId()).thenReturn( + when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(true); + when(mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId()).thenReturn( SubscriptionManager.INVALID_SUBSCRIPTION_ID); mMockitoSession = ExtendedMockito.mockitoSession() .spyStatic(WifiEnterpriseRestrictionUtils.class) @@ -137,10 +137,10 @@ public class InternetDialogDelegateTest extends SysuiTestCase { } private void createInternetDialog() { - mInternetDialogDelegate = new InternetDialogDelegate( + mInternetDialogDelegateLegacy = new InternetDialogDelegateLegacy( mContext, mock(InternetDialogManager.class), - mInternetDialogController, + mInternetDetailsContentController, true, true, true, @@ -152,13 +152,13 @@ public class InternetDialogDelegateTest extends SysuiTestCase { mKeyguard, mSystemUIDialogFactory, new FakeShadeDialogContextInteractor(mContext)); - mInternetDialogDelegate.createDialog(); - mInternetDialogDelegate.onCreate(mSystemUIDialog, null); - mInternetDialogDelegate.mAdapter = mInternetAdapter; - mInternetDialogDelegate.mConnectedWifiEntry = mInternetWifiEntry; - mInternetDialogDelegate.mWifiEntriesCount = mWifiEntries.size(); + mInternetDialogDelegateLegacy.createDialog(); + mInternetDialogDelegateLegacy.onCreate(mSystemUIDialog, null); + mInternetDialogDelegateLegacy.mAdapter = mInternetAdapter; + mInternetDialogDelegateLegacy.mConnectedWifiEntry = mInternetWifiEntry; + mInternetDialogDelegateLegacy.mWifiEntriesCount = mWifiEntries.size(); - mDialogView = mInternetDialogDelegate.mDialogView; + mDialogView = mInternetDialogDelegateLegacy.mDialogView; mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle); mEthernet = mDialogView.requireViewById(R.id.ethernet_layout); mMobileDataLayout = mDialogView.requireViewById(R.id.mobile_network_layout); @@ -171,13 +171,13 @@ public class InternetDialogDelegateTest extends SysuiTestCase { mSeeAll = mDialogView.requireViewById(R.id.see_all_layout); mWifiScanNotify = mDialogView.requireViewById(R.id.wifi_scan_notify_layout); mAirplaneModeSummaryText = mDialogView.requireViewById(R.id.airplane_mode_summary); - mInternetDialogDelegate.onStart(mSystemUIDialog); + mInternetDialogDelegateLegacy.onStart(mSystemUIDialog); } @After public void tearDown() { - mInternetDialogDelegate.onStop(mSystemUIDialog); - mInternetDialogDelegate.dismissDialog(); + mInternetDialogDelegateLegacy.onStop(mSystemUIDialog); + mInternetDialogDelegateLegacy.dismissDialog(); mMockitoSession.finishMocking(); } @@ -189,9 +189,9 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void hideWifiViews_WifiViewsGone() { - mInternetDialogDelegate.hideWifiViews(); + mInternetDialogDelegateLegacy.hideWifiViews(); - assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isFalse(); + assertThat(mInternetDialogDelegateLegacy.mIsProgressBarVisible).isFalse(); assertThat(mWifiToggle.getVisibility()).isEqualTo(View.GONE); assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE); assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE); @@ -200,76 +200,76 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_withApmOn_internetDialogSubTitleGone() { - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE); }); } @Test public void updateDialog_withApmOff_internetDialogSubTitleVisible() { - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE); }); } @Test public void updateDialog_apmOffAndHasEthernet_showEthernet() { - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false); - when(mInternetDialogController.hasEthernet()).thenReturn(true); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false); + when(mInternetDetailsContentController.hasEthernet()).thenReturn(true); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE); }); } @Test public void updateDialog_apmOffAndNoEthernet_hideEthernet() { - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false); - when(mInternetDialogController.hasEthernet()).thenReturn(false); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false); + when(mInternetDetailsContentController.hasEthernet()).thenReturn(false); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE); }); } @Test public void updateDialog_apmOnAndHasEthernet_showEthernet() { - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); - when(mInternetDialogController.hasEthernet()).thenReturn(true); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true); + when(mInternetDetailsContentController.hasEthernet()).thenReturn(true); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE); }); } @Test public void updateDialog_apmOnAndNoEthernet_hideEthernet() { - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); - when(mInternetDialogController.hasEthernet()).thenReturn(false); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true); + when(mInternetDetailsContentController.hasEthernet()).thenReturn(false); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE); }); } @@ -277,14 +277,14 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_apmOffAndNotCarrierNetwork_mobileDataLayoutGone() { // Mobile network should be gone if the list of active subscriptionId is null. - when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false); - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false); - when(mInternetDialogController.hasActiveSubIdOnDds()).thenReturn(false); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false); + when(mInternetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(false); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE); }); } @@ -292,14 +292,14 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutVisible() { // Carrier network should be visible if airplane mode ON and Wi-Fi is ON. - when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true); - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); - when(mInternetDialogController.isWifiEnabled()).thenReturn(true); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true); + when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(true); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE); }); } @@ -307,42 +307,42 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutGone() { // Carrier network should be gone if airplane mode ON and Wi-Fi is off. - when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true); - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); - when(mInternetDialogController.isWifiEnabled()).thenReturn(false); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true); + when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE); }); } @Test public void updateDialog_apmOnAndNoCarrierNetwork_mobileDataLayoutGone() { - when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false); - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE); }); } @Test public void updateDialog_apmOnAndWifiOnHasCarrierNetwork_showAirplaneSummary() { - when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true); - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); - mInternetDialogDelegate.mConnectedWifiEntry = null; - doReturn(false).when(mInternetDialogController).activeNetworkIsCellular(); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true); + mInternetDialogDelegateLegacy.mConnectedWifiEntry = null; + doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular(); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE); }); @@ -350,87 +350,87 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_apmOffAndWifiOnHasCarrierNetwork_notShowApmSummary() { - when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true); - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false); - mInternetDialogDelegate.mConnectedWifiEntry = null; - doReturn(false).when(mInternetDialogController).activeNetworkIsCellular(); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false); + mInternetDialogDelegateLegacy.mConnectedWifiEntry = null; + doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular(); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE); }); } @Test public void updateDialog_apmOffAndHasCarrierNetwork_notShowApmSummary() { - when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true); - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE); }); } @Test public void updateDialog_apmOnAndNoCarrierNetwork_notShowApmSummary() { - when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false); - when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false); + when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE); }); } @Test public void updateDialog_mobileDataIsEnabled_checkMobileDataSwitch() { - doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds(); - when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true); - when(mInternetDialogController.isMobileDataEnabled()).thenReturn(true); + doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds(); + when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true); + when(mInternetDetailsContentController.isMobileDataEnabled()).thenReturn(true); mMobileToggleSwitch.setChecked(false); - mInternetDialogDelegate.updateDialog(true); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mMobileToggleSwitch.isChecked()).isTrue(); }); } @Test public void updateDialog_mobileDataIsNotChanged_checkMobileDataSwitch() { - doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds(); - when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true); - when(mInternetDialogController.isMobileDataEnabled()).thenReturn(false); + doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds(); + when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true); + when(mInternetDetailsContentController.isMobileDataEnabled()).thenReturn(false); mMobileToggleSwitch.setChecked(false); - mInternetDialogDelegate.updateDialog(true); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mMobileToggleSwitch.isChecked()).isFalse(); }); } @Test public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() { - when(mInternetDialogController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1); - doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds(); + when(mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1); + doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds(); // The preconditions WiFi ON and Internet WiFi are already in setUp() - doReturn(false).when(mInternetDialogController).activeNetworkIsCellular(); + doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular(); - mInternetDialogDelegate.updateDialog(true); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE); LinearLayout secondaryLayout = mDialogView.requireViewById( R.id.secondary_mobile_network_layout); @@ -441,13 +441,13 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_wifiOnAndNoConnectedWifi_hideConnectedWifi() { // The precondition WiFi ON is already in setUp() - mInternetDialogDelegate.mConnectedWifiEntry = null; - doReturn(false).when(mInternetDialogController).activeNetworkIsCellular(); - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.mConnectedWifiEntry = null; + doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular(); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE); }); } @@ -455,13 +455,13 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() { // The precondition WiFi ON is already in setUp() - mInternetDialogDelegate.mConnectedWifiEntry = null; - mInternetDialogDelegate.mWifiEntriesCount = 0; - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.mConnectedWifiEntry = null; + mInternetDialogDelegateLegacy.mWifiEntriesCount = 0; + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE); // Show a blank block to fix the dialog height even if there is no WiFi list assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE); @@ -473,13 +473,13 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() { // The precondition WiFi ON is already in setUp() - mInternetDialogDelegate.mConnectedWifiEntry = null; - mInternetDialogDelegate.mWifiEntriesCount = 1; - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.mConnectedWifiEntry = null; + mInternetDialogDelegateLegacy.mWifiEntriesCount = 1; + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE); // Show a blank block to fix the dialog height even if there is no WiFi list assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE); @@ -491,12 +491,12 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() { // The preconditions WiFi ON and WiFi entries are already in setUp() - mInternetDialogDelegate.mWifiEntriesCount = 0; - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.mWifiEntriesCount = 0; + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE); // Show a blank block to fix the dialog height even if there is no WiFi list assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE); @@ -508,14 +508,14 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_wifiOnAndHasMaxWifiList_showWifiListAndSeeAll() { // The preconditions WiFi ON and WiFi entries are already in setUp() - mInternetDialogDelegate.mConnectedWifiEntry = null; - mInternetDialogDelegate.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT; - mInternetDialogDelegate.mHasMoreWifiEntries = true; - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.mConnectedWifiEntry = null; + mInternetDialogDelegateLegacy.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT; + mInternetDialogDelegateLegacy.mHasMoreWifiEntries = true; + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE); assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE); verify(mInternetAdapter).setMaxEntriesCount(3); @@ -526,13 +526,13 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() { // The preconditions WiFi ON and WiFi entries are already in setUp() - mInternetDialogDelegate.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1; - mInternetDialogDelegate.mHasMoreWifiEntries = true; - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1; + mInternetDialogDelegateLegacy.mHasMoreWifiEntries = true; + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE); verify(mInternetAdapter).setMaxEntriesCount(2); @@ -543,13 +543,13 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_deviceLockedAndNoConnectedWifi_showWifiToggle() { // The preconditions WiFi entries are already in setUp() - when(mInternetDialogController.isDeviceLocked()).thenReturn(true); - mInternetDialogDelegate.mConnectedWifiEntry = null; - mInternetDialogDelegate.updateDialog(false); + when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(true); + mInternetDialogDelegateLegacy.mConnectedWifiEntry = null; + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { // Show WiFi Toggle without background assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mWifiToggle.getBackground()).isNull(); @@ -563,12 +563,12 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_deviceLockedAndHasConnectedWifi_showWifiToggleWithBackground() { // The preconditions WiFi ON and WiFi entries are already in setUp() - when(mInternetDialogController.isDeviceLocked()).thenReturn(true); - mInternetDialogDelegate.updateDialog(false); + when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(true); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { // Show WiFi Toggle with highlight background assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mWifiToggle.getBackground()).isNotNull(); @@ -581,14 +581,14 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_disallowChangeWifiState_disableWifiSwitch() { - mInternetDialogDelegate.dismissDialog(); + mInternetDialogDelegateLegacy.dismissDialog(); when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(false); createInternetDialog(); - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { // Disable Wi-Fi switch and show restriction message in summary. assertThat(mWifiToggleSwitch.isEnabled()).isFalse(); assertThat(mWifiToggleSummary.getVisibility()).isEqualTo(View.VISIBLE); @@ -598,14 +598,14 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_allowChangeWifiState_enableWifiSwitch() { - mInternetDialogDelegate.dismissDialog(); + mInternetDialogDelegateLegacy.dismissDialog(); when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true); createInternetDialog(); - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { // Enable Wi-Fi switch and hide restriction message in summary. assertThat(mWifiToggleSwitch.isEnabled()).isTrue(); assertThat(mWifiToggleSummary.getVisibility()).isEqualTo(View.GONE); @@ -614,22 +614,22 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_showSecondaryDataSub() { - when(mInternetDialogController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1); - doReturn(1).when(mInternetDialogController).getActiveAutoSwitchNonDdsSubId(); - doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds(); - doReturn(false).when(mInternetDialogController).isAirplaneModeEnabled(); - clearInvocations(mInternetDialogController); - mInternetDialogDelegate.updateDialog(true); + when(mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1); + doReturn(1).when(mInternetDetailsContentController).getActiveAutoSwitchNonDdsSubId(); + doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds(); + doReturn(false).when(mInternetDetailsContentController).isAirplaneModeEnabled(); + clearInvocations(mInternetDetailsContentController); + mInternetDialogDelegateLegacy.updateDialog(true); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { LinearLayout primaryLayout = mDialogView.requireViewById( R.id.mobile_network_layout); LinearLayout secondaryLayout = mDialogView.requireViewById( R.id.secondary_mobile_network_layout); - verify(mInternetDialogController).getMobileNetworkSummary(1); + verify(mInternetDetailsContentController).getMobileNetworkSummary(1); assertThat(primaryLayout.getBackground()).isNotEqualTo( secondaryLayout.getBackground()); }); @@ -639,11 +639,11 @@ public class InternetDialogDelegateTest extends SysuiTestCase { public void updateDialog_wifiOn_hideWifiScanNotify() { // The preconditions WiFi ON and WiFi entries are already in setUp() - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE); }); @@ -652,13 +652,13 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_wifiOffAndWifiScanOff_hideWifiScanNotify() { - when(mInternetDialogController.isWifiEnabled()).thenReturn(false); - when(mInternetDialogController.isWifiScanEnabled()).thenReturn(false); - mInternetDialogDelegate.updateDialog(false); + when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false); + when(mInternetDetailsContentController.isWifiScanEnabled()).thenReturn(false); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE); }); @@ -667,14 +667,14 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() { - when(mInternetDialogController.isWifiEnabled()).thenReturn(false); - when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true); - when(mInternetDialogController.isDeviceLocked()).thenReturn(true); - mInternetDialogDelegate.updateDialog(false); + when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false); + when(mInternetDetailsContentController.isWifiScanEnabled()).thenReturn(true); + when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(true); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE); }); @@ -683,14 +683,14 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() { - when(mInternetDialogController.isWifiEnabled()).thenReturn(false); - when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true); - when(mInternetDialogController.isDeviceLocked()).thenReturn(false); - mInternetDialogDelegate.updateDialog(false); + when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false); + when(mInternetDetailsContentController.isWifiScanEnabled()).thenReturn(true); + when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(false); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.VISIBLE); TextView wifiScanNotifyText = mDialogView.requireViewById( R.id.wifi_scan_notify_text); @@ -701,26 +701,26 @@ public class InternetDialogDelegateTest extends SysuiTestCase { @Test public void updateDialog_wifiIsDisabled_uncheckWifiSwitch() { - when(mInternetDialogController.isWifiEnabled()).thenReturn(false); + when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false); mWifiToggleSwitch.setChecked(true); - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mWifiToggleSwitch.isChecked()).isFalse(); }); } @Test public void updateDialog_wifiIsEnabled_checkWifiSwitch() throws Exception { - when(mInternetDialogController.isWifiEnabled()).thenReturn(true); + when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(true); mWifiToggleSwitch.setChecked(false); - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { assertThat(mWifiToggleSwitch.isChecked()).isTrue(); }); } @@ -729,26 +729,26 @@ public class InternetDialogDelegateTest extends SysuiTestCase { public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() { mSeeAll.performClick(); - verify(mInternetDialogController).launchNetworkSetting( + verify(mInternetDetailsContentController).launchNetworkSetting( mDialogView.requireViewById(R.id.see_all_layout)); } @Test public void onWifiScan_isScanTrue_setProgressBarVisibleTrue() { - mInternetDialogDelegate.mIsProgressBarVisible = false; + mInternetDialogDelegateLegacy.mIsProgressBarVisible = false; - mInternetDialogDelegate.onWifiScan(true); + mInternetDialogDelegateLegacy.onWifiScan(true); - assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isTrue(); + assertThat(mInternetDialogDelegateLegacy.mIsProgressBarVisible).isTrue(); } @Test public void onWifiScan_isScanFalse_setProgressBarVisibleFalse() { - mInternetDialogDelegate.mIsProgressBarVisible = true; + mInternetDialogDelegateLegacy.mIsProgressBarVisible = true; - mInternetDialogDelegate.onWifiScan(false); + mInternetDialogDelegateLegacy.onWifiScan(false); - assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isFalse(); + assertThat(mInternetDialogDelegateLegacy.mIsProgressBarVisible).isFalse(); } @Test @@ -757,73 +757,77 @@ public class InternetDialogDelegateTest extends SysuiTestCase { // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT. setNetworkVisible(false, false, false); - assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT); + assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()).isEqualTo( + MAX_WIFI_ENTRY_COUNT); // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count. setNetworkVisible(false, false, true); - assertThat(mInternetDialogDelegate.getWifiListMaxCount()) + assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()) .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1); // Only one of Ethernet, MobileData is displayed. // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT. setNetworkVisible(true, false, false); - assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT); + assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()).isEqualTo( + MAX_WIFI_ENTRY_COUNT); setNetworkVisible(false, true, false); - assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT); + assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()).isEqualTo( + MAX_WIFI_ENTRY_COUNT); // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count. setNetworkVisible(true, false, true); - assertThat(mInternetDialogDelegate.getWifiListMaxCount()) + assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()) .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1); setNetworkVisible(false, true, true); - assertThat(mInternetDialogDelegate.getWifiListMaxCount()) + assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()) .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1); // Both of Ethernet, MobileData, ConnectedWiFi is displayed. // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1. setNetworkVisible(true, true, false); - assertThat(mInternetDialogDelegate.getWifiListMaxCount()) + assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()) .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1); // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count. setNetworkVisible(true, true, true); - assertThat(mInternetDialogDelegate.getWifiListMaxCount()) + assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()) .isEqualTo(MAX_WIFI_ENTRY_COUNT - 2); } @Test public void updateDialog_shareWifiIntentNull_hideButton() { - when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any())) + when(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(any())) .thenReturn(null); - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { - assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility()).isEqualTo( - View.GONE); + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { + assertThat( + mInternetDialogDelegateLegacy.mShareWifiButton.getVisibility()) + .isEqualTo(View.GONE); }); } @Test public void updateDialog_shareWifiShareable_showButton() { - when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any())) + when(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(any())) .thenReturn(new Intent()); - mInternetDialogDelegate.updateDialog(false); + mInternetDialogDelegateLegacy.updateDialog(false); mBgExecutor.runAllReady(); - mInternetDialogDelegate.mDataInternetContent.observe( - mInternetDialogDelegate.mLifecycleOwner, i -> { - assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility()) + mInternetDialogDelegateLegacy.mDataInternetContent.observe( + mInternetDialogDelegateLegacy.mLifecycleOwner, i -> { + assertThat(mInternetDialogDelegateLegacy.mShareWifiButton.getVisibility()) .isEqualTo(View.VISIBLE); }); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt index 2d35ea5d4e95..61943f2283e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt @@ -65,7 +65,7 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor -import com.android.systemui.statusbar.notification.headsup.headsUpManager +import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.notificationLockscreenUserManager @@ -128,7 +128,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { private val shadeController = kosmos.shadeControllerSceneImpl private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager private val statusBarStateController = kosmos.statusBarStateController - private val headsUpManager = kosmos.headsUpManager + private val headsUpManager = kosmos.mockHeadsUpManager private val activityStarter = kosmos.activityStarter private val userManager = kosmos.userManager private val activeNotificationsInteractor = kosmos.activeNotificationsInteractor diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 411c81d13e0b..1fcf02d417e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -36,6 +36,8 @@ import static com.android.systemui.statusbar.phone.CentralSurfaces.MSG_DISMISS_K import static com.google.common.truth.Truth.assertThat; +import static kotlinx.coroutines.flow.FlowKt.flowOf; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -54,8 +56,6 @@ import static org.mockito.Mockito.when; import static java.util.Collections.emptySet; -import static kotlinx.coroutines.flow.FlowKt.flowOf; - import android.app.ActivityManager; import android.app.IWallpaperManager; import android.app.NotificationManager; @@ -132,6 +132,7 @@ import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.notetask.NoteTaskController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -175,6 +176,7 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.core.StatusBarConnectedDisplays; import com.android.systemui.statusbar.core.StatusBarInitializerImpl; import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationActivityStarter; @@ -216,6 +218,10 @@ import com.android.systemui.volume.VolumeComponent; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.StartingSurface; +import dagger.Lazy; + +import kotlinx.coroutines.test.TestScope; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -232,9 +238,6 @@ import java.util.Set; import javax.inject.Provider; -import dagger.Lazy; -import kotlinx.coroutines.test.TestScope; - @SmallTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) @@ -536,6 +539,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { new StatusBarInitializerImpl( mStatusBarWindowController, mStatusBarModePerDisplayRepository, + mock(StatusBarConfigurationController.class), + mock(DarkIconDispatcher.class), mCollapsedStatusBarFragmentProvider, mock(StatusBarRootFactory.class), mock(HomeStatusBarComponent.Factory.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 0b4436755fa5..3a99328fa8ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -61,6 +62,9 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.core.StatusBarRootModernization; +import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder; @@ -75,6 +79,8 @@ import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatu import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewModel; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.StatusBarOperatorNameViewModel; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; import com.android.systemui.util.CarrierConfigTracker; @@ -134,6 +140,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private StatusBarWindowStateController mStatusBarWindowStateController; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock private StatusBarWindowControllerStore mStatusBarWindowControllerStore; + @Mock private StatusBarWindowController mStatusBarWindowController; + @Mock private StatusBarConfigurationControllerStore mStatusBarConfigurationControllerStore; + @Mock private StatusBarConfigurationController mStatusBarConfigurationController; + @Mock private DarkIconDispatcherStore mDarkIconDispatcherStore; + @Mock private DarkIconDispatcher mDarkIconDispatcher; @Rule public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this); @@ -145,6 +157,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Before public void setup() { + when(mStatusBarWindowControllerStore.forDisplay(anyInt())) + .thenReturn(mStatusBarWindowController); + when(mStatusBarConfigurationControllerStore.forDisplay(anyInt())) + .thenReturn(mStatusBarConfigurationController); + when(mDarkIconDispatcherStore.forDisplay(anyInt())).thenReturn(mDarkIconDispatcher); + injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); mDependency.injectMockDependency(DarkIconDispatcher.class); @@ -1276,11 +1294,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mDumpManager, mStatusBarWindowStateController, mKeyguardUpdateMonitor, - mock(DemoModeController.class)); + mock(DemoModeController.class), + mStatusBarWindowControllerStore, + mStatusBarConfigurationControllerStore, + mDarkIconDispatcherStore); } private void setUpDaggerComponent() { - when(mStatusBarFragmentComponentFactory.create(any())) + when(mStatusBarFragmentComponentFactory.create(any(), any(), any(), any())) .thenReturn(mHomeStatusBarComponent); when(mHomeStatusBarComponent.getHeadsUpAppearanceController()) .thenReturn(mHeadsUpAppearanceController); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java index b8be6aa50015..64d89c5ac8ba 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java @@ -81,6 +81,14 @@ public class SysuiTestableContext extends TestableContext { return super.getDisplay(); } + @Override + public int getDisplayId() { + if (mCustomDisplay != null) { + return mCustomDisplay.getDisplayId(); + } + return super.getDisplayId(); + } + public SysuiTestableContext createDefaultDisplayContext() { Display display = getBaseContext().getSystemService(DisplayManager.class).getDisplays()[0]; return (SysuiTestableContext) createDisplayContext(display); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt index 50a19a9bc68a..fb2e2a3b76a9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.core +import com.android.systemui.plugins.DarkIconDispatcher +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository import com.android.systemui.statusbar.window.StatusBarWindowController @@ -24,5 +26,7 @@ class FakeStatusBarInitializerFactory() : StatusBarInitializer.Factory { override fun create( statusBarWindowController: StatusBarWindowController, statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository, + statusBarConfigurationController: StatusBarConfigurationController, + darkIconDispatcher: DarkIconDispatcher, ): StatusBarInitializer = FakeStatusBarInitializer() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt index 6e990277df6b..b8dafb23b206 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt @@ -19,7 +19,9 @@ package com.android.systemui.statusbar.core import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.statusbar.data.repository.darkIconDispatcherStore import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository +import com.android.systemui.statusbar.data.repository.statusBarConfigurationControllerStore import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore val Kosmos.fakeStatusBarInitializer by Kosmos.Fixture { FakeStatusBarInitializer() } @@ -39,6 +41,8 @@ val Kosmos.multiDisplayStatusBarInitializerStore by fakeStatusBarInitializerFactory, fakeStatusBarWindowControllerStore, fakeStatusBarModeRepository, + statusBarConfigurationControllerStore, + darkIconDispatcherStore, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerKosmos.kt new file mode 100644 index 000000000000..2a2f5f974a19 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerKosmos.kt @@ -0,0 +1,29 @@ +/* + * 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.headsup + +import com.android.internal.logging.uiEventLoggerFake +import com.android.systemui.dump.dumpManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.util.mockito.mock + +var Kosmos.mockAvalancheController by Fixture { mock<AvalancheController>() } + +val Kosmos.avalancheController by Fixture { + AvalancheController(dumpManager, uiEventLoggerFake, headsUpManagerLogger, bgHandler = mock()) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt index de9485d48383..dfc8a68968cb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt @@ -16,8 +16,44 @@ package com.android.systemui.statusbar.notification.headsup +import android.content.applicationContext +import android.view.accessibility.accessibilityManagerWrapper +import com.android.internal.logging.uiEventLoggerFake +import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.statusbar.statusBarStateController +import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager +import com.android.systemui.statusbar.phone.keyguardBypassController +import com.android.systemui.statusbar.policy.configurationController +import com.android.systemui.util.concurrency.mockExecutorHandler +import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.util.mockito.mock +import com.android.systemui.util.settings.fakeGlobalSettings +import com.android.systemui.util.time.fakeSystemClock -var Kosmos.headsUpManager by Fixture { mock<HeadsUpManager>() } +var Kosmos.mockHeadsUpManager by Fixture { mock<HeadsUpManager>() } + +var Kosmos.headsUpManager: HeadsUpManager by Fixture { + HeadsUpManagerImpl( + applicationContext, + headsUpManagerLogger, + statusBarStateController, + keyguardBypassController, + mock<GroupMembershipManager>(), + visualStabilityProvider, + configurationController, + mockExecutorHandler(fakeExecutor), + fakeGlobalSettings, + fakeSystemClock, + fakeExecutor, + accessibilityManagerWrapper, + uiEventLoggerFake, + JavaAdapter(testScope.backgroundScope), + shadeInteractor, + avalancheController, + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLoggerKosmos.kt new file mode 100644 index 000000000000..d595fa6f05d2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLoggerKosmos.kt @@ -0,0 +1,28 @@ +/* + * 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.headsup + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.log.logcatLogBuffer +import org.mockito.kotlin.mock + +val Kosmos.mockHeadsUpManagerLogger by Fixture { mock<HeadsUpManagerLogger>() } + +val Kosmos.headsUpManagerLogger by Fixture { + HeadsUpManagerLogger(logcatLogBuffer("HeadsUpManagerLogger")) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt index ec54c33f1156..cc3f21b2277a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt @@ -22,14 +22,14 @@ import com.android.systemui.statusbar.notification.collection.coordinator.visual import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl import com.android.systemui.statusbar.notification.collection.notifCollection import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider -import com.android.systemui.statusbar.notification.headsup.headsUpManager +import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager var Kosmos.onUserInteractionCallback: OnUserInteractionCallback by Kosmos.Fixture { OnUserInteractionCallbackImpl( notificationVisibilityProvider, notifCollection, - headsUpManager, + mockHeadsUpManager, statusBarStateController, visualStabilityCoordinator, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt index 383e31d99314..65f4ec1c437c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt @@ -21,6 +21,7 @@ import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.shade.transition.largeScreenShadeInterpolator +import com.android.systemui.statusbar.notification.headsup.mockAvalancheController import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,6 +34,6 @@ val Kosmos.ambientState by Fixture { /*bypassController=*/ stackScrollAlgorithmBypassController, /*statusBarKeyguardViewManager=*/ statusBarKeyguardViewManager, /*largeScreenShadeInterpolator=*/ largeScreenShadeInterpolator, - /*avalancheController=*/ avalancheController, + /*avalancheController=*/ mockAvalancheController, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt index a5c4bfd61945..67343c95f6e1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.stack import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.statusbar.notification.headsup.AvalancheController import com.android.systemui.util.mockito.mock var Kosmos.stackScrollAlgorithmSectionProvider by Fixture { @@ -28,5 +27,3 @@ var Kosmos.stackScrollAlgorithmSectionProvider by Fixture { var Kosmos.stackScrollAlgorithmBypassController by Fixture { mock<StackScrollAlgorithm.BypassController>() } - -var Kosmos.avalancheController by Fixture { mock<AvalancheController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt index e972c2c77902..64f16dac6242 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt @@ -25,14 +25,14 @@ import com.android.systemui.scene.data.repository.windowRootViewVisibilityReposi import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor -import com.android.systemui.statusbar.notification.headsup.headsUpManager +import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager val Kosmos.windowRootViewVisibilityInteractor by Fixture { WindowRootViewVisibilityInteractor( scope = applicationCoroutineScope, windowRootViewVisibilityRepository = windowRootViewVisibilityRepository, keyguardRepository = keyguardRepository, - headsUpManager = headsUpManager, + headsUpManager = mockHeadsUpManager, powerInteractor = powerInteractor, activeNotificationsInteractor = activeNotificationsInteractor, ) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt index f86824a1af6d..d0bf584d9a62 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt @@ -26,7 +26,7 @@ import com.android.systemui.keyguard.wakefulnessLifecycle import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.shade.domain.interactor.shadeLockscreenInteractor -import com.android.systemui.statusbar.notification.headsup.headsUpManager +import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager import com.android.systemui.statusbar.notificationShadeWindowController import com.android.systemui.statusbar.policy.batteryController import com.android.systemui.statusbar.policy.deviceProvisionedController @@ -42,7 +42,7 @@ val Kosmos.dozeServiceHost: DozeServiceHost by wakefulnessLifecycle, statusBarStateController, deviceProvisionedController, - headsUpManager, + mockHeadsUpManager, batteryController, scrimController, { biometricUnlockController }, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt index 5b7f23b0cff2..9adaeff9c552 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt @@ -24,7 +24,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor -import com.android.systemui.statusbar.notification.headsup.headsUpManager +import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager import com.android.systemui.statusbar.notificationShadeWindowController import com.android.systemui.statusbar.policy.configurationController import com.android.systemui.util.kotlin.JavaAdapter @@ -36,7 +36,7 @@ var Kosmos.shadeTouchableRegionManager by applicationContext, notificationShadeWindowController, configurationController, - headsUpManager, + mockHeadsUpManager, shadeInteractor, { sceneInteractor }, JavaAdapter(testScope.backgroundScope), diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt index 6083414240a6..7743a1c7e0cd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt @@ -36,7 +36,7 @@ import com.android.systemui.shade.shadeController import com.android.systemui.statusbar.commandQueue import com.android.systemui.statusbar.notification.collection.provider.launchFullScreenIntentProvider import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider -import com.android.systemui.statusbar.notification.headsup.headsUpManager +import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager import com.android.systemui.statusbar.notification.notificationTransitionAnimatorControllerProvider import com.android.systemui.statusbar.notification.row.onUserInteractionCallback import com.android.systemui.statusbar.notificationClickNotifier @@ -58,7 +58,7 @@ val Kosmos.statusBarNotificationActivityStarter by fakeExecutorHandler, fakeExecutor, notificationVisibilityProvider, - headsUpManager, + mockHeadsUpManager, activityStarter, commandQueue, notificationClickNotifier, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 50b6990c0c1c..c9f06ac87269 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -10837,8 +10837,12 @@ public class ActivityManagerService extends IActivityManager.Stub opti++; } synchronized (this) { + // TODO: b/361161826 - Always pass in the dumpAll and let + // BroadcastController decide how to treat it. + final boolean requestDumpAll = "filter".equals(dumpPackage) + ? dumpAll : true; mBroadcastController.dumpBroadcastsLocked(fd, pw, args, opti, - /* dumpAll= */ true, dumpPackage); + requestDumpAll, dumpPackage); } } else if ("broadcast-stats".equals(cmd)) { if (opti < args.length) { diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java index aa06b7ecf76c..bfacfbba4e22 100644 --- a/services/core/java/com/android/server/am/BroadcastController.java +++ b/services/core/java/com/android/server/am/BroadcastController.java @@ -47,6 +47,8 @@ import static com.android.server.am.ActivityManagerService.UPDATE_HTTP_PROXY_MSG import static com.android.server.am.ActivityManagerService.UPDATE_TIME_PREFERENCE_MSG; import static com.android.server.am.ActivityManagerService.UPDATE_TIME_ZONE; import static com.android.server.am.ActivityManagerService.checkComponentPermission; +import static com.android.server.am.BroadcastRecord.debugLog; +import static com.android.server.am.BroadcastRecord.intentToString; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1017,6 +1019,13 @@ class BroadcastController { android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS, callingPid, callingUid, "recordResponseEventWhileInBackground"); } + + if (brOptions.isDebugLogEnabled()) { + if (!isShellOrRoot(callingUid) + && (callerApp == null || !callerApp.hasActiveInstrumentation())) { + brOptions.setDebugLogEnabled(false); + } + } } // Verify that protected broadcasts are only being sent by system code, @@ -1622,6 +1631,10 @@ class BroadcastController { } } while (ir < NR) { + // Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS + if (callerInstantApp) { + intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); + } if (receivers == null) { receivers = new ArrayList(); } @@ -1647,7 +1660,9 @@ class BroadcastController { callerAppProcessState, mService.mPlatformCompat); broadcastSentEventRecord.setBroadcastRecord(r); - if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r); + if (DEBUG_BROADCAST || r.debugLog()) { + Slog.v(TAG_BROADCAST, "Enqueueing broadcast " + r); + } queue.enqueueBroadcastLocked(r); } else { // There was nobody interested in the broadcast, but we still want to record @@ -1657,11 +1672,19 @@ class BroadcastController { // This was an implicit broadcast... let's record it for posterity. addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0); } + if (DEBUG_BROADCAST || debugLog(brOptions)) { + Slog.v(TAG_BROADCAST, "Skipping broadcast " + intentToString(intent) + + " due to no receivers"); + } } return ActivityManager.BROADCAST_SUCCESS; } + private boolean isShellOrRoot(int uid) { + return uid == SHELL_UID || uid == ROOT_UID; + } + @GuardedBy("mService") private void scheduleCanceledResultTo(ProcessRecord resultToApp, IIntentReceiver resultTo, Intent intent, int userId, BroadcastOptions options, int callingUid, @@ -2178,6 +2201,8 @@ class BroadcastController { boolean printedAnything = false; boolean onlyReceivers = false; int filteredUid = Process.INVALID_UID; + boolean onlyFilter = false; + String dumpIntentAction = null; if ("history".equals(dumpPackage)) { if (opti < args.length && "-s".equals(args[opti])) { @@ -2185,8 +2210,7 @@ class BroadcastController { } onlyHistory = true; dumpPackage = null; - } - if ("receivers".equals(dumpPackage)) { + } else if ("receivers".equals(dumpPackage)) { onlyReceivers = true; dumpPackage = null; if (opti + 2 <= args.length) { @@ -2205,7 +2229,23 @@ class BroadcastController { } } } + } else if ("filter".equals(dumpPackage)) { + onlyFilter = true; + dumpPackage = null; + if (opti + 2 <= args.length) { + if ("--action".equals(args[opti++])) { + dumpIntentAction = args[opti++]; + if (dumpIntentAction == null) { + pw.printf("Missing argument for --action option\n"); + return; + } + } else { + pw.printf("Unknown argument: %s\n", args[opti]); + return; + } + } } + if (DEBUG_BROADCAST) { Slogf.d(TAG_BROADCAST, "dumpBroadcastsLocked(): dumpPackage=%s, onlyHistory=%b, " + "onlyReceivers=%b, filteredUid=%d", dumpPackage, onlyHistory, @@ -2213,7 +2253,7 @@ class BroadcastController { } pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)"); - if (!onlyHistory && dumpAll) { + if (!onlyHistory && !onlyFilter && dumpAll) { if (mRegisteredReceivers.size() > 0) { boolean printed = false; Iterator it = mRegisteredReceivers.values().iterator(); @@ -2257,14 +2297,14 @@ class BroadcastController { if (!onlyReceivers) { needSep = mBroadcastQueue.dumpLocked(fd, pw, args, opti, - dumpConstants, dumpHistory, dumpAll, dumpPackage, needSep); + dumpConstants, dumpHistory, dumpAll, dumpPackage, dumpIntentAction, needSep); printedAnything |= needSep; } needSep = true; synchronized (mStickyBroadcasts) { - if (!onlyHistory && !onlyReceivers && mStickyBroadcasts != null + if (!onlyHistory && !onlyReceivers && !onlyFilter && mStickyBroadcasts != null && dumpPackage == null) { for (int user = 0; user < mStickyBroadcasts.size(); user++) { if (needSep) { @@ -2312,13 +2352,12 @@ class BroadcastController { } } - if (!onlyHistory && !onlyReceivers && dumpAll) { + if (!onlyHistory && !onlyReceivers && !onlyFilter && dumpAll) { pw.println(); - pw.println(" Queue " + mBroadcastQueue.toString() + ": " + pw.println(" Queue " + mBroadcastQueue + ": " + mBroadcastQueue.describeStateLocked()); pw.println(" mHandler:"); mService.mHandler.dump(new PrintWriterPrinter(pw), " "); - needSep = true; printedAnything = true; } diff --git a/services/core/java/com/android/server/am/BroadcastHistory.java b/services/core/java/com/android/server/am/BroadcastHistory.java index d6e3d4336e8e..6ddf60b369e6 100644 --- a/services/core/java/com/android/server/am/BroadcastHistory.java +++ b/services/core/java/com/android/server/am/BroadcastHistory.java @@ -29,6 +29,7 @@ import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.Objects; /** * Collection of recent historical broadcasts that are available to be dumped @@ -163,10 +164,11 @@ public class BroadcastHistory { @NeverCompile public boolean dumpLocked(@NonNull PrintWriter pw, @Nullable String dumpPackage, - @NonNull String queueName, @NonNull SimpleDateFormat sdf, - boolean dumpAll, boolean needSep) { - dumpBroadcastList(pw, sdf, mFrozenBroadcasts, "Frozen"); - dumpBroadcastList(pw, sdf, mPendingBroadcasts, "Pending"); + @Nullable String dumpIntentAction, @NonNull String queueName, + @NonNull SimpleDateFormat sdf, boolean dumpAll) { + boolean needSep = true; + dumpBroadcastList(pw, sdf, mFrozenBroadcasts, dumpIntentAction, dumpAll, "Frozen"); + dumpBroadcastList(pw, sdf, mPendingBroadcasts, dumpIntentAction, dumpAll, "Pending"); int i; boolean printed = false; @@ -187,6 +189,10 @@ public class BroadcastHistory { if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) { continue; } + if (dumpIntentAction != null && !Objects.equals(dumpIntentAction, + r.intent.getAction())) { + continue; + } if (!printed) { if (needSep) { pw.println(); @@ -195,9 +201,16 @@ public class BroadcastHistory { pw.println(" Historical broadcasts [" + queueName + "]:"); printed = true; } - if (dumpAll) { + if (dumpIntentAction != null) { + pw.print(" Historical Broadcast " + queueName + " #"); + pw.print(i); pw.println(":"); + r.dump(pw, " ", sdf); + if (!dumpAll) { + break; + } + } else if (dumpAll) { pw.print(" Historical Broadcast " + queueName + " #"); - pw.print(i); pw.println(":"); + pw.print(i); pw.println(":"); r.dump(pw, " ", sdf); } else { pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r); @@ -213,7 +226,7 @@ public class BroadcastHistory { } } while (ringIndex != lastIndex); - if (dumpPackage == null) { + if (dumpPackage == null && dumpIntentAction == null) { lastIndex = ringIndex = mSummaryHistoryNext; if (dumpAll) { printed = false; @@ -276,15 +289,28 @@ public class BroadcastHistory { } private void dumpBroadcastList(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf, - @NonNull ArrayList<BroadcastRecord> broadcasts, @NonNull String flavor) { + @NonNull ArrayList<BroadcastRecord> broadcasts, @Nullable String dumpIntentAction, + boolean dumpAll, @NonNull String flavor) { pw.print(" "); pw.print(flavor); pw.println(" broadcasts:"); if (broadcasts.isEmpty()) { pw.println(" <empty>"); } else { + boolean printedAnything = false; for (int idx = broadcasts.size() - 1; idx >= 0; --idx) { final BroadcastRecord r = broadcasts.get(idx); + if (dumpIntentAction != null && !Objects.equals(dumpIntentAction, + r.intent.getAction())) { + continue; + } pw.print(flavor); pw.print(" broadcast #"); pw.print(idx); pw.println(":"); r.dump(pw, " ", sdf); + printedAnything = true; + if (dumpIntentAction != null && !dumpAll) { + break; + } + } + if (!printedAnything) { + pw.println(" <no-matches>"); } } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 6386af673e8b..a7d74a9fe54a 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -264,7 +264,8 @@ public abstract class BroadcastQueue { @GuardedBy("mService") public abstract boolean dumpLocked(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args, int opti, boolean dumpConstants, boolean dumpHistory, - boolean dumpAll, @Nullable String dumpPackage, boolean needSep); + boolean dumpAll, @Nullable String dumpPackage, @Nullable String dumpIntentAction, + boolean needSep); /** * Execute {@link #dumpLocked} and store the output into @@ -276,7 +277,7 @@ public abstract class BroadcastQueue { PrintWriter pw = new PrintWriter(out)) { pw.print("Message: "); pw.println(msg); - dumpLocked(fd, pw, null, 0, false, false, false, null, false); + dumpLocked(fd, pw, null, 0, false, false, false, null, null, false); pw.flush(); } }, DropBoxManager.IS_TEXT); diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index 9e4666cca140..b270513e5563 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -798,7 +798,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { mService.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncImmediateLSP(r.callerApp); return; } - if (DEBUG_BROADCAST) logv("Enqueuing " + r + " for " + r.receivers.size() + " receivers"); + if (DEBUG_BROADCAST || r.debugLog()) { + logv("Enqueuing " + r + " for " + r.receivers.size() + " receivers"); + } final int cookie = traceBegin("enqueueBroadcast"); r.applySingletonPolicy(mService); @@ -1019,7 +1021,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0; long startTimeNs = SystemClock.uptimeNanos(); - if (DEBUG_BROADCAST) logv("Scheduling " + r + " to cold " + queue); + if (DEBUG_BROADCAST || r.debugLog()) { + logv("Scheduling " + r + " to cold " + queue); + } queue.app = mService.startProcessLocked(queue.processName, info, true, intentFlags, hostingRecord, zygotePolicyFlags, allowWhileBooting, false); if (queue.app == null) { @@ -1176,7 +1180,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } } - if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app); + if (DEBUG_BROADCAST || r.debugLog()) { + logv("Scheduling " + r + " to warm " + app); + } setDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED, "scheduleReceiverWarmLocked"); @@ -1562,12 +1568,17 @@ class BroadcastQueueModernImpl extends BroadcastQueue { // bookkeeping to update for ordered broadcasts if (!isDeliveryStateTerminal(oldDeliveryState) && isDeliveryStateTerminal(newDeliveryState)) { - if (DEBUG_BROADCAST - && newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) { - logw("Delivery state of " + r + " to " + receiver + if ((DEBUG_BROADCAST && newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) + || r.debugLog()) { + final String msg = "Delivery state of " + r + " to " + receiver + " via " + app + " changed from " + deliveryStateToString(oldDeliveryState) + " to " - + deliveryStateToString(newDeliveryState) + " because " + reason); + + deliveryStateToString(newDeliveryState) + " because " + reason; + if (newDeliveryState == BroadcastRecord.DELIVERY_DELIVERED) { + logv(msg); + } else { + logw(msg); + } } notifyFinishReceiver(queue, app, r, index, receiver); @@ -2417,12 +2428,33 @@ class BroadcastQueueModernImpl extends BroadcastQueue { @GuardedBy("mService") public boolean dumpLocked(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args, int opti, boolean dumpConstants, boolean dumpHistory, - boolean dumpAll, @Nullable String dumpPackage, boolean needSep) { + boolean dumpAll, @Nullable String dumpPackage, @Nullable String dumpIntentAction, + boolean needSep) { final long now = SystemClock.uptimeMillis(); final IndentingPrintWriter ipw = new IndentingPrintWriter(pw); ipw.increaseIndent(); ipw.println(); + if (dumpIntentAction == null) { + dumpProcessQueues(ipw, now); + dumpBroadcastsWithIgnoredPolicies(ipw); + dumpForegroundUids(ipw); + + if (dumpConstants) { + mFgConstants.dump(ipw); + mBgConstants.dump(ipw); + } + } + + if (dumpHistory) { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + needSep = mHistory.dumpLocked(ipw, dumpPackage, dumpIntentAction, mQueueName, + sdf, dumpAll); + } + return needSep; + } + + private void dumpProcessQueues(@NonNull IndentingPrintWriter ipw, @UptimeMillisLong long now) { ipw.println("📋 Per-process queues:"); ipw.increaseIndent(); for (int i = 0; i < mProcessQueues.size(); i++) { @@ -2470,28 +2502,21 @@ class BroadcastQueueModernImpl extends BroadcastQueue { } ipw.decreaseIndent(); ipw.println(); + } + private void dumpBroadcastsWithIgnoredPolicies(@NonNull IndentingPrintWriter ipw) { ipw.println("Broadcasts with ignored delivery group policies:"); ipw.increaseIndent(); mService.dumpDeliveryGroupPolicyIgnoredActions(ipw); ipw.decreaseIndent(); ipw.println(); + } + private void dumpForegroundUids(@NonNull IndentingPrintWriter ipw) { ipw.println("Foreground UIDs:"); ipw.increaseIndent(); ipw.println(mUidForeground); ipw.decreaseIndent(); ipw.println(); - - if (dumpConstants) { - mFgConstants.dump(ipw); - mBgConstants.dump(ipw); - } - - if (dumpHistory) { - final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - needSep = mHistory.dumpLocked(ipw, dumpPackage, mQueueName, sdf, dumpAll, needSep); - } - return needSep; } } diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 8d0805d3fa13..c1b0a76b24af 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -1285,31 +1285,43 @@ final class BroadcastRecord extends Binder { } @Override + @NonNull public String toString() { if (mCachedToString == null) { - String label = intent.getAction(); - if (label == null) { - label = intent.toString(); - } mCachedToString = "BroadcastRecord{" + toShortString() + "}"; } return mCachedToString; } + @NonNull public String toShortString() { if (mCachedToShortString == null) { - String label = intent.getAction(); - if (label == null) { - label = intent.toString(); - } + final String label = intentToString(intent); mCachedToShortString = Integer.toHexString(System.identityHashCode(this)) + " " + label + "/u" + userId; } return mCachedToShortString; } + @NonNull + public static String intentToString(@NonNull Intent intent) { + String label = intent.getAction(); + if (label == null) { + label = intent.toString(); + } + return label; + } + + public boolean debugLog() { + return debugLog(options); + } + + public static boolean debugLog(@Nullable BroadcastOptions options) { + return options != null && options.isDebugLogEnabled(); + } + @NeverCompile - public void dumpDebug(ProtoOutputStream proto, long fieldId) { + public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) { long token = proto.start(fieldId); proto.write(BroadcastRecordProto.USER_ID, userId); proto.write(BroadcastRecordProto.INTENT_ACTION, intent.getAction()); diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 49149e1fa415..1503d889c298 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1005,6 +1005,11 @@ class ProcessRecord implements WindowProcessListener { } @GuardedBy(anyOf = {"mService", "mProcLock"}) + boolean hasActiveInstrumentation() { + return mInstr != null; + } + + @GuardedBy(anyOf = {"mService", "mProcLock"}) boolean isKilledByAm() { return mKilledByAm; } diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java index 56cb6f49f6c4..febf24edc294 100644 --- a/services/core/java/com/android/server/input/InputSettingsObserver.java +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -71,6 +71,9 @@ class InputSettingsObserver extends ContentObserver { (reason) -> updateMouseSwapPrimaryButton()), Map.entry(Settings.System.getUriFor(Settings.System.MOUSE_SCROLLING_ACCELERATION), (reason) -> updateMouseScrollingAcceleration()), + Map.entry(Settings.System.getUriFor( + Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED), + (reason) -> updateMouseAccelerationEnabled()), Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_POINTER_SPEED), (reason) -> updateTouchpadPointerSpeed()), Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING), @@ -191,6 +194,11 @@ class InputSettingsObserver extends ContentObserver { InputSettings.isMouseScrollingAccelerationEnabled(mContext)); } + private void updateMouseAccelerationEnabled() { + mNative.setMouseAccelerationEnabled( + InputSettings.isMousePointerAccelerationEnabled(mContext)); + } + private void updateTouchpadPointerSpeed() { mNative.setTouchpadPointerSpeed( constrainPointerSpeedValue(InputSettings.getTouchpadPointerSpeed(mContext))); diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index ab5a680867e9..7dbde64a6412 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -138,6 +138,8 @@ interface NativeInputManagerService { void setMouseSwapPrimaryButtonEnabled(boolean enabled); + void setMouseAccelerationEnabled(boolean enabled); + void setTouchpadPointerSpeed(int speed); void setTouchpadNaturalScrollingEnabled(boolean enabled); @@ -429,6 +431,9 @@ interface NativeInputManagerService { public native void setMouseSwapPrimaryButtonEnabled(boolean enabled); @Override + public native void setMouseAccelerationEnabled(boolean enabled); + + @Override public native void setTouchpadPointerSpeed(int speed); @Override diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7375a68c547b..f50e8aa7eb7b 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3082,16 +3082,42 @@ public class NotificationManagerService extends SystemService { private void sendRegisteredOnlyBroadcast(Intent baseIntent) { int[] userIds = mUmInternal.getProfileIds(mAmi.getCurrentUserId(), true); - Intent intent = new Intent(baseIntent).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - for (int userId : userIds) { - getContext().sendBroadcastAsUser(intent, UserHandle.of(userId), null); - } - // explicitly send the broadcast to all DND packages, even if they aren't currently running - for (int userId : userIds) { - for (String pkg : mConditionProviders.getAllowedPackages(userId)) { - Intent pkgIntent = new Intent(baseIntent).setPackage(pkg).setFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - getContext().sendBroadcastAsUser(pkgIntent, UserHandle.of(userId)); + if (Flags.nmBinderPerfReduceZenBroadcasts()) { + for (int userId : userIds) { + Context userContext = getContext().createContextAsUser(UserHandle.of(userId), 0); + String[] dndPackages = mConditionProviders.getAllowedPackages(userId) + .toArray(new String[0]); + + // We send the broadcast to all DND packages in the second step, so leave them out + // of this first broadcast for *running* receivers. That ensures each package only + // receives it once. + Intent registeredOnlyIntent = new Intent(baseIntent) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + userContext.sendBroadcastMultiplePermissions(registeredOnlyIntent, + /* receiverPermissions= */ new String[0], + /* excludedPermissions= */ new String[0], + /* excludedPackages= */ dndPackages); + + for (String pkg : dndPackages) { + Intent pkgIntent = new Intent(baseIntent).setPackage(pkg) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + userContext.sendBroadcast(pkgIntent); + } + } + } else { + Intent intent = new Intent(baseIntent).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + for (int userId : userIds) { + getContext().sendBroadcastAsUser(intent, UserHandle.of(userId), null); + } + + // explicitly send the broadcast to all DND packages, even if they aren't currently + // running + for (int userId : userIds) { + for (String pkg : mConditionProviders.getAllowedPackages(userId)) { + Intent pkgIntent = new Intent(baseIntent).setPackage(pkg).setFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + getContext().sendBroadcastAsUser(pkgIntent, UserHandle.of(userId)); + } } } } @@ -4276,16 +4302,16 @@ public class NotificationManagerService extends SystemService { @Override @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) - public @NonNull String[] getTypeAdjustmentDeniedPackages() { + public @NonNull int[] getAllowedAdjustmentKeyTypesForPackage(String pkg) { checkCallerIsSystemOrSystemUiOrShell(); - return mAssistants.getTypeAdjustmentDeniedPackages(); + return mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg); } - @Override @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) - public void setTypeAdjustmentForPackageState(String pkg, boolean enabled) { + public void setAssistantAdjustmentKeyTypeStateForPackage(String pkg, int type, + boolean enabled) { checkCallerIsSystemOrSystemUiOrShell(); - mAssistants.setTypeAdjustmentForPackageState(pkg, enabled); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, type, enabled); handleSavePolicyFile(); } @@ -7083,7 +7109,7 @@ public class NotificationManagerService extends SystemService { toRemove.add(potentialKey); } else if (notificationClassificationUi() && !mAssistants.isTypeAdjustmentAllowedForPackage( - r.getSbn().getPackageName())) { + r.getSbn().getPackageName(), adjustments.getInt(KEY_TYPE))) { toRemove.add(potentialKey); } } @@ -11740,7 +11766,11 @@ public class NotificationManagerService extends SystemService { private static final String ATT_DENIED = "denied_adjustments"; private static final String ATT_ENABLED_TYPES = "enabled_key_types"; private static final String ATT_NAS_UNSUPPORTED = "unsupported_adjustments"; - private static final String ATT_TYPES_DENIED_APPS = "types_denied_apps"; + // Encapsulates a list of packages and the bundle types enabled for each package. + private static final String TAG_TYPES_ENABLED_FOR_APPS = "types_enabled_for_apps"; + // Encapsulates the bundle types enabled for a package. + private static final String ATT_APP_ENABLED_TYPES = "app_enabled_types"; + private static final String ATT_PACKAGE = "package"; private final Object mLock = new Object(); @@ -11756,8 +11786,14 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mLock") private Map<Integer, HashSet<String>> mNasUnsupported = new ArrayMap<>(); + // Types of classifications (aka bundles) enabled/allowed for this package. + // If the set is NULL (or package is not in the list), default classification allow list + // (the global one) should be used. + // If the set is empty, that indicates the package explicitly has all classifications + // disallowed. @GuardedBy("mLock") - private Set<String> mClassificationTypeDeniedPackages = new ArraySet<>(); + private Map<String, Set<Integer>> mClassificationTypePackagesEnabledTypes = + new ArrayMap<>(); protected ComponentName mDefaultFromConfig = null; @@ -11958,41 +11994,88 @@ public class NotificationManagerService extends SystemService { } } + /** + * Returns whether the type adjustment is allowed for this particular package. + * If no package-specific restrictions have been set, defaults to the same value as + * isAdjustmentKeyTypeAllowed(type). + */ @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) - protected @NonNull boolean isTypeAdjustmentAllowedForPackage(String pkg) { + protected boolean isTypeAdjustmentAllowedForPackage(String pkg, + @Adjustment.Types int type) { synchronized (mLock) { if (notificationClassificationUi()) { - return !mClassificationTypeDeniedPackages.contains(pkg); + if (mClassificationTypePackagesEnabledTypes.containsKey(pkg)) { + Set<Integer> enabled = mClassificationTypePackagesEnabledTypes.get(pkg); + if (enabled != null) { + return enabled.contains(type); + } + } + // If mClassificationTypePackagesEnabledTypes does not contain the pkg, or + // the stored set is null, return the default. + return isAdjustmentKeyTypeAllowed(type); } } - return true; + return false; } @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) - protected @NonNull String[] getTypeAdjustmentDeniedPackages() { + protected @NonNull int[] getAllowedAdjustmentKeyTypesForPackage(String pkg) { synchronized (mLock) { if (notificationClassificationUi()) { - return mClassificationTypeDeniedPackages.toArray(new String[0]); + if (mClassificationTypePackagesEnabledTypes.containsKey(pkg)) { + Set<Integer> enabled = mClassificationTypePackagesEnabledTypes.get(pkg); + if (enabled != null) { + // Convert Set to int[] for return. + int[] returnEnabled = new int[enabled.size()]; + int i = 0; + for (int val: enabled) { + returnEnabled[i] = val; + i++; + } + return returnEnabled; + } + } + // If package is not in the map, or the value is null, return the default. + return getAllowedAdjustmentKeyTypes(); } } - return new String[]{}; + return new int[]{}; } /** * Set whether a particular package can have its notification channels adjusted to have a * different type by NotificationAssistants. + * Note: once this method is called to enable or disable a specific type for a package, + * the global default is set as the starting point, and the type is enabled/disabled from + * there. Future changes to the global default will not apply automatically to this package. */ @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) - public void setTypeAdjustmentForPackageState(String pkg, boolean enabled) { + public void setAssistantAdjustmentKeyTypeStateForPackage(String pkg, + @Adjustment.Types int type, + boolean enabled) { if (!notificationClassificationUi()) { return; } synchronized (mLock) { + Set<Integer> enabledTypes = null; + if (mClassificationTypePackagesEnabledTypes.containsKey(pkg)) { + enabledTypes = mClassificationTypePackagesEnabledTypes.get(pkg); + } + if (enabledTypes == null) { + // Use global default to start. + enabledTypes = new ArraySet<Integer>(); + // Convert from int[] to Set<Integer> + for (int value : getAllowedAdjustmentKeyTypes()) { + enabledTypes.add(value); + } + } + if (enabled) { - mClassificationTypeDeniedPackages.remove(pkg); + enabledTypes.add(type); } else { - mClassificationTypeDeniedPackages.add(pkg); + enabledTypes.remove(type); } + mClassificationTypePackagesEnabledTypes.put(pkg, enabledTypes); } } @@ -12459,16 +12542,25 @@ public class NotificationManagerService extends SystemService { TextUtils.join(",", mAllowedAdjustmentKeyTypes)); out.endTag(null, ATT_ENABLED_TYPES); if (notificationClassificationUi()) { - out.startTag(null, ATT_TYPES_DENIED_APPS); - out.attribute(null, ATT_TYPES, - TextUtils.join(",", mClassificationTypeDeniedPackages)); - out.endTag(null, ATT_TYPES_DENIED_APPS); + out.startTag(null, TAG_TYPES_ENABLED_FOR_APPS); + for (String pkg: mClassificationTypePackagesEnabledTypes.keySet()) { + Set<Integer> allowedTypes = + mClassificationTypePackagesEnabledTypes.get(pkg); + if (allowedTypes != null) { + out.startTag(null, ATT_APP_ENABLED_TYPES); + out.attribute(null, ATT_PACKAGE, pkg); + out.attribute(null, ATT_TYPES, TextUtils.join(",", allowedTypes)); + out.endTag(null, ATT_APP_ENABLED_TYPES); + } + } + out.endTag(null, TAG_TYPES_ENABLED_FOR_APPS); } } } @Override - protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException { + protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException, + XmlPullParserException { if (!notificationClassification()) { return; } @@ -12495,12 +12587,25 @@ public class NotificationManagerService extends SystemService { } } } - } else if (notificationClassificationUi() && ATT_TYPES_DENIED_APPS.equals(tag)) { - final String apps = XmlUtils.readStringAttribute(parser, ATT_TYPES); + } else if (TAG_TYPES_ENABLED_FOR_APPS.equals(tag)) { + final int appsOuterDepth = parser.getDepth(); synchronized (mLock) { - mClassificationTypeDeniedPackages.clear(); - if (!TextUtils.isEmpty(apps)) { - mClassificationTypeDeniedPackages.addAll(Arrays.asList(apps.split(","))); + mClassificationTypePackagesEnabledTypes.clear(); + while (XmlUtils.nextElementWithin(parser, appsOuterDepth)) { + if (!ATT_APP_ENABLED_TYPES.equals(parser.getName())) { + continue; + } + final String app = XmlUtils.readStringAttribute(parser, ATT_PACKAGE); + Set<Integer> allowedTypes = new ArraySet<>(); + final String typesString = XmlUtils.readStringAttribute(parser, ATT_TYPES); + if (!TextUtils.isEmpty(typesString)) { + allowedTypes = Arrays.stream(typesString.split(",")) + .map(Integer::valueOf) + .collect(Collectors.toSet()); + } + // Empty type list is allowed, because empty type list signifies the user + // has manually cleared the package of allowed types. + mClassificationTypePackagesEnabledTypes.put(app, allowedTypes); } } } diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig index 65a38ae1fcde..f15c23e110a4 100644 --- a/services/core/java/com/android/server/notification/flags.aconfig +++ b/services/core/java/com/android/server/notification/flags.aconfig @@ -187,3 +187,13 @@ flag { description: "Enables sound uri with vibration source in notification channel" bug: "351975435" } + +flag { + name: "nm_binder_perf_reduce_zen_broadcasts" + namespace: "systemui" + description: "Don't send duplicate zen-related (policy changed, etc) broadcasts" + bug: "324376849" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java index c4e4c422688d..1726f0da9cbe 100644 --- a/services/core/java/com/android/server/power/hint/HintManagerService.java +++ b/services/core/java/com/android/server/power/hint/HintManagerService.java @@ -1594,12 +1594,9 @@ public final class HintManagerService extends SystemService { } halParams.tids = params.tids; } - if (halParams.calculationWindowMillis - == mDefaultCpuHeadroomCalculationWindowMillis) { - synchronized (mCpuHeadroomLock) { - final CpuHeadroomResult res = mCpuHeadroomCache.get(halParams); - if (res != null) return res; - } + synchronized (mCpuHeadroomLock) { + final CpuHeadroomResult res = mCpuHeadroomCache.get(halParams); + if (res != null) return res; } final boolean shouldCheckUserModeCpuTime = mEnforceCpuHeadroomUserModeCpuTimeCheck @@ -1622,11 +1619,8 @@ public final class HintManagerService extends SystemService { Slog.wtf(TAG, "CPU headroom from Power HAL is invalid"); return null; } - if (halParams.calculationWindowMillis - == mDefaultCpuHeadroomCalculationWindowMillis) { - synchronized (mCpuHeadroomLock) { - mCpuHeadroomCache.add(halParams, result); - } + synchronized (mCpuHeadroomLock) { + mCpuHeadroomCache.add(halParams, result); } if (shouldCheckUserModeCpuTime) { synchronized (mCpuHeadroomLock) { @@ -1737,12 +1731,9 @@ public final class HintManagerService extends SystemService { final GpuHeadroomParams halParams = new GpuHeadroomParams(); halParams.calculationType = params.calculationType; halParams.calculationWindowMillis = params.calculationWindowMillis; - if (halParams.calculationWindowMillis - == mDefaultGpuHeadroomCalculationWindowMillis) { - synchronized (mGpuHeadroomLock) { - final GpuHeadroomResult res = mGpuHeadroomCache.get(halParams); - if (res != null) return res; - } + synchronized (mGpuHeadroomLock) { + final GpuHeadroomResult res = mGpuHeadroomCache.get(halParams); + if (res != null) return res; } // return from HAL directly try { @@ -1751,11 +1742,8 @@ public final class HintManagerService extends SystemService { Slog.wtf(TAG, "GPU headroom from Power HAL is invalid"); return null; } - if (halParams.calculationWindowMillis - == mDefaultGpuHeadroomCalculationWindowMillis) { - synchronized (mGpuHeadroomLock) { - mGpuHeadroomCache.add(halParams, headroom); - } + synchronized (mGpuHeadroomLock) { + mGpuHeadroomCache.add(halParams, headroom); } return headroom; } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 66b77b9d4d2a..4c2d8492197c 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -50,6 +50,7 @@ import static com.android.window.flags.Flags.balImprovedMetrics; import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator; import static com.android.window.flags.Flags.balShowToastsBlocked; import static com.android.window.flags.Flags.balStrictModeRo; +import static com.android.window.flags.Flags.balStrictModeGracePeriod; import static java.lang.annotation.RetentionPolicy.SOURCE; import static java.util.Objects.requireNonNull; @@ -516,7 +517,9 @@ public class BackgroundActivityStartController { return !callerExplicitOptOut(); } return mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode() - == MODE_BACKGROUND_ACTIVITY_START_ALLOWED; + != MODE_BACKGROUND_ACTIVITY_START_DENIED + && mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode() + != MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; } public boolean realCallerExplicitOptInOrAutoOptIn() { @@ -524,7 +527,9 @@ public class BackgroundActivityStartController { return !realCallerExplicitOptOut(); } return mCheckedOptions.getPendingIntentBackgroundActivityStartMode() - == MODE_BACKGROUND_ACTIVITY_START_ALLOWED; + != MODE_BACKGROUND_ACTIVITY_START_DENIED + && mCheckedOptions.getPendingIntentBackgroundActivityStartMode() + != MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; } public boolean callerExplicitOptOut() { @@ -1908,7 +1913,14 @@ public class BackgroundActivityStartController { (state.mOriginatingPendingIntent != null)); } - logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_GRACE_PERIOD); + if (logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_GRACE_PERIOD)) { + if (balStrictModeRo() && balStrictModeGracePeriod()) { + String abortDebugMessage = "Activity start is only allowed by grace period. " + + "This may stop working in the future. " + + "intent: " + state.mIntent; + strictModeLaunchAborted(state.mRealCallingUid, abortDebugMessage); + } + } logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_NON_APP_VISIBLE_WINDOW); if (balImprovedMetrics()) { @@ -1952,24 +1964,29 @@ public class BackgroundActivityStartController { * Logs details about the activity starts if the only reason it is allowed is the provided * {@code balCode}. */ - private static void logIfOnlyAllowedBy(BalVerdict finalVerdict, BalState state, int balCode) { + private static boolean logIfOnlyAllowedBy(BalVerdict finalVerdict, BalState state, + int balCode) { if (finalVerdict.getRawCode() == balCode) { if (state.realCallerExplicitOptInOrAutoOptIn() && state.mResultForRealCaller != null && state.mResultForRealCaller.allows() && state.mResultForRealCaller.getRawCode() != balCode) { // real caller could allow with a different exemption + return false; } else if (state.callerExplicitOptInOrAutoOptIn() && state.mResultForCaller != null && state.mResultForCaller.allows() && state.mResultForCaller.getRawCode() != balCode) { // caller could allow with a different exemption + return false; } else { // log to determine grace period length distribution Slog.wtf(TAG, "Activity start ONLY allowed by " + balCodeToString(balCode) + " " + finalVerdict.mMessage + ": " + state); + return true; } } + return false; } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index cba606cf2b0c..98ed6f76b2f9 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -101,7 +101,8 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { // isLeashReadyForDispatching (used to dispatch the leash of the control) is // depending on mGivenInsetsReady. Therefore, triggering notifyControlChanged here // again, so that the control with leash can be eventually dispatched - if (!mGivenInsetsReady && isServerVisible() && !givenInsetsPending) { + if (!mGivenInsetsReady && isServerVisible() && !givenInsetsPending + && mControlTarget != null) { mGivenInsetsReady = true; ImeTracker.forLogging().onProgress(mStatsToken, ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED); diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 7276007481ab..d1585d06ae40 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -384,7 +384,7 @@ class InsetsSourceProvider { } final boolean serverVisibleChanged = mServerVisible != isServerVisible; setServerVisible(isServerVisible); - if (mControl != null) { + if (mControl != null && mControlTarget != null) { final boolean positionChanged = updateInsetsControlPosition(windowState); if (!(positionChanged || mHasPendingPosition) // The insets hint would be updated while changing the position. Here updates it diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index ce8518449230..9df65f60e8d7 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -371,7 +371,7 @@ class InsetsStateController { array.add(provider); } - void notifyControlChanged(InsetsControlTarget target, InsetsSourceProvider provider) { + void notifyControlChanged(@NonNull InsetsControlTarget target, InsetsSourceProvider provider) { addToPendingControlMaps(target, provider); notifyPendingInsetsControlChanged(); } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 813fec1454a7..f634beb77329 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -345,6 +345,7 @@ public: void setMouseReverseVerticalScrollingEnabled(bool enabled); void setMouseScrollingAccelerationEnabled(bool enabled); void setMouseSwapPrimaryButtonEnabled(bool enabled); + void setMouseAccelerationEnabled(bool enabled); void setTouchpadPointerSpeed(int32_t speed); void setTouchpadNaturalScrollingEnabled(bool enabled); void setTouchpadTapToClickEnabled(bool enabled); @@ -502,6 +503,9 @@ private: // True if the mouse primary button is swapped (left/right buttons). bool mouseSwapPrimaryButtonEnabled{false}; + // True if the mouse cursor will accelerate as the mouse moves faster. + bool mousePointerAccelerationEnabled{true}; + // The touchpad pointer speed, as a number from -7 (slowest) to 7 (fastest). int32_t touchpadPointerSpeed{0}; @@ -847,6 +851,7 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon outConfig->mouseReverseVerticalScrollingEnabled = mLocked.mouseReverseVerticalScrollingEnabled; outConfig->mouseSwapPrimaryButtonEnabled = mLocked.mouseSwapPrimaryButtonEnabled; + outConfig->mousePointerAccelerationEnabled = mLocked.mousePointerAccelerationEnabled; outConfig->touchpadPointerSpeed = mLocked.touchpadPointerSpeed; outConfig->touchpadNaturalScrollingEnabled = mLocked.touchpadNaturalScrollingEnabled; @@ -1458,6 +1463,21 @@ void NativeInputManager::setMouseSwapPrimaryButtonEnabled(bool enabled) { InputReaderConfiguration::Change::MOUSE_SETTINGS); } +void NativeInputManager::setMouseAccelerationEnabled(bool enabled) { + { // acquire lock + std::scoped_lock _l(mLock); + + if (mLocked.mousePointerAccelerationEnabled == enabled) { + return; + } + + mLocked.mousePointerAccelerationEnabled = enabled; + } // release lock + + mInputManager->getReader().requestRefreshConfiguration( + InputReaderConfiguration::Change::POINTER_SPEED); +} + void NativeInputManager::setPointerSpeed(int32_t speed) { { // acquire lock std::scoped_lock _l(mLock); @@ -3220,6 +3240,11 @@ static void nativeSetMouseSwapPrimaryButtonEnabled(JNIEnv* env, jobject nativeIm im->setMouseSwapPrimaryButtonEnabled(enabled); } +static void nativeSetMouseAccelerationEnabled(JNIEnv* env, jobject nativeImplObj, bool enabled) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); + im->setMouseAccelerationEnabled(enabled); +} + static jboolean nativeSetKernelWakeEnabled(JNIEnv* env, jobject nativeImplObj, jint deviceId, jboolean enabled) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); @@ -3280,6 +3305,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"setMouseScrollingAccelerationEnabled", "(Z)V", (void*)nativeSetMouseScrollingAccelerationEnabled}, {"setMouseSwapPrimaryButtonEnabled", "(Z)V", (void*)nativeSetMouseSwapPrimaryButtonEnabled}, + {"setMouseAccelerationEnabled", "(Z)V", (void*)nativeSetMouseAccelerationEnabled}, {"setTouchpadPointerSpeed", "(I)V", (void*)nativeSetTouchpadPointerSpeed}, {"setTouchpadNaturalScrollingEnabled", "(Z)V", (void*)nativeSetTouchpadNaturalScrollingEnabled}, diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java index ea80f283793e..db6aeebecce7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -602,7 +602,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { mQueue.dumpDebug(new ProtoOutputStream(), ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE); mQueue.dumpLocked(FileDescriptor.err, new PrintWriter(Writer.nullWriter()), - null, 0, true, true, true, null, false); + null, 0, true, true, true, null, null, false); mQueue.dumpToDropBoxLocked(TAG); BroadcastQueue.logv(TAG); @@ -1019,7 +1019,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { mQueue.dumpDebug(new ProtoOutputStream(), ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE); mQueue.dumpLocked(FileDescriptor.err, new PrintWriter(Writer.nullWriter()), - null, 0, true, true, true, null, false); + null, 0, true, true, true, null, null, false); } waitForIdle(); diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java index bd15bd058fcc..cd94c0f6e245 100644 --- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java +++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java @@ -1411,7 +1411,6 @@ public class HintManagerServiceTest { halParams3.tids = tids; halParams3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE; - // this params should not be cached as the window is not default CpuHeadroomParamsInternal params4 = new CpuHeadroomParamsInternal(); params4.calculationWindowMillis = 123; CpuHeadroomParams halParams4 = new CpuHeadroomParams(); @@ -1450,11 +1449,7 @@ public class HintManagerServiceTest { assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3)); assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4)); - verify(mIPowerMock, times(1)).getCpuHeadroom(any()); - verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1)); - verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams2)); - verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3)); - verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); + verify(mIPowerMock, times(0)).getCpuHeadroom(any()); // after 500ms more it should be served with cache Thread.sleep(500); @@ -1463,11 +1458,7 @@ public class HintManagerServiceTest { assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3)); assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4)); - verify(mIPowerMock, times(1)).getCpuHeadroom(any()); - verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1)); - verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams2)); - verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3)); - verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); + verify(mIPowerMock, times(0)).getCpuHeadroom(any()); // after 1+ seconds it should be served from HAL as it exceeds 1000 millis interval Thread.sleep(600); @@ -1574,18 +1565,14 @@ public class HintManagerServiceTest { clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); - verify(mIPowerMock, times(1)).getGpuHeadroom(any()); - verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1)); - verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); + verify(mIPowerMock, times(0)).getGpuHeadroom(any()); // after 500ms it should be served with cache Thread.sleep(500); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); - verify(mIPowerMock, times(1)).getGpuHeadroom(any()); - verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1)); - verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); + verify(mIPowerMock, times(0)).getGpuHeadroom(any()); // after 1+ seconds it should be served from HAL as it exceeds 1000 millis interval Thread.sleep(600); diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java index c7a06b8eec7b..b1df0f1e9cce 100644 --- a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java @@ -31,7 +31,6 @@ import android.os.test.FakePermissionEnforcer; import android.os.test.TestLooper; import android.provider.Settings; import android.security.advancedprotection.AdvancedProtectionFeature; -import android.security.advancedprotection.AdvancedProtectionManager; import android.security.advancedprotection.IAdvancedProtectionCallback; import androidx.annotation.NonNull; @@ -55,8 +54,7 @@ public class AdvancedProtectionServiceTest { private Context mContext; private AdvancedProtectionService.AdvancedProtectionStore mStore; private TestLooper mLooper; - AdvancedProtectionFeature mTestFeature2g = new AdvancedProtectionFeature( - AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G); + AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature("test-id"); @Before public void setup() throws Settings.SettingNotFoundException { @@ -107,7 +105,7 @@ public class AdvancedProtectionServiceTest { @NonNull @Override public AdvancedProtectionFeature getFeature() { - return mTestFeature2g; + return mFeature; } @Override @@ -137,7 +135,7 @@ public class AdvancedProtectionServiceTest { @NonNull @Override public AdvancedProtectionFeature getFeature() { - return mTestFeature2g; + return mFeature; } @Override @@ -167,7 +165,7 @@ public class AdvancedProtectionServiceTest { @NonNull @Override public AdvancedProtectionFeature getFeature() { - return mTestFeature2g; + return mFeature; } @Override @@ -240,10 +238,8 @@ public class AdvancedProtectionServiceTest { @Test public void testGetFeatures() { - AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature( - AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G); - AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature( - AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES); + AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature("id-1"); + AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature("id-2"); AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) { @NonNull @Override @@ -272,10 +268,8 @@ public class AdvancedProtectionServiceTest { @Test public void testGetFeatures_featureNotAvailable() { - AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature( - AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G); - AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature( - AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES); + AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature("id-1"); + AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature("id-2"); AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) { @NonNull @Override diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index d1dc8d6e81c8..4f5cdb73edd2 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -19,9 +19,13 @@ import static android.os.UserHandle.USER_ALL; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION; import static android.service.notification.Adjustment.TYPE_NEWS; +import static android.service.notification.Adjustment.TYPE_OTHER; import static android.service.notification.Adjustment.TYPE_PROMOTION; +import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA; +import static android.service.notification.Flags.notificationClassification; import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENTS; +import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES; import static com.google.common.truth.Truth.assertThat; @@ -144,6 +148,17 @@ public class NotificationAssistantsTest extends UiServiceTestCase { mAssistants.readXml(parser, mNm::canUseManagedServices, false, USER_ALL); } + private void setDefaultAllowedAdjustmentKeyTypes(NotificationAssistants assistants) { + assistants.setAssistantAdjustmentKeyTypeState(TYPE_OTHER, false); + assistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false); + assistants.setAssistantAdjustmentKeyTypeState(TYPE_SOCIAL_MEDIA, false); + assistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, false); + assistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, false); + + for (int type : DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES) { + assistants.setAssistantAdjustmentKeyTypeState(type, true); + } + } @Before public void setUp() throws Exception { @@ -154,6 +169,9 @@ public class NotificationAssistantsTest extends UiServiceTestCase { com.android.internal.R.string.config_defaultAssistantAccessComponent, mCn.flattenToString()); mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm)); + if (notificationClassification()) { + setDefaultAllowedAdjustmentKeyTypes(mAssistants); + } when(mNm.getBinderService()).thenReturn(mINm); mContext.ensureTestableResources(); @@ -695,7 +713,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true); assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() - .containsExactlyElementsIn(List.of(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION)); + .containsExactly(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION); } @Test @@ -716,7 +734,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { writeXmlAndReload(USER_ALL); assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList() - .containsExactlyElementsIn(List.of(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION)); + .containsExactly(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION); } @Test @@ -732,76 +750,146 @@ public class NotificationAssistantsTest extends UiServiceTestCase { @Test @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + public void testSetAssistantAdjustmentKeyTypeStateForPackage_usesGlobalDefault() { + String pkg = "my.package"; + setDefaultAllowedAdjustmentKeyTypes(mAssistants); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue(); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isFalse(); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList() + .containsExactlyElementsIn(DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) public void testSetAssistantAdjustmentKeyTypeStateForPackage_allowsAndDenies() { - // Given that a package is allowed to have its type adjusted, - String allowedPackage = "allowed.package"; - assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty(); - mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true); + setDefaultAllowedAdjustmentKeyTypes(mAssistants); + // Given that a package is set to have a type adjustment allowed, + String pkg = "my.package"; + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, true); - assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty(); - assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage)); + // The newly set state is the combination of the global default and the newly set type. + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList() + .containsExactly(TYPE_NEWS, TYPE_PROMOTION); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue(); // Set type adjustment disallowed for this package - mAssistants.setTypeAdjustmentForPackageState(allowedPackage, false); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, false); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, false); // Then the package is marked as denied - assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList() - .containsExactly(allowedPackage); - assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage)); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).isEmpty(); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isFalse(); // Set type adjustment allowed again - mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, true); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, true); // Then the package is marked as allowed again - assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty(); - assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage)); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList() + .containsExactly(TYPE_NEWS, TYPE_PROMOTION); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue(); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue(); + + // Set type adjustment promotions false, + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, false); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList() + .containsExactly(TYPE_NEWS); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue(); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isFalse(); } @Test @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) - public void testSetAssistantAdjustmentKeyTypeStateForPackage_deniesMultiple() { - // Given packages not allowed to have their type adjusted, - String deniedPkg1 = "denied.Pkg1"; - String deniedPkg2 = "denied.Pkg2"; - String deniedPkg3 = "denied.Pkg3"; - // Set type adjustment disallowed for these packages - mAssistants.setTypeAdjustmentForPackageState(deniedPkg1, false); - mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, false); - mAssistants.setTypeAdjustmentForPackageState(deniedPkg3, false); - - // Then the packages are marked as denied - assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList() - .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg2, deniedPkg3)); - assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg1)); - assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg2)); - assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg3)); - - // And when we re-allow one of them, - mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, true); - - // Then the rest of the original packages are still marked as denied. - assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList() - .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg3)); - assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg1)); - assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg2)); - assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg3)); + public void testSetAssistantAdjustmentKeyTypeStateForPackage_allowsMultiplePkgs() { + setDefaultAllowedAdjustmentKeyTypes(mAssistants); + // Given packages allowed to have their type adjusted to TYPE_NEWS, + String allowedPkg1 = "allowed.Pkg1"; + String allowedPkg2 = "allowed.Pkg2"; + String allowedPkg3 = "allowed.Pkg3"; + // Set type adjustment allowed for these packages + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg1, TYPE_NEWS, true); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_NEWS, true); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_NEWS, true); + + // The newly set state is the combination of the global default and the newly set type. + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg1)).asList() + .containsExactly(TYPE_NEWS, TYPE_PROMOTION); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg2)).asList() + .containsExactly(TYPE_NEWS, TYPE_PROMOTION); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg3)).asList() + .containsExactly(TYPE_NEWS, TYPE_PROMOTION); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg1, TYPE_NEWS)).isTrue(); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg2, TYPE_NEWS)).isTrue(); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg3, TYPE_NEWS)).isTrue(); + + // And when we deny some of them, + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_NEWS, false); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_PROMOTION, + false); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_PROMOTION, + false); + + // Then the rest of the original packages are still marked as allowed. + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg1)).asList() + .containsExactly(TYPE_NEWS, TYPE_PROMOTION); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg2)).isEmpty(); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg3)).asList() + .containsExactly(TYPE_NEWS); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg1, TYPE_NEWS)).isTrue(); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg2, TYPE_NEWS)).isFalse(); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg3, TYPE_NEWS)).isTrue(); } @Test @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) public void testSetAssistantAdjustmentKeyTypeStateForPackage_readWriteXml() throws Exception { + setDefaultAllowedAdjustmentKeyTypes(mAssistants); mAssistants.loadDefaultsFromConfig(true); String deniedPkg1 = "denied.Pkg1"; String allowedPkg2 = "allowed.Pkg2"; - String deniedPkg3 = "denied.Pkg3"; + String allowedPkg3 = "allowed.Pkg3"; // Set type adjustment disallowed or allowed for these packages - mAssistants.setTypeAdjustmentForPackageState(deniedPkg1, false); - mAssistants.setTypeAdjustmentForPackageState(allowedPkg2, true); - mAssistants.setTypeAdjustmentForPackageState(deniedPkg3, false); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(deniedPkg1, TYPE_PROMOTION, false); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_NEWS, true); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_NEWS, true); + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_SOCIAL_MEDIA, + true); writeXmlAndReload(USER_ALL); - assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList() - .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg3)); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(deniedPkg1)).isEmpty(); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg2)).asList() + .containsExactly(TYPE_NEWS, TYPE_PROMOTION); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg3)).asList() + .containsExactly(TYPE_NEWS, TYPE_SOCIAL_MEDIA, TYPE_PROMOTION); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) + public void testSetAssistantAdjustmentKeyTypeStateForPackage_noGlobalImpact() throws Exception { + setDefaultAllowedAdjustmentKeyTypes(mAssistants); + // When the global state is changed, + mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true); + + // The package state reflects the global state. + String pkg = "my.package"; + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue(); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue(); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList() + .containsExactly(TYPE_NEWS, TYPE_PROMOTION); + + // Once the package specific state is modified, + mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_SOCIAL_MEDIA, true); + + // The package specific state combines the global state with those modifications + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_SOCIAL_MEDIA)).isTrue(); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList() + .containsExactly(TYPE_NEWS, TYPE_PROMOTION, TYPE_SOCIAL_MEDIA); + + // And further changes to the global state are ignored. + mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, false); + assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue(); + assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList() + .containsExactly(TYPE_NEWS, TYPE_PROMOTION, TYPE_SOCIAL_MEDIA); } }
\ No newline at end of file diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 601023f89656..301165f8151d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -365,6 +365,9 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; @@ -380,9 +383,6 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; -import platform.test.runner.parameterized.ParameterizedAndroidJunit4; -import platform.test.runner.parameterized.Parameters; - @SmallTest @RunWith(ParameterizedAndroidJunit4.class) @RunWithLooper @@ -644,6 +644,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { doNothing().when(mContext).sendBroadcast(any(), anyString()); doNothing().when(mContext).sendBroadcastAsUser(any(), any()); doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); + doNothing().when(mContext).sendBroadcastMultiplePermissions(any(), any(), any(), any()); + doReturn(mContext).when(mContext).createContextAsUser(eq(mUser), anyInt()); + TestableContentResolver cr = mock(TestableContentResolver.class); when(mContext.getContentResolver()).thenReturn(cr); doNothing().when(cr).registerContentObserver(any(), anyBoolean(), any(), anyInt()); @@ -7631,7 +7634,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true); when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true); - when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true); + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true); // Set up notifications that will be adjusted final NotificationRecord r1 = spy(generateNotificationRecord( @@ -11235,7 +11238,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void onZenModeChanged_sendsBroadcasts() throws Exception { + @DisableFlags(Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS) + public void onZenModeChanged_sendsBroadcasts_oldBehavior() throws Exception { when(mAmi.getCurrentUserId()).thenReturn(100); when(mUmInternal.getProfileIds(eq(100), anyBoolean())).thenReturn(new int[]{100, 101, 102}); when(mConditionProviders.getAllowedPackages(anyInt())).then(new Answer<List<String>>() { @@ -11288,6 +11292,74 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + @EnableFlags(Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS) + public void onZenModeChanged_sendsBroadcasts() throws Exception { + when(mAmi.getCurrentUserId()).thenReturn(100); + when(mUmInternal.getProfileIds(eq(100), anyBoolean())).thenReturn(new int[]{100, 101, 102}); + when(mConditionProviders.getAllowedPackages(anyInt())).then(new Answer<List<String>>() { + @Override + public List<String> answer(InvocationOnMock invocation) { + int userId = invocation.getArgument(0); + switch (userId) { + case 100: + return Lists.newArrayList("a", "b", "c"); + case 101: + return Lists.newArrayList(); + case 102: + return Lists.newArrayList("b"); + default: + throw new IllegalArgumentException( + "Why would you ask for packages of userId " + userId + "?"); + } + } + }); + Context context100 = mock(Context.class); + doReturn(context100).when(mContext).createContextAsUser(eq(UserHandle.of(100)), anyInt()); + Context context101 = mock(Context.class); + doReturn(context101).when(mContext).createContextAsUser(eq(UserHandle.of(101)), anyInt()); + Context context102 = mock(Context.class); + doReturn(context102).when(mContext).createContextAsUser(eq(UserHandle.of(102)), anyInt()); + + mService.getBinderService().setZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS, null, + "testing!", false); + waitForIdle(); + + // Verify broadcasts per user: registered receivers first, then DND packages. + InOrder inOrder = inOrder(context100, context101, context102); + + inOrder.verify(context100).sendBroadcastMultiplePermissions( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)), + eq(new String[0]), eq(new String[0]), eq(new String[] {"a", "b", "c"})); + inOrder.verify(context100).sendBroadcast( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setPackage("a") + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT))); + inOrder.verify(context100).sendBroadcast( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setPackage("b") + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT))); + inOrder.verify(context100).sendBroadcast( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setPackage("c") + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT))); + + inOrder.verify(context101).sendBroadcastMultiplePermissions( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)), + eq(new String[0]), eq(new String[0]), eq(new String[] {})); + + inOrder.verify(context102).sendBroadcastMultiplePermissions( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)), + eq(new String[0]), eq(new String[0]), eq(new String[] {"b"})); + inOrder.verify(context102).sendBroadcast( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setPackage("b") + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT))); + } + + @Test @EnableFlags(android.app.Flags.FLAG_MODES_API) public void onAutomaticRuleStatusChanged_sendsBroadcastToRuleOwner() throws Exception { mService.mZenModeHelper.getCallbacks().forEach(c -> c.onAutomaticRuleStatusChanged( @@ -17305,7 +17377,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.WorkerHandler.class); mService.setHandler(handler); when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true); - when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true); + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true); Bundle signals = new Bundle(); signals.putInt(KEY_TYPE, TYPE_NEWS); @@ -17349,7 +17421,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.WorkerHandler.class); mService.setHandler(handler); when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true); - when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true); + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), eq(TYPE_NEWS))) + .thenReturn(true); + // Blocking adjustments for a different type does nothing + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), eq(TYPE_PROMOTION))) + .thenReturn(false); Bundle signals = new Bundle(); signals.putInt(KEY_TYPE, TYPE_NEWS); @@ -17364,8 +17440,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID); - // When we block adjustments for this package - when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(false); + // When we block adjustments for this package/type + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), eq(TYPE_PROMOTION))) + .thenReturn(false); signals.putInt(KEY_TYPE, TYPE_PROMOTION); mBinderService.applyAdjustmentFromAssistant(null, adjustment); @@ -17695,7 +17772,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true); when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true); - when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true); + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true); // Post a single notification final boolean hasOriginalSummary = false; @@ -17735,7 +17812,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true); when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true); - when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true); + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true); // Post grouped notifications final String originalGroupName = "originalGroup"; @@ -17784,7 +17861,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true); when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true); - when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true); + when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true); // Post grouped notifications final String originalGroupName = "originalGroup"; diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index e7c9e927b311..e27dbe588183 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1869,10 +1869,18 @@ public class UsageStatsService extends SystemService implements } private boolean shouldDeleteObsoleteData(UserHandle userHandle) { - final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); - // If a profile owner is not defined for the given user, obsolete data should be deleted - return dpmInternal == null - || dpmInternal.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle) == null; + if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) { + final SupervisionManagerInternal smInternal = getSupervisionManagerInternal(); + // If supervision is not enabled for the given user, obsolete data should be deleted. + return smInternal == null + || !smInternal.isSupervisionEnabledForUser(userHandle.getIdentifier()); + } else { + final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); + // If a profile owner is not defined for the given user, obsolete data should be deleted + return dpmInternal == null + || dpmInternal.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle) + == null; + } } private String buildFullToken(String packageName, String token) { |