Merge "Move screenshot_context_url to sysui shared flags" into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a60fd11..e724ab8 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -12676,21 +12676,27 @@
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 4db3727..0e45902 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -38,6 +38,7 @@
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 @@
FLAG_IS_ALARM_BROADCAST,
FLAG_SHARE_IDENTITY,
FLAG_INTERACTIVE,
+ FLAG_DEBUG_LOG,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Flags {}
@@ -86,6 +88,7 @@
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 @@
}
/**
+ * 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 a6c1a57..0451ac0 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -270,6 +270,6 @@
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 ec10913..08bd854 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1986,10 +1986,12 @@
* @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 f29e2e8..2ad9660 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -211,6 +211,16 @@
}
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 da9d4f4..0e2c05f 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -246,10 +246,9 @@
// 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 @@
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 @@
}
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 34c88e9..8da630c 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.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 @@
}
/**
+ * 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 @@
}
/**
+ * 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 1cc64f9..4c90750 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -29,6 +29,7 @@
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 @@
@Nullable
private Boolean mPreviousResponseIsEmpty;
+ private boolean mAlwaysNotifyAutofill = false;
/**
* Indicates whether {@link #makeInlineSuggestionRequestUncheck()} has been called or not,
@@ -105,6 +107,7 @@
mResponseConsumer = responseConsumer;
mInlineSuggestionSessionController = inlineSuggestionSessionController;
mMainThreadHandler = mainThreadHandler;
+ mAlwaysNotifyAutofill = AutofillFeatureFlags.isImproveFillDialogEnabled();
}
@MainThread
@@ -176,6 +179,9 @@
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 @@
} 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 2ad6669..6e58780 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6362,6 +6362,16 @@
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 @@
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 d476d96..a086bf7 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java
@@ -30,25 +30,26 @@
@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 @@
@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 ea01fc9..59628e8 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -24,18 +24,17 @@
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 @@
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 @@
@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 @@
* 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 @@
*
* @hide */
@FeatureId
+ @SystemApi
public static final String EXTRA_SUPPORT_DIALOG_FEATURE =
"android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
@@ -157,41 +157,37 @@
*
* @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 @@
* 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 @@
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 4f74198..880622a 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -221,13 +221,13 @@
@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 b0813f3..c174fbe 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -959,6 +959,7 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
handlePendingControlRequest(statsToken);
getImeSourceConsumer().removeSurface();
}
- applyAnimation(typesReady, false /* show */, fromIme, statsToken);
+ applyAnimation(typesReady, false /* show */, fromIme, false /* skipsCallbacks */,
+ statsToken);
}
@Override
@@ -2007,24 +2022,24 @@
@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 @@
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 @@
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 @@
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 17f33c1..e8e6621 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -130,7 +130,10 @@
* @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 @@
} 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 acbd95bf..7f2f0e8 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -142,6 +142,10 @@
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 d527007..206e47f 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 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 eaa8f60..e904345f 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -992,23 +992,7 @@
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 @@
}
}
+ 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 d5ba32c..4b5adfc 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -75,4 +75,9 @@
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 751cfde..b7537ed 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -149,8 +149,10 @@
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 @@
}
private boolean shouldShowNumber() {
+ if (notificationsRedesignTemplates()) {
+ return mNumber > 1;
+ }
return !mExpanded && mNumber > 1;
}
@@ -230,7 +235,7 @@
/**
* 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 dd9bfa5..0d99200 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -228,6 +228,7 @@
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 3ef3dfd..d7c4212 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -73,6 +73,7 @@
"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 931d646..f1925bb 100644
--- a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
+++ b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
@@ -29,6 +29,8 @@
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 @@
@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 b7d2562..008db5e 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 @@
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 45864b0..0000000
--- 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 248db65..4a54f6b 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -133,7 +133,7 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 d7f6a29..905d897 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -100,14 +100,14 @@
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 3a8f7ee..45d66e8 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -117,7 +117,7 @@
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 @@
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 @@
@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 @@
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 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 @@
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 @@
// 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 @@
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 7e87462..166b388 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -276,16 +276,19 @@
/**
* 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 @@
/**
* 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 b4afb7d..7374f80 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 @@
}
/**
- * @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 @@
}
}
+ /**
+ * 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 @@
}
/**
- * @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 @@
}
/**
+ * @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 b754706..313013c 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.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 @@
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 @@
}
/**
+ * 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 @@
/** 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 @@
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 @@
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 ad34e83..b2c2794 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.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 @@
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 @@
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 8dfeb55..7c24df9 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 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 @@
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 @@
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 @@
}
}
- 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 @@
}
}
+ // 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 c71b19c..e01f279 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 @@
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 05f471f..69e99c6 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.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 @@
@Mock
private HearingAidProfile mHearingAidProfile;
@Mock
+ private HapClientProfile mHapClientProfile;
+ @Mock
private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
@Mock
private BluetoothDevice mDevice1;
@@ -89,12 +107,11 @@
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 @@
@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 @@
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 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(cachedDevice1);
+ mCachedDeviceManager.onActiveDeviceChanged(mCachedDevice1);
- verify(mHearingAidDeviceManager).onActiveDeviceChanged(cachedDevice1);
+ 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.notifyHearingDevicesConnectionStatusChangedIfNeeded(mCachedDevice1);
+
+ 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 30f8a79..d933a1c 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 @@
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 2458c5b..21dde1f 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 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 @@
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 @@
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 @@
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 6ff90ba..219bfe0 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.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 @@
@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 @@
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 f1bbfc6..5b4ee8b 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 @@
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 6abd9b7..0432eea 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 @@
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 3ee2db1..b88ae37 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -278,13 +278,13 @@
"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 a93e4a0..7d201c1 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -492,7 +492,7 @@
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
similarity index 75%
rename from packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt
index 2233deb..4ee6db3 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 @@
}
}
}
-
-/** 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
similarity index 71%
rename from packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
index f459c46..d992403 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.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 @@
)
@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 @@
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
similarity index 100%
rename from packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt
rename to 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
similarity index 98%
rename from packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt
rename to packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt
index da8fe30..5a3f240 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 7956d02..9643f19 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.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 @@
)
}
- 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 eae46e9..fb01e70 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 @@
.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 @@
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 @@
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 @@
.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 cfbe667..ffdf509 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 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 1480db9..5f991fb 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 @@
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 @@
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 7b30a2a..c704a3e 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.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 8794df0..fda6fab 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 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 @@
/** 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 8c5a727..4c15f7a 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.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 0000000..2db45aa
--- /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 6769032..53495be 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 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 80de087..2665910 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 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 @@
@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 24f3a29..785493f 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 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 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 @@
@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 @@
anyInt());
mMenuInfoRepository = new MenuInfoRepository(mContext, mAccessibilityManager,
- mMockSettingsContentsChanged, mSecureSettings);
+ mMockSettingsContentsChanged, mSecureSettings, mHearingAidDeviceManager);
}
@After
@@ -103,4 +108,16 @@
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 157cccc..241da5f 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.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 @@
@Mock
private AccessibilityManager mAccessibilityManager;
+ @Mock
+ private HearingAidDeviceManager mHearingAidDeviceManager;
private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings();
private RecyclerView mStubListView;
private MenuView mMenuView;
@@ -84,7 +87,7 @@
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 46f076a..fbd8a71 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.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 @@
@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 fcdeff9..4f04310 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 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 @@
@Mock
private AccessibilityManager mAccessibilityManager;
+ @Mock
+ private HearingAidDeviceManager mHearingAidDeviceManager;
@Mock
private SecureSettings mSecureSettings;
@@ -93,7 +96,7 @@
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 ee8ce17..cb7c205 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 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 @@
@Mock
private AccessibilityManager mAccessibilityManager;
+ @Mock
+ private HearingAidDeviceManager mHearingAidDeviceManager;
private SysuiTestableContext mSpyContext;
@@ -90,7 +93,7 @@
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 f68a1b5..eae5728 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 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 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 @@
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 @@
}
@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 @@
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 @@
@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 @@
@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 @@
@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 @@
.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 6a0781b..73957eb 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 @@
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 0000000..765c02a
--- /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 028beb5..e5e8d4a 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.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 @@
@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 @@
private QsEventLogger mUiEventLogger;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private QSSettingsPackageRepository mQSSettingsPackageRepository;
private TestableLooper mTestableLooper;
private SecureSettings mSecureSettings;
@@ -83,6 +87,8 @@
mTestableLooper = TestableLooper.get(this);
when(mHost.getContext()).thenReturn(mContext);
+ when(mQSSettingsPackageRepository.getSettingsPackageName())
+ .thenReturn(SETTINGS_PACKAGE_NAME);
mTile = new ColorCorrectionTile(
mHost,
@@ -95,7 +101,8 @@
mActivityStarter,
mQSLogger,
mUserTracker,
- mSecureSettings
+ mSecureSettings,
+ mQSSettingsPackageRepository
);
mTile.initialize();
@@ -119,5 +126,6 @@
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 a58dd63..cbde998 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.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.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 @@
private QsEventLogger mUiEventLogger;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private QSSettingsPackageRepository mQSSettingsPackageRepository;
private TestableLooper mTestableLooper;
private SecureSettings mSecureSettings;
@@ -108,6 +112,8 @@
mTestableLooper = TestableLooper.get(this);
when(mHost.getContext()).thenReturn(mContext);
+ when(mQSSettingsPackageRepository.getSettingsPackageName())
+ .thenReturn(SETTINGS_PACKAGE_NAME);
mTile = new ColorInversionTile(
mHost,
@@ -120,7 +126,8 @@
mActivityStarter,
mQSLogger,
mUserTracker,
- mSecureSettings
+ mSecureSettings,
+ mQSSettingsPackageRepository
);
mTile.initialize();
@@ -144,6 +151,7 @@
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 c854920..ae4da9d 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.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.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 @@
@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 @@
keyguardStateController,
mDialogTransitionAnimator,
{ fontScalingDialogDelegate },
+ settingsPackageRepository,
)
fontScalingTile.initialize()
testableLooper.processAllMessages()
@@ -120,7 +125,7 @@
@Test
fun clickTile_screenUnlocked_showDialogAnimationFromView() {
- `when`(keyguardStateController.isShowing).thenReturn(false)
+ whenever(keyguardStateController.isShowing).thenReturn(false)
fontScalingTile.click(expandable)
testableLooper.processAllMessages()
@@ -130,7 +135,7 @@
eq(null),
eq(true),
eq(true),
- eq(false)
+ eq(false),
)
argumentCaptor.value.run()
verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
@@ -138,7 +143,7 @@
@Test
fun clickTile_onLockScreen_neverShowDialogAnimationFromView() {
- `when`(keyguardStateController.isShowing).thenReturn(true)
+ whenever(keyguardStateController.isShowing).thenReturn(true)
fontScalingTile.click(expandable)
testableLooper.processAllMessages()
@@ -148,7 +153,7 @@
eq(null),
eq(true),
eq(true),
- eq(false)
+ eq(false),
)
argumentCaptor.value.run()
verify(mDialogTransitionAnimator, never()).show(any(), any(), anyBoolean())
@@ -159,5 +164,10 @@
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 5c6657b..cfbc812 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 @@
@Mock
private WifiEntry mWifiEntry;
@Mock
- private InternetDialogController mInternetDialogController;
+ private InternetDetailsContentController mInternetDetailsContentController;
@Mock
private Drawable mWifiDrawable;
@Mock
@@ -86,7 +86,7 @@
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 @@
@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 @@
@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 @@
mViewHolder.onWifiClick(mWifiEntry, mock(View.class));
- verify(mInternetDialogController).startActivityForDialog(any());
+ verify(mInternetDetailsContentController).startActivityForDialog(any());
verify(mSpyContext, never()).startActivity(any());
}
@@ -242,7 +242,7 @@
mViewHolder.onWifiClick(mWifiEntry, mock(View.class));
- verify(mInternetDialogController).connect(mWifiEntry);
+ verify(mInternetDetailsContentController).connect(mWifiEntry);
}
@Test
@@ -252,7 +252,7 @@
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 3bc53b27..0cf3734 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.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 @@
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 d309554..9bd4895 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.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.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.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 @@
@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 @@
eq(null),
eq(true),
eq(true),
- eq(false)
+ eq(false),
)
argumentCaptor.value.run()
verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
@@ -114,7 +122,7 @@
@Test
fun clickTile_onLockScreen_neverShowDialogAnimationFromView_butShowsDialog() =
- kosmos.testScope.runTest {
+ scope.runTest {
keyguardStateController.isShowing = true
underTest.handleInput(click(FontScalingTileModel, expandable = expandable))
@@ -125,7 +133,7 @@
eq(null),
eq(true),
eq(true),
- eq(false)
+ eq(false),
)
argumentCaptor.value.run()
verify(mDialogTransitionAnimator, never()).show(any(), any(), anyBoolean())
@@ -134,17 +142,20 @@
@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 f574f79..3f77b86 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.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 @@
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 0000000..e68045f
--- /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 009b33b..3515c56 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 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 @@
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 18eef33..884c35c 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 @@
@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 @@
@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 @@
@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 a2c3c66..f37648a 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 @@
@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 @@
@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 4a26fdf..e0a1f27 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 @@
@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 @@
@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 @@
@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 a9920ec5..11fd902 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 @@
@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 @@
@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 e65c04c..3cc592c 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 @@
@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 @@
@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 d38fb50..5f154ac 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.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 @@
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 @@
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 be20bc1..d86c6ef 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 @@
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 @@
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 @@
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 87abd0a..256da253 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.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 @@
@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 @@
@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 @@
@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 @@
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 f4c2545..216f51d 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.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 @@
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 90506a1..d163726 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 @@
@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 @@
@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 2d9880a..659d91a 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 @@
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 @@
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 @@
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 7a9d017..769f012 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 @@
@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 @@
@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 8972f3e..8b526bb 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.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 bad5711..3270759 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 6994a55..11327b6 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 41b9d33..5f0acfa 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 @@
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.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 @@
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 @@
AccessibilityManager accessibilityManager,
AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
AccessibilityButtonModeObserver accessibilityButtonModeObserver,
+ @Nullable HearingAidDeviceManager hearingAidDeviceManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecureSettings secureSettings,
DisplayTracker displayTracker,
@@ -119,6 +123,7 @@
mAccessibilityManager = accessibilityManager;
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+ mHearingAidDeviceManager = hearingAidDeviceManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mSecureSettings = secureSettings;
mDisplayTracker = displayTracker;
@@ -201,7 +206,7 @@
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 ffb5f3d..121b51f 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 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 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 @@
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 @@
};
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 @@
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 @@
}
private void onTargetFeaturesChanged() {
- mSettingsContentsCallback.onTargetFeaturesChanged(
- getTargets(mContext, SOFTWARE));
+ List<AccessibilityTarget> targets = getTargets(mContext, SOFTWARE);
+ mSettingsContentsCallback.onTargetFeaturesChanged(targets);
}
private Position getStartPosition() {
@@ -269,6 +285,24 @@
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 @@
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 @@
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 9d9e7df..23fc546 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 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 @@
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 @@
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 @@
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 @@
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 cb96e78..184518a 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.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 @@
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 f924784..8b7d6a1 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 @@
* 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 @@
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 @@
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 @@
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 @@
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 14626e1..e72dfad 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.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 @@
@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 f310b30..3390640 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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
)
} 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 @@
}
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 711534f..564588c 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 @@
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 @@
* 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 @@
* @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 @@
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 22e467b..99c9ca9 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 @@
*
* @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 @@
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 c895732..f9df676 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 @@
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 @@
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 @@
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 @@
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 ffeec4e..c302cb2 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 @@
@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 9677d47..3b3fb9b 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.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 @@
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 0000000..592e9da
--- /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 c2e609d..1f93681 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.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 @@
@Nullable
private Icon mIcon = null;
private final UserSettingObserver mSetting;
+ private final QSSettingsPackageRepository mQSSettingsPackageRepository;
@Inject
public ColorCorrectionTile(
@@ -66,11 +68,13 @@
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 @@
@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 ce80133..38e9a20 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.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 static final String TILE_SPEC = "inversion";
private final UserSettingObserver mSetting;
+ private final QSSettingsPackageRepository mQSSettingsPackageRepository;
@Inject
public ColorInversionTile(
@@ -64,11 +66,13 @@
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 @@
@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 43e84a0..4050f2a 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.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 @@
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 @@
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 7516ca0..b21c3e4 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
index 7036ef91..23210ef 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 @@
/**
* 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 @@
}
@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 @@
FeatureFlags featureFlags
) {
if (DEBUG) {
- Log.d(TAG, "Init InternetDialogController");
+ Log.d(TAG, "Init InternetDetailsContentController");
}
mHandler = handler;
mWorkerHandler = workerHandler;
@@ -1108,13 +1109,13 @@
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 @@
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
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java
index 5e9deec..82367eb 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 @@
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 @@
/**
* 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 @@
@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 @@
@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 @@
}
@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 @@
KeyguardStateController keyguardStateController,
SystemUIDialog.Factory systemUIDialogFactory,
ShadeDialogContextInteractor shadeDialogContextInteractor) {
+ // TODO: b/377388104 QsDetailedView.assertInLegacyMode();
+
mAboveStatusBar = aboveStatusBar;
mSystemUIDialogFactory = systemUIDialogFactory;
mShadeDialogContextInteractor = shadeDialogContextInteractor;
@@ -218,8 +220,8 @@
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 @@
mCoroutineScope = coroutineScope;
mUiEventLogger = uiEventLogger;
mDialogTransitionAnimator = dialogTransitionAnimator;
- mAdapter = new InternetAdapter(mInternetDialogController, coroutineScope);
+ mAdapter = new InternetAdapter(mInternetDetailsContentController, coroutineScope);
}
@Override
@@ -309,7 +311,8 @@
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 @@
mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
- mInternetDialogController.onStart(this, mCanConfigWifi);
+ mInternetDetailsContentController.onStart(this, mCanConfigWifi);
if (!mCanConfigWifi) {
hideWifiViews();
}
@@ -356,7 +359,7 @@
mDoneButton.setOnClickListener(null);
mShareWifiButton.setOnClickListener(null);
mAirplaneModeButton.setOnClickListener(null);
- mInternetDialogController.onStop();
+ mInternetDetailsContentController.onStop();
mInternetDialogManager.destroyDialog();
}
@@ -413,18 +416,20 @@
internetContent.mInternetDialogSubTitle = getSubtitleText();
if (shouldUpdateMobileNetwork) {
internetContent.mActiveNetworkIsCellular =
- mInternetDialogController.activeNetworkIsCellular();
+ mInternetDetailsContentController.activeNetworkIsCellular();
internetContent.mIsCarrierNetworkActive =
- mInternetDialogController.isCarrierNetworkActive();
+ mInternetDetailsContentController.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();
+ 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 @@
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 @@
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 @@
});
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 @@
}
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 @@
}
} 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
.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 @@
})
.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 @@
@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 f674971..8a54648 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 @@
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 @@
}
/**
- * 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 @@
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 dfdec3b..b774643 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 @@
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 @@
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 6ab5796..0ebb51e 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.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 @@
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 @@
?.dialogTransitionController(
DialogCuj(
InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
- INTERACTION_JANK_TAG
+ INTERACTION_JANK_TAG,
)
)
?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show()
@@ -78,7 +80,7 @@
/* cancelAction= */ null,
/* dismissShade= */ true,
/* afterKeyguardGone= */ true,
- /* deferred= */ false
+ /* deferred= */ false,
)
}
}
@@ -86,6 +88,7 @@
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 aa83877..f783497 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 @@
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 @@
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 10ac2cf..0650f86 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 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 0f80e74..03a8d17 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.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 @@
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 201dc03..4edba27 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 @@
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 0000000..a747abb
--- /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 d24edda..d25ca28 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 @@
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 9e9a38e..b057fb0 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 @@
}
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 @@
// 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 4c54fc4..1e127ee 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 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 dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import java.lang.IllegalStateException
import javax.inject.Provider
/**
@@ -75,6 +76,8 @@
fun create(
statusBarWindowController: StatusBarWindowController,
statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository,
+ statusBarConfigurationController: StatusBarConfigurationController,
+ darkIconDispatcher: DarkIconDispatcher,
): StatusBarInitializer
}
}
@@ -84,6 +87,8 @@
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 @@
->
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,
)
+ .also { component ->
+ // CollapsedStatusBarFragment used to be responsible initializing
+ component.init()
- if (StatusBarConnectedDisplays.isEnabled) {
- statusBarModePerDisplayRepository.onStatusBarViewInitialized(component)
- } else {
- creationListeners.forEach { listener ->
- listener.onStatusBarViewInitialized(component)
+ 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 @@
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 @@
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 4f815c1..de6cd07 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.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 @@
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 @@
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 8183a48..041f3c8 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 @@
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 @@
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 e498755..c629d10 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 @@
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 bd61c44..d48c94b 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 @@
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 a1f5655..086cc99 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 @@
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 6cf2c73..38cea83 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 @@
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 e471b12..554c46f 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 @@
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 7760f58..ffc1255 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 @@
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 f2bb7b1..4b9721e 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 @@
}
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 9928ac6..f7799bb 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_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 @@
// 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 @@
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 227a1fe..eb55856 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 @@
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 2ba28a6..e103282 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 @@
.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 64d1654..5106ccc 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 @@
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 @@
/**
* @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 @@
/**
* @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 @@
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 744f969..2ae38dd4 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 @@
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 ea67f1c..ca0c1ac9 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 @@
/** 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 394502b..031754d 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.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 @@
) {
/** 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 @@
/**
* 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 @@
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 @@
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 @@
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 4f32aaa26..037dda9 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.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 @@
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 49356eb..0464654 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 @@
@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 ed8b3e8..b15fffb 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 @@
@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 d257288..c31e34c5 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.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.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.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 @@
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 @@
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 @@
mStatusBarWindowStateController = statusBarWindowStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDemoModeController = demoModeController;
+ mStatusBarWindowControllerStore = statusBarWindowControllerStore;
+ mStatusBarConfigurationControllerStore = statusBarConfigurationControllerStore;
+ mDarkIconDispatcherStore = darkIconDispatcherStore;
}
private final DemoMode mDemoModeCallback = new DemoMode() {
@@ -337,8 +352,27 @@
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 @@
@Override
public void onResume() {
super.onResume();
+ if (mHomeStatusBarComponent == null) {
+ return;
+ }
mCommandQueue.addCallback(this);
mStatusBarStateController.addCallback(this);
initOngoingCallChip();
@@ -468,6 +505,9 @@
@Override
public void onPause() {
super.onPause();
+ if (mHomeStatusBarComponent == null) {
+ return;
+ }
mCommandQueue.removeCallback(this);
mStatusBarStateController.removeCallback(this);
if (!StatusBarRootModernization.isEnabled()) {
@@ -480,6 +520,9 @@
@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 f8ad0f2..5837752 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.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.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 @@
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 182f8d7..6a331b9 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 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 @@
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 c3299bb..7243ba7 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 @@
) {
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 @@
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 dcfbc5d..c9cc173 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.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 @@
} 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 811a2ec..848e91d 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 @@
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 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 7403161..f7688d2 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 @@
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 8bb0279..8733eeb 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 @@
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 @@
)
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 @@
}
drawerContainer.setTransitionListener(ringerDrawerTransitionListener)
volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate()
+ ringerHBackgroundView.background = ringerHBackgroundView.background.mutate()
viewModel.ringerViewModel
.mapLatest { ringerState ->
@@ -184,6 +192,8 @@
)
volumeDialogBackgroundView.background =
volumeDialogBackgroundView.background.mutate()
+ ringerHBackgroundView.background =
+ ringerHBackgroundView.background.mutate()
}
}
}
@@ -193,6 +203,9 @@
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 25ba1bd..69ffa38 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 @@
)
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 @@
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 51a7b5f..bc9d4c7 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.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 @@
private Lazy<ViewCapture> mLazyViewCapture;
@Mock
private NavigationModeController mNavigationModeController;
+ @Mock
+ private HearingAidDeviceManager mHearingAidDeviceManager;
@Before
public void setUp() throws Exception {
@@ -170,7 +173,7 @@
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController);
+ mNavigationModeController, mHearingAidDeviceManager);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
@@ -198,7 +201,7 @@
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController);
+ mNavigationModeController, mHearingAidDeviceManager);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserSwitching(fakeUserId);
@@ -213,7 +216,7 @@
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController);
+ mNavigationModeController, mHearingAidDeviceManager);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
mKeyguardCallback.onKeyguardVisibilityChanged(true);
@@ -368,9 +371,9 @@
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 dddaabb..856c379 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.verifyNoMoreInteractions;
import android.graphics.PointF;
-
import android.testing.TestableLooper;
import android.view.View;
import android.view.ViewPropertyAnimator;
@@ -40,6 +39,7 @@
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 @@
@Mock
private AccessibilityManager mAccessibilityManager;
+ @Mock
+ private HearingAidDeviceManager mHearingAidDeviceManager;
@Before
public void setUp() throws Exception {
@@ -82,7 +84,7 @@
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 400b3b3..33cfb38 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 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 @@
@Mock
private AccessibilityManager mStubAccessibilityManager;
@Mock
+ private HearingAidDeviceManager mHearingAidDeviceManager;
+ @Mock
private PackageManager mMockPackageManager;
private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings();
@@ -160,7 +163,7 @@
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 @@
@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 @@
@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
similarity index 79%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java
index 782b248..e4a49530 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 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 @@
@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 @@
@Mock
private WifiUtils.InternetIconInjector mWifiIconInjector;
@Mock
- InternetDialogController.InternetDialogCallback mInternetDialogCallback;
+ InternetDetailsContentController.InternetDialogCallback mInternetDialogCallback;
@Mock
private ViewCaptureAwareWindowManager mWindowManager;
@Mock
@@ -189,7 +189,7 @@
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 @@
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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 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 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 void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
fakeAirplaneModeEnabled(true);
- assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+ assertTrue(TextUtils.equals(mInternetDetailsContentController.getDialogTitleText(),
getResourcesString("airplane_mode")));
}
@@ -366,7 +364,7 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
@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 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 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 @@
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 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 @@
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 @@
@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 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 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 @@
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+ mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
mWifiEntries.clear();
verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
@@ -632,7 +634,7 @@
mAccessPoints.clear();
mAccessPoints.add(mWifiEntry1);
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+ mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
@@ -647,7 +649,7 @@
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+ mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
@@ -663,7 +665,7 @@
mAccessPoints.add(mWifiEntry1);
mAccessPoints.add(mWifiEntry2);
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+ mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
@@ -681,7 +683,7 @@
mAccessPoints.add(mWifiEntry2);
mAccessPoints.add(mWifiEntry3);
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+ mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
@@ -699,7 +701,7 @@
mAccessPoints.add(mWifiEntry3);
mAccessPoints.add(mWifiEntry4);
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+ mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
@@ -718,7 +720,7 @@
when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
mAccessPoints.add(mWifiEntry1);
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+ mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
@@ -735,9 +737,10 @@
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 @@
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 @@
reset(mInternetDialogCallback);
when(mWifiStateWorker.isWifiEnabled()).thenReturn(false);
- mInternetDialogController.onWifiScan(true);
+ mInternetDetailsContentController.onWifiScan(true);
verify(mInternetDialogCallback).onWifiScan(false);
}
@@ -772,7 +776,7 @@
reset(mInternetDialogCallback);
when(mKeyguardStateController.isUnlocked()).thenReturn(false);
- mInternetDialogController.onWifiScan(true);
+ mInternetDetailsContentController.onWifiScan(true);
verify(mInternetDialogCallback).onWifiScan(false);
}
@@ -781,7 +785,7 @@
public void onWifiScan_onWifiScanFalse_callbackOnWifiScanFalse() {
reset(mInternetDialogCallback);
- mInternetDialogController.onWifiScan(false);
+ mInternetDetailsContentController.onWifiScan(false);
verify(mInternetDialogCallback).onWifiScan(false);
}
@@ -790,7 +794,7 @@
public void onWifiScan_onWifiScanTrue_callbackOnWifiScanTrue() {
reset(mInternetDialogCallback);
- mInternetDialogController.onWifiScan(true);
+ mInternetDetailsContentController.onWifiScan(true);
verify(mInternetDialogCallback).onWifiScan(true);
}
@@ -800,7 +804,7 @@
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 @@
.thenReturn(false);
when(mAccessPointController.getMergedCarrierEntry()).thenReturn(null);
- mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
+ mInternetDetailsContentController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
}
@Test
@@ -819,11 +823,11 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
// 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 @@
// 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 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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/InternetDialogDelegateLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java
new file mode 100644
index 0000000..2385515
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java
@@ -0,0 +1,841 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.view.Window;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
+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.time.FakeSystemClock;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+
+import java.util.List;
+
+import kotlinx.coroutines.CoroutineScope;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@UiThreadTest
+public class InternetDialogDelegateLegacyTest extends SysuiTestCase {
+
+ private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
+ private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
+ private static final String WIFI_TITLE = "Connected Wi-Fi Title";
+ private static final String WIFI_SUMMARY = "Connected Wi-Fi Summary";
+
+ @Mock
+ private Handler mHandler;
+ @Mock
+ CoroutineScope mScope;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private WifiEntry mInternetWifiEntry;
+ @Mock
+ private List<WifiEntry> mWifiEntries;
+ @Mock
+ private InternetAdapter mInternetAdapter;
+ @Mock
+ private InternetDetailsContentController mInternetDetailsContentController;
+ @Mock
+ private KeyguardStateController mKeyguard;
+ @Mock
+ private DialogTransitionAnimator mDialogTransitionAnimator;
+ @Mock
+ private SystemUIDialog.Factory mSystemUIDialogFactory;
+ @Mock
+ private SystemUIDialog mSystemUIDialog;
+ @Mock
+ private Window mWindow;
+
+ private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
+ private InternetDialogDelegateLegacy mInternetDialogDelegateLegacy;
+ private View mDialogView;
+ private View mSubTitle;
+ private LinearLayout mEthernet;
+ private LinearLayout mMobileDataLayout;
+ private Switch mMobileToggleSwitch;
+ private LinearLayout mWifiToggle;
+ private Switch mWifiToggleSwitch;
+ private TextView mWifiToggleSummary;
+ private LinearLayout mConnectedWifi;
+ private RecyclerView mWifiList;
+ private LinearLayout mSeeAll;
+ private LinearLayout mWifiScanNotify;
+ private TextView mAirplaneModeSummaryText;
+
+ private MockitoSession mMockitoSession;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+ when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+ when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+ when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true);
+ when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true);
+ when(mWifiEntries.size()).thenReturn(1);
+
+ when(mInternetDetailsContentController.getMobileNetworkTitle(anyInt()))
+ .thenReturn(MOBILE_NETWORK_TITLE);
+ when(mInternetDetailsContentController.getMobileNetworkSummary(anyInt()))
+ .thenReturn(MOBILE_NETWORK_SUMMARY);
+ when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(true);
+ when(mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId()).thenReturn(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mMockitoSession = ExtendedMockito.mockitoSession()
+ .spyStatic(WifiEnterpriseRestrictionUtils.class)
+ .startMocking();
+ when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
+ when(mSystemUIDialogFactory.create(any(SystemUIDialog.Delegate.class), eq(mContext)))
+ .thenReturn(mSystemUIDialog);
+ when(mSystemUIDialog.getContext()).thenReturn(mContext);
+ when(mSystemUIDialog.getWindow()).thenReturn(mWindow);
+ createInternetDialog();
+ }
+
+ private void createInternetDialog() {
+ mInternetDialogDelegateLegacy = new InternetDialogDelegateLegacy(
+ mContext,
+ mock(InternetDialogManager.class),
+ mInternetDetailsContentController,
+ true,
+ true,
+ true,
+ mScope,
+ mock(UiEventLogger.class),
+ mDialogTransitionAnimator,
+ mHandler,
+ mBgExecutor,
+ mKeyguard,
+ mSystemUIDialogFactory,
+ new FakeShadeDialogContextInteractor(mContext));
+ mInternetDialogDelegateLegacy.createDialog();
+ mInternetDialogDelegateLegacy.onCreate(mSystemUIDialog, null);
+ mInternetDialogDelegateLegacy.mAdapter = mInternetAdapter;
+ mInternetDialogDelegateLegacy.mConnectedWifiEntry = mInternetWifiEntry;
+ mInternetDialogDelegateLegacy.mWifiEntriesCount = mWifiEntries.size();
+
+ 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);
+ mMobileToggleSwitch = mDialogView.requireViewById(R.id.mobile_toggle);
+ mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
+ mWifiToggleSwitch = mDialogView.requireViewById(R.id.wifi_toggle);
+ mWifiToggleSummary = mDialogView.requireViewById(R.id.wifi_toggle_summary);
+ mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
+ mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout);
+ 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);
+ mInternetDialogDelegateLegacy.onStart(mSystemUIDialog);
+ }
+
+ @After
+ public void tearDown() {
+ mInternetDialogDelegateLegacy.onStop(mSystemUIDialog);
+ mInternetDialogDelegateLegacy.dismissDialog();
+ mMockitoSession.finishMocking();
+ }
+
+ @Test
+ public void createInternetDialog_setAccessibilityPaneTitleToQuickSettings() {
+ assertThat(mDialogView.getAccessibilityPaneTitle())
+ .isEqualTo(mContext.getText(R.string.accessibility_desc_quick_settings));
+ }
+
+ @Test
+ public void hideWifiViews_WifiViewsGone() {
+ mInternetDialogDelegateLegacy.hideWifiViews();
+
+ assertThat(mInternetDialogDelegateLegacy.mIsProgressBarVisible).isFalse();
+ assertThat(mWifiToggle.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_withApmOn_internetDialogSubTitleGone() {
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOffAndHasEthernet_showEthernet() {
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDetailsContentController.hasEthernet()).thenReturn(true);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOffAndNoEthernet_hideEthernet() {
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDetailsContentController.hasEthernet()).thenReturn(false);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOnAndHasEthernet_showEthernet() {
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mInternetDetailsContentController.hasEthernet()).thenReturn(true);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOnAndNoEthernet_hideEthernet() {
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mInternetDetailsContentController.hasEthernet()).thenReturn(false);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOffAndNotCarrierNetwork_mobileDataLayoutGone() {
+ // Mobile network should be gone if the list of active subscriptionId is null.
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false);
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(false);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutVisible() {
+ // Carrier network should be visible if airplane mode ON and Wi-Fi is ON.
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(true);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutGone() {
+ // Carrier network should be gone if airplane mode ON and Wi-Fi is off.
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOnAndNoCarrierNetwork_mobileDataLayoutGone() {
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false);
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOnAndWifiOnHasCarrierNetwork_showAirplaneSummary() {
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+ mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+ doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular();
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOffAndWifiOnHasCarrierNetwork_notShowApmSummary() {
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+ mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+ doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular();
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOffAndHasCarrierNetwork_notShowApmSummary() {
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_apmOnAndNoCarrierNetwork_notShowApmSummary() {
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false);
+ when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_mobileDataIsEnabled_checkMobileDataSwitch() {
+ doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds();
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDetailsContentController.isMobileDataEnabled()).thenReturn(true);
+ mMobileToggleSwitch.setChecked(false);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mMobileToggleSwitch.isChecked()).isTrue();
+ });
+ }
+
+ @Test
+ public void updateDialog_mobileDataIsNotChanged_checkMobileDataSwitch() {
+ doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds();
+ when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDetailsContentController.isMobileDataEnabled()).thenReturn(false);
+ mMobileToggleSwitch.setChecked(false);
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mMobileToggleSwitch.isChecked()).isFalse();
+ });
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
+ when(mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1);
+ doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds();
+ // The preconditions WiFi ON and Internet WiFi are already in setUp()
+ doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular();
+
+ mInternetDialogDelegateLegacy.updateDialog(true);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+ LinearLayout secondaryLayout = mDialogView.requireViewById(
+ R.id.secondary_mobile_network_layout);
+ assertThat(secondaryLayout.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndNoConnectedWifi_hideConnectedWifi() {
+ // The precondition WiFi ON is already in setUp()
+ mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+ doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular();
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
+ // The precondition WiFi ON is already in setUp()
+ mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+ mInternetDialogDelegateLegacy.mWifiEntriesCount = 0;
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ 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);
+ verify(mInternetAdapter).setMaxEntriesCount(3);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() {
+ // The precondition WiFi ON is already in setUp()
+ mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+ mInternetDialogDelegateLegacy.mWifiEntriesCount = 1;
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ 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);
+ verify(mInternetAdapter).setMaxEntriesCount(3);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ mInternetDialogDelegateLegacy.mWifiEntriesCount = 0;
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ 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);
+ verify(mInternetAdapter).setMaxEntriesCount(2);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndHasMaxWifiList_showWifiListAndSeeAll() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+ mInternetDialogDelegateLegacy.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ mInternetDialogDelegateLegacy.mHasMoreWifiEntries = true;
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(3);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ mInternetDialogDelegateLegacy.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
+ mInternetDialogDelegateLegacy.mHasMoreWifiEntries = true;
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(2);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+ });
+ }
+
+ @Test
+ public void updateDialog_deviceLockedAndNoConnectedWifi_showWifiToggle() {
+ // The preconditions WiFi entries are already in setUp()
+ when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(true);
+ mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ // Show WiFi Toggle without background
+ assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mWifiToggle.getBackground()).isNull();
+ // Hide Wi-Fi networks and See all
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_deviceLockedAndHasConnectedWifi_showWifiToggleWithBackground() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+ when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(true);
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ // Show WiFi Toggle with highlight background
+ assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mWifiToggle.getBackground()).isNotNull();
+ // Hide Wi-Fi networks and See all
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_disallowChangeWifiState_disableWifiSwitch() {
+ mInternetDialogDelegateLegacy.dismissDialog();
+ when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(false);
+ createInternetDialog();
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ 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);
+ assertThat(mWifiToggleSummary.getText().length()).isNotEqualTo(0);
+ });
+ }
+
+ @Test
+ public void updateDialog_allowChangeWifiState_enableWifiSwitch() {
+ mInternetDialogDelegateLegacy.dismissDialog();
+ when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
+ createInternetDialog();
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ 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);
+ });
+ }
+
+ @Test
+ public void updateDialog_showSecondaryDataSub() {
+ 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();
+
+ 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(mInternetDetailsContentController).getMobileNetworkSummary(1);
+ assertThat(primaryLayout.getBackground()).isNotEqualTo(
+ secondaryLayout.getBackground());
+ });
+ }
+
+ @Test
+ public void updateDialog_wifiOn_hideWifiScanNotify() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+ });
+
+ assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_wifiOffAndWifiScanOff_hideWifiScanNotify() {
+ when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false);
+ when(mInternetDetailsContentController.isWifiScanEnabled()).thenReturn(false);
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+ });
+
+ assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() {
+ when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false);
+ when(mInternetDetailsContentController.isWifiScanEnabled()).thenReturn(true);
+ when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(true);
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+ });
+
+ assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() {
+ when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false);
+ when(mInternetDetailsContentController.isWifiScanEnabled()).thenReturn(true);
+ when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(false);
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.VISIBLE);
+ TextView wifiScanNotifyText = mDialogView.requireViewById(
+ R.id.wifi_scan_notify_text);
+ assertThat(wifiScanNotifyText.getText().length()).isNotEqualTo(0);
+ assertThat(wifiScanNotifyText.getMovementMethod()).isNotNull();
+ });
+ }
+
+ @Test
+ public void updateDialog_wifiIsDisabled_uncheckWifiSwitch() {
+ when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false);
+ mWifiToggleSwitch.setChecked(true);
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mWifiToggleSwitch.isChecked()).isFalse();
+ });
+ }
+
+ @Test
+ public void updateDialog_wifiIsEnabled_checkWifiSwitch() throws Exception {
+ when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(true);
+ mWifiToggleSwitch.setChecked(false);
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mWifiToggleSwitch.isChecked()).isTrue();
+ });
+ }
+
+ @Test
+ public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
+ mSeeAll.performClick();
+
+ verify(mInternetDetailsContentController).launchNetworkSetting(
+ mDialogView.requireViewById(R.id.see_all_layout));
+ }
+
+ @Test
+ public void onWifiScan_isScanTrue_setProgressBarVisibleTrue() {
+ mInternetDialogDelegateLegacy.mIsProgressBarVisible = false;
+
+ mInternetDialogDelegateLegacy.onWifiScan(true);
+
+ assertThat(mInternetDialogDelegateLegacy.mIsProgressBarVisible).isTrue();
+ }
+
+ @Test
+ public void onWifiScan_isScanFalse_setProgressBarVisibleFalse() {
+ mInternetDialogDelegateLegacy.mIsProgressBarVisible = true;
+
+ mInternetDialogDelegateLegacy.onWifiScan(false);
+
+ assertThat(mInternetDialogDelegateLegacy.mIsProgressBarVisible).isFalse();
+ }
+
+ @Test
+ public void getWifiListMaxCount_returnCountCorrectly() {
+ // Both of the Ethernet, MobileData is hidden.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
+ setNetworkVisible(false, false, false);
+
+ 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(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(mInternetDialogDelegateLegacy.getWifiListMaxCount()).isEqualTo(
+ MAX_WIFI_ENTRY_COUNT);
+
+ setNetworkVisible(false, true, false);
+
+ 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(mInternetDialogDelegateLegacy.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ setNetworkVisible(false, true, true);
+
+ 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(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(mInternetDialogDelegateLegacy.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+ }
+
+ @Test
+ public void updateDialog_shareWifiIntentNull_hideButton() {
+ when(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
+ .thenReturn(null);
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(
+ mInternetDialogDelegateLegacy.mShareWifiButton.getVisibility())
+ .isEqualTo(View.GONE);
+ });
+ }
+
+ @Test
+ public void updateDialog_shareWifiShareable_showButton() {
+ when(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
+ .thenReturn(new Intent());
+ mInternetDialogDelegateLegacy.updateDialog(false);
+ mBgExecutor.runAllReady();
+
+ mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+ mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+ assertThat(mInternetDialogDelegateLegacy.mShareWifiButton.getVisibility())
+ .isEqualTo(View.VISIBLE);
+ });
+ }
+
+ private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
+ boolean connectedWifiVisible) {
+ mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
+ mMobileDataLayout.setVisibility(mobileDataVisible ? View.VISIBLE : View.GONE);
+ mConnectedWifi.setVisibility(connectedWifiVisible ? View.VISIBLE : View.GONE);
+ }
+}
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/InternetDialogDelegateTest.java
deleted file mode 100644
index 8560b67..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
+++ /dev/null
@@ -1,837 +0,0 @@
-package com.android.systemui.qs.tiles.dialog;
-
-import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Intent;
-import android.os.Handler;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.testing.TestableLooper;
-import android.view.View;
-import android.view.Window;
-import android.widget.LinearLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.internal.logging.UiEventLogger;
-import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.DialogTransitionAnimator;
-import com.android.systemui.res.R;
-import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
-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.time.FakeSystemClock;
-import com.android.wifitrackerlib.WifiEntry;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoSession;
-
-import java.util.List;
-
-import kotlinx.coroutines.CoroutineScope;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@UiThreadTest
-public class InternetDialogDelegateTest extends SysuiTestCase {
-
- private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
- private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
- private static final String WIFI_TITLE = "Connected Wi-Fi Title";
- private static final String WIFI_SUMMARY = "Connected Wi-Fi Summary";
-
- @Mock
- private Handler mHandler;
- @Mock
- CoroutineScope mScope;
- @Mock
- private TelephonyManager mTelephonyManager;
- @Mock
- private WifiEntry mInternetWifiEntry;
- @Mock
- private List<WifiEntry> mWifiEntries;
- @Mock
- private InternetAdapter mInternetAdapter;
- @Mock
- private InternetDialogController mInternetDialogController;
- @Mock
- private KeyguardStateController mKeyguard;
- @Mock
- private DialogTransitionAnimator mDialogTransitionAnimator;
- @Mock
- private SystemUIDialog.Factory mSystemUIDialogFactory;
- @Mock
- private SystemUIDialog mSystemUIDialog;
- @Mock
- private Window mWindow;
-
- private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
- private InternetDialogDelegate mInternetDialogDelegate;
- private View mDialogView;
- private View mSubTitle;
- private LinearLayout mEthernet;
- private LinearLayout mMobileDataLayout;
- private Switch mMobileToggleSwitch;
- private LinearLayout mWifiToggle;
- private Switch mWifiToggleSwitch;
- private TextView mWifiToggleSummary;
- private LinearLayout mConnectedWifi;
- private RecyclerView mWifiList;
- private LinearLayout mSeeAll;
- private LinearLayout mWifiScanNotify;
- private TextView mAirplaneModeSummaryText;
-
- private MockitoSession mMockitoSession;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
- when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
- when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
- when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true);
- when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true);
- when(mWifiEntries.size()).thenReturn(1);
-
- when(mInternetDialogController.getMobileNetworkTitle(anyInt()))
- .thenReturn(MOBILE_NETWORK_TITLE);
- when(mInternetDialogController.getMobileNetworkSummary(anyInt()))
- .thenReturn(MOBILE_NETWORK_SUMMARY);
- when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
- when(mInternetDialogController.getActiveAutoSwitchNonDdsSubId()).thenReturn(
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- mMockitoSession = ExtendedMockito.mockitoSession()
- .spyStatic(WifiEnterpriseRestrictionUtils.class)
- .startMocking();
- when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
- when(mSystemUIDialogFactory.create(any(SystemUIDialog.Delegate.class), eq(mContext)))
- .thenReturn(mSystemUIDialog);
- when(mSystemUIDialog.getContext()).thenReturn(mContext);
- when(mSystemUIDialog.getWindow()).thenReturn(mWindow);
- createInternetDialog();
- }
-
- private void createInternetDialog() {
- mInternetDialogDelegate = new InternetDialogDelegate(
- mContext,
- mock(InternetDialogManager.class),
- mInternetDialogController,
- true,
- true,
- true,
- mScope,
- mock(UiEventLogger.class),
- mDialogTransitionAnimator,
- mHandler,
- mBgExecutor,
- mKeyguard,
- mSystemUIDialogFactory,
- new FakeShadeDialogContextInteractor(mContext));
- mInternetDialogDelegate.createDialog();
- mInternetDialogDelegate.onCreate(mSystemUIDialog, null);
- mInternetDialogDelegate.mAdapter = mInternetAdapter;
- mInternetDialogDelegate.mConnectedWifiEntry = mInternetWifiEntry;
- mInternetDialogDelegate.mWifiEntriesCount = mWifiEntries.size();
-
- mDialogView = mInternetDialogDelegate.mDialogView;
- mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
- mEthernet = mDialogView.requireViewById(R.id.ethernet_layout);
- mMobileDataLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
- mMobileToggleSwitch = mDialogView.requireViewById(R.id.mobile_toggle);
- mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
- mWifiToggleSwitch = mDialogView.requireViewById(R.id.wifi_toggle);
- mWifiToggleSummary = mDialogView.requireViewById(R.id.wifi_toggle_summary);
- mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
- mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout);
- 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);
- }
-
- @After
- public void tearDown() {
- mInternetDialogDelegate.onStop(mSystemUIDialog);
- mInternetDialogDelegate.dismissDialog();
- mMockitoSession.finishMocking();
- }
-
- @Test
- public void createInternetDialog_setAccessibilityPaneTitleToQuickSettings() {
- assertThat(mDialogView.getAccessibilityPaneTitle())
- .isEqualTo(mContext.getText(R.string.accessibility_desc_quick_settings));
- }
-
- @Test
- public void hideWifiViews_WifiViewsGone() {
- mInternetDialogDelegate.hideWifiViews();
-
- assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isFalse();
- assertThat(mWifiToggle.getVisibility()).isEqualTo(View.GONE);
- assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
- public void updateDialog_withApmOn_internetDialogSubTitleGone() {
- when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
- mInternetDialogDelegate.updateDialog(true);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
- });
- }
-
- @Test
- public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
- when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
- mInternetDialogDelegate.updateDialog(true);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
- });
- }
-
- @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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
- });
- }
-
- @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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
- });
- }
-
- @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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
- });
- }
-
- @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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- mMobileToggleSwitch.setChecked(false);
- mInternetDialogDelegate.updateDialog(true);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- mMobileToggleSwitch.setChecked(false);
- mInternetDialogDelegate.updateDialog(true);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mMobileToggleSwitch.isChecked()).isFalse();
- });
- }
-
- @Test
- public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
- when(mInternetDialogController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1);
- doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds();
- // The preconditions WiFi ON and Internet WiFi are already in setUp()
- doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
-
- mInternetDialogDelegate.updateDialog(true);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
- LinearLayout secondaryLayout = mDialogView.requireViewById(
- R.id.secondary_mobile_network_layout);
- assertThat(secondaryLayout.getVisibility()).isEqualTo(View.GONE);
- });
- }
-
- @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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
- });
- }
-
- @Test
- public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
- // The precondition WiFi ON is already in setUp()
- mInternetDialogDelegate.mConnectedWifiEntry = null;
- mInternetDialogDelegate.mWifiEntriesCount = 0;
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- verify(mInternetAdapter).setMaxEntriesCount(3);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
- });
- }
-
- @Test
- public void updateDialog_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() {
- // The precondition WiFi ON is already in setUp()
- mInternetDialogDelegate.mConnectedWifiEntry = null;
- mInternetDialogDelegate.mWifiEntriesCount = 1;
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- verify(mInternetAdapter).setMaxEntriesCount(3);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
- });
- }
-
- @Test
- public void updateDialog_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
- // The preconditions WiFi ON and WiFi entries are already in setUp()
- mInternetDialogDelegate.mWifiEntriesCount = 0;
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- verify(mInternetAdapter).setMaxEntriesCount(2);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
- });
- }
-
- @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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
- verify(mInternetAdapter).setMaxEntriesCount(3);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
- });
- }
-
- @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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
- verify(mInternetAdapter).setMaxEntriesCount(2);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
- });
- }
-
- @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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- // Show WiFi Toggle without background
- assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mWifiToggle.getBackground()).isNull();
- // Hide Wi-Fi networks and See all
- assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
- });
- }
-
- @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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- // Show WiFi Toggle with highlight background
- assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mWifiToggle.getBackground()).isNotNull();
- // Hide Wi-Fi networks and See all
- assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
- });
- }
-
- @Test
- public void updateDialog_disallowChangeWifiState_disableWifiSwitch() {
- mInternetDialogDelegate.dismissDialog();
- when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(false);
- createInternetDialog();
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- // Disable Wi-Fi switch and show restriction message in summary.
- assertThat(mWifiToggleSwitch.isEnabled()).isFalse();
- assertThat(mWifiToggleSummary.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mWifiToggleSummary.getText().length()).isNotEqualTo(0);
- });
- }
-
- @Test
- public void updateDialog_allowChangeWifiState_enableWifiSwitch() {
- mInternetDialogDelegate.dismissDialog();
- when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
- createInternetDialog();
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- // Enable Wi-Fi switch and hide restriction message in summary.
- assertThat(mWifiToggleSwitch.isEnabled()).isTrue();
- assertThat(mWifiToggleSummary.getVisibility()).isEqualTo(View.GONE);
- });
- }
-
- @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);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.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);
- assertThat(primaryLayout.getBackground()).isNotEqualTo(
- secondaryLayout.getBackground());
- });
- }
-
- @Test
- public void updateDialog_wifiOn_hideWifiScanNotify() {
- // The preconditions WiFi ON and WiFi entries are already in setUp()
-
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
- });
-
- assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
- public void updateDialog_wifiOffAndWifiScanOff_hideWifiScanNotify() {
- when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
- when(mInternetDialogController.isWifiScanEnabled()).thenReturn(false);
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
- });
-
- assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
- public void updateDialog_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() {
- when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
- when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
- when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
- });
-
- assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
- public void updateDialog_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() {
- when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
- when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
- when(mInternetDialogController.isDeviceLocked()).thenReturn(false);
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.VISIBLE);
- TextView wifiScanNotifyText = mDialogView.requireViewById(
- R.id.wifi_scan_notify_text);
- assertThat(wifiScanNotifyText.getText().length()).isNotEqualTo(0);
- assertThat(wifiScanNotifyText.getMovementMethod()).isNotNull();
- });
- }
-
- @Test
- public void updateDialog_wifiIsDisabled_uncheckWifiSwitch() {
- when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
- mWifiToggleSwitch.setChecked(true);
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mWifiToggleSwitch.isChecked()).isFalse();
- });
- }
-
- @Test
- public void updateDialog_wifiIsEnabled_checkWifiSwitch() throws Exception {
- when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
- mWifiToggleSwitch.setChecked(false);
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mWifiToggleSwitch.isChecked()).isTrue();
- });
- }
-
- @Test
- public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
- mSeeAll.performClick();
-
- verify(mInternetDialogController).launchNetworkSetting(
- mDialogView.requireViewById(R.id.see_all_layout));
- }
-
- @Test
- public void onWifiScan_isScanTrue_setProgressBarVisibleTrue() {
- mInternetDialogDelegate.mIsProgressBarVisible = false;
-
- mInternetDialogDelegate.onWifiScan(true);
-
- assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isTrue();
- }
-
- @Test
- public void onWifiScan_isScanFalse_setProgressBarVisibleFalse() {
- mInternetDialogDelegate.mIsProgressBarVisible = true;
-
- mInternetDialogDelegate.onWifiScan(false);
-
- assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isFalse();
- }
-
- @Test
- public void getWifiListMaxCount_returnCountCorrectly() {
- // Both of the Ethernet, MobileData is hidden.
- // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
- setNetworkVisible(false, false, false);
-
- assertThat(mInternetDialogDelegate.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())
- .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);
-
- setNetworkVisible(false, true, false);
-
- assertThat(mInternetDialogDelegate.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())
- .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
-
- setNetworkVisible(false, true, true);
-
- assertThat(mInternetDialogDelegate.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())
- .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())
- .isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
- }
-
- @Test
- public void updateDialog_shareWifiIntentNull_hideButton() {
- when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
- .thenReturn(null);
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility()).isEqualTo(
- View.GONE);
- });
- }
-
- @Test
- public void updateDialog_shareWifiShareable_showButton() {
- when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
- .thenReturn(new Intent());
- mInternetDialogDelegate.updateDialog(false);
- mBgExecutor.runAllReady();
-
- mInternetDialogDelegate.mDataInternetContent.observe(
- mInternetDialogDelegate.mLifecycleOwner, i -> {
- assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility())
- .isEqualTo(View.VISIBLE);
- });
- }
-
- private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
- boolean connectedWifiVisible) {
- mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
- mMobileDataLayout.setVisibility(mobileDataVisible ? View.VISIBLE : View.GONE);
- mConnectedWifi.setVisibility(connectedWifiVisible ? View.VISIBLE : View.GONE);
- }
-}
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 2d35ea5..61943f2 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.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 @@
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 411c81d13..1fcf02d 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.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 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.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.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.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 javax.inject.Provider;
-import dagger.Lazy;
-import kotlinx.coroutines.test.TestScope;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
@@ -536,6 +539,8 @@
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 0b443675..3a99328 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.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.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.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 @@
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 @@
@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 @@
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 b8be6aa..64d89c5 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 @@
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 50a19a9..fb2e2a3 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 @@
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 6e99027..b8dafb2 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 @@
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 @@
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 0000000..2a2f5f9
--- /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 de9485d..dfc8a68 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 0000000..d595fa6
--- /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 ec54c33..cc3f21b 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.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 383e31d..65f4ec1 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.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 @@
/*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 a5c4bfd..67343c95 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 @@
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.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 e972c2c..64f16da 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.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 f86824a..d0bf584 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.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 @@
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 5b7f23b..9adaeff9 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.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 @@
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 6083414..7743a1c 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.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 @@
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 50b6990..c9f06ac 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 @@
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 aa06b7e..bfacfbb 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_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 @@
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 @@
}
}
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 @@
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 @@
// 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 @@
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 @@
}
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 @@
}
}
}
+ } 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 @@
}
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 @@
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 @@
}
}
- 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 d6e3d43..6ddf60b 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.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 @@
@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 @@
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 @@
pw.println(" Historical broadcasts [" + queueName + "]:");
printed = true;
}
- if (dumpAll) {
+ if (dumpIntentAction != null) {
pw.print(" Historical Broadcast " + queueName + " #");
- pw.print(i); pw.println(":");
+ 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(":");
r.dump(pw, " ", sdf);
} else {
pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
@@ -213,7 +226,7 @@
}
} while (ringIndex != lastIndex);
- if (dumpPackage == null) {
+ if (dumpPackage == null && dumpIntentAction == null) {
lastIndex = ringIndex = mSummaryHistoryNext;
if (dumpAll) {
printed = false;
@@ -276,15 +289,28 @@
}
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 6386af6..a7d74a9 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 @@
@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 @@
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 9e4666c..b270513 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 @@
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 @@
& 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 @@
}
}
- 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 @@
// 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 @@
@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 @@
}
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 8d0805d..c1b0a76 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 @@
}
@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 49149e1..1503d88 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 @@
}
@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 56cb6f49..febf24e 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 @@
(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 @@
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 ab5a680..7dbde64 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 @@
void setMouseSwapPrimaryButtonEnabled(boolean enabled);
+ void setMouseAccelerationEnabled(boolean enabled);
+
void setTouchpadPointerSpeed(int speed);
void setTouchpadNaturalScrollingEnabled(boolean enabled);
@@ -429,6 +431,9 @@
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 7375a68..f50e8aa 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 @@
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 @@
@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 @@
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 @@
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 @@
@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 @@
}
}
+ /**
+ * 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) {
- if (enabled) {
- mClassificationTypeDeniedPackages.remove(pkg);
- } else {
- mClassificationTypeDeniedPackages.add(pkg);
+ 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) {
+ enabledTypes.add(type);
+ } else {
+ enabledTypes.remove(type);
+ }
+ mClassificationTypePackagesEnabledTypes.put(pkg, enabledTypes);
}
}
@@ -12459,16 +12542,25 @@
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 @@
}
}
}
- } 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 65a38ae..f15c23e 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 @@
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 c4e4c42..1726f0d 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 @@
}
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 @@
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 @@
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 @@
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 66b77b9..4c2d849 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.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 @@
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 @@
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 @@
(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 @@
* 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 cba606c..98ed6f7 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 @@
// 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 7276007..d1585d0 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 @@
}
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 ce85184..9df65f6 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 @@
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 813fec1..f634beb 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 @@
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 @@
// 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 @@
outConfig->mouseReverseVerticalScrollingEnabled =
mLocked.mouseReverseVerticalScrollingEnabled;
outConfig->mouseSwapPrimaryButtonEnabled = mLocked.mouseSwapPrimaryButtonEnabled;
+ outConfig->mousePointerAccelerationEnabled = mLocked.mousePointerAccelerationEnabled;
outConfig->touchpadPointerSpeed = mLocked.touchpadPointerSpeed;
outConfig->touchpadNaturalScrollingEnabled = mLocked.touchpadNaturalScrollingEnabled;
@@ -1458,6 +1463,21 @@
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 @@
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 @@
{"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 ea80f28..db6aeeb 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 @@
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 @@
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 bd15bd0..cd94c0f 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 @@
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 @@
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 @@
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 @@
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 c7a06b8..b1df0f1 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.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 @@
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 @@
@NonNull
@Override
public AdvancedProtectionFeature getFeature() {
- return mTestFeature2g;
+ return mFeature;
}
@Override
@@ -137,7 +135,7 @@
@NonNull
@Override
public AdvancedProtectionFeature getFeature() {
- return mTestFeature2g;
+ return mFeature;
}
@Override
@@ -167,7 +165,7 @@
@NonNull
@Override
public AdvancedProtectionFeature getFeature() {
- return mTestFeature2g;
+ return mFeature;
}
@Override
@@ -240,10 +238,8 @@
@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 @@
@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 d1dc8d6..4f5cdb7 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.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 @@
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 @@
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 @@
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 @@
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 @@
@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);
-
- assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
- assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
-
- // Set type adjustment disallowed for this package
- mAssistants.setTypeAdjustmentForPackageState(allowedPackage, false);
-
- // Then the package is marked as denied
- assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
- .containsExactly(allowedPackage);
- assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
-
- // Set type adjustment allowed again
- mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true);
-
- // Then the package is marked as allowed again
- assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
- assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
+ 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_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);
+ public void testSetAssistantAdjustmentKeyTypeStateForPackage_allowsAndDenies() {
+ setDefaultAllowedAdjustmentKeyTypes(mAssistants);
+ // Given that a package is set to have a type adjustment allowed,
+ String pkg = "my.package";
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, true);
- // 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));
+ // 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();
- // And when we re-allow one of them,
- mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, true);
+ // Set type adjustment disallowed for this package
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, false);
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, false);
- // 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));
+ // Then the package is marked as denied
+ assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).isEmpty();
+ assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isFalse();
+
+ // Set type adjustment allowed again
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, true);
+ mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, true);
+
+ // Then the package is marked as allowed again
+ 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_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 601023f..301165f 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.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.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 @@
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 @@
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 @@
}
@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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 e7c9e92..e27dbe5 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 @@
}
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) {