diff --git a/Android.bp b/Android.bp index 0c199a634725826f55f3bfd4cfc5ca314763c631..7d5e788c5a37920d21fb79bea228a5dfe2c9a7bb 100644 --- a/Android.bp +++ b/Android.bp @@ -323,7 +323,6 @@ java_defaults { ":installd_aidl", ":libaudioclient_aidl", ":libbinder_aidl", - ":libbluetooth-binder-aidl", ":libcamera_client_aidl", ":libcamera_client_framework_aidl", ":libupdate_engine_aidl", diff --git a/cmds/svc/src/com/android/commands/svc/NfcCommand.java b/cmds/svc/src/com/android/commands/svc/NfcCommand.java index 870e0078450f7612aae369f07e14b94d20cfbbbf..ee2af129d4001fbb23298c3a8b15b7fb8dd534e7 100644 --- a/cmds/svc/src/com/android/commands/svc/NfcCommand.java +++ b/cmds/svc/src/com/android/commands/svc/NfcCommand.java @@ -20,6 +20,7 @@ import android.app.ActivityThread; import android.content.Context; import android.nfc.NfcAdapter; import android.nfc.NfcManager; +import android.os.Looper; public class NfcCommand extends Svc.Command { @@ -42,6 +43,8 @@ public class NfcCommand extends Svc.Command { @Override public void run(String[] args) { + Looper.prepareMainLooper(); + ActivityThread.initializeMainlineModules(); Context context = ActivityThread.systemMain().getSystemContext(); NfcManager nfcManager = context.getSystemService(NfcManager.class); if (nfcManager == null) { diff --git a/core/api/current.txt b/core/api/current.txt index 940be6b33467ea4363eb37883d3bc08bae3b0804..28f83d887c30c0551f5cc0093dedec8ce83e873f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9644,13 +9644,13 @@ package android.companion { method @Deprecated @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo); method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull String); method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo); - method @MainThread public void onDeviceEvent(@NonNull android.companion.AssociationInfo, int); - field public static final int DEVICE_EVENT_BLE_APPEARED = 0; // 0x0 - field public static final int DEVICE_EVENT_BLE_DISAPPEARED = 1; // 0x1 - field public static final int DEVICE_EVENT_BT_CONNECTED = 2; // 0x2 - field public static final int DEVICE_EVENT_BT_DISCONNECTED = 3; // 0x3 - field public static final int DEVICE_EVENT_SELF_MANAGED_APPEARED = 4; // 0x4 - field public static final int DEVICE_EVENT_SELF_MANAGED_DISAPPEARED = 5; // 0x5 + method @FlaggedApi("android.companion.device_presence") @MainThread public void onDeviceEvent(@NonNull android.companion.AssociationInfo, int); + field @FlaggedApi("android.companion.device_presence") public static final int DEVICE_EVENT_BLE_APPEARED = 0; // 0x0 + field @FlaggedApi("android.companion.device_presence") public static final int DEVICE_EVENT_BLE_DISAPPEARED = 1; // 0x1 + field @FlaggedApi("android.companion.device_presence") public static final int DEVICE_EVENT_BT_CONNECTED = 2; // 0x2 + field @FlaggedApi("android.companion.device_presence") public static final int DEVICE_EVENT_BT_DISCONNECTED = 3; // 0x3 + field @FlaggedApi("android.companion.device_presence") public static final int DEVICE_EVENT_SELF_MANAGED_APPEARED = 4; // 0x4 + field @FlaggedApi("android.companion.device_presence") public static final int DEVICE_EVENT_SELF_MANAGED_DISAPPEARED = 5; // 0x5 field public static final String SERVICE_INTERFACE = "android.companion.CompanionDeviceService"; } @@ -11101,7 +11101,7 @@ package android.content { field public static final String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE"; field @Deprecated public static final String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE"; field public static final String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS"; - field public static final String EXTRA_ARCHIVAL = "android.intent.extra.ARCHIVAL"; + field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_ARCHIVAL = "android.intent.extra.ARCHIVAL"; field public static final String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT"; field public static final String EXTRA_ASSIST_INPUT_DEVICE_ID = "android.intent.extra.ASSIST_INPUT_DEVICE_ID"; field public static final String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD"; @@ -11988,6 +11988,40 @@ package android.content.pm { method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo); } + @FlaggedApi("android.content.pm.archiving") public final class ArchivedActivity { + ctor public ArchivedActivity(@NonNull CharSequence, @NonNull android.content.ComponentName); + method @NonNull public android.content.ComponentName getComponentName(); + method @Nullable public android.graphics.drawable.Drawable getIcon(); + method @NonNull public CharSequence getLabel(); + method @Nullable public android.graphics.drawable.Drawable getMonochromeIcon(); + method @NonNull public android.content.pm.ArchivedActivity setComponentName(@NonNull android.content.ComponentName); + method @NonNull public android.content.pm.ArchivedActivity setIcon(@NonNull android.graphics.drawable.Drawable); + method @NonNull public android.content.pm.ArchivedActivity setLabel(@NonNull CharSequence); + method @NonNull public android.content.pm.ArchivedActivity setMonochromeIcon(@NonNull android.graphics.drawable.Drawable); + } + + @FlaggedApi("android.content.pm.archiving") public final class ArchivedPackage { + ctor public ArchivedPackage(@NonNull String, @NonNull android.content.pm.SigningInfo, @NonNull java.util.List<android.content.pm.ArchivedActivity>); + method @Nullable public String getDefaultToDeviceProtectedStorage(); + method @NonNull public java.util.List<android.content.pm.ArchivedActivity> getLauncherActivities(); + method @NonNull public String getPackageName(); + method @Nullable public String getRequestLegacyExternalStorage(); + method @NonNull public android.content.pm.SigningInfo getSigningInfo(); + method public int getTargetSdkVersion(); + method @Nullable public String getUserDataFragile(); + method public int getVersionCode(); + method public int getVersionCodeMajor(); + method @NonNull public android.content.pm.ArchivedPackage setDefaultToDeviceProtectedStorage(@NonNull String); + method @NonNull public android.content.pm.ArchivedPackage setLauncherActivities(@NonNull java.util.List<android.content.pm.ArchivedActivity>); + method @NonNull public android.content.pm.ArchivedPackage setPackageName(@NonNull String); + method @NonNull public android.content.pm.ArchivedPackage setRequestLegacyExternalStorage(@NonNull String); + method @NonNull public android.content.pm.ArchivedPackage setSigningInfo(@NonNull android.content.pm.SigningInfo); + method @NonNull public android.content.pm.ArchivedPackage setTargetSdkVersion(int); + method @NonNull public android.content.pm.ArchivedPackage setUserDataFragile(@NonNull String); + method @NonNull public android.content.pm.ArchivedPackage setVersionCode(int); + method @NonNull public android.content.pm.ArchivedPackage setVersionCodeMajor(int); + } + public final class Attribution implements android.os.Parcelable { method public int describeContents(); method @IdRes public int getLabel(); @@ -12320,6 +12354,7 @@ package android.content.pm { method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int); method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions(); method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.INSTALL_EXISTING_PACKAGES"}) public void installExistingPackage(@NonNull String, int, @Nullable android.content.IntentSender); + method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void installPackageArchived(@NonNull android.content.pm.ArchivedPackage, @NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.content.IntentSender); method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException; method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback); method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler); @@ -12601,6 +12636,7 @@ package android.content.pm { method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo); method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo); method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @FlaggedApi("android.content.pm.archiving") @Nullable public android.content.pm.ArchivedPackage getArchivedPackage(@NonNull String); method @NonNull public CharSequence getBackgroundPermissionOptionLabel(); method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int); method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName); @@ -16259,6 +16295,8 @@ package android.graphics { public static class Paint.FontMetricsInt { ctor public Paint.FontMetricsInt(); + method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void set(@NonNull android.graphics.Paint.FontMetricsInt); + method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void set(@NonNull android.graphics.Paint.FontMetrics); field public int ascent; field public int bottom; field public int descent; @@ -33473,7 +33511,7 @@ package android.os { field public static final String DISALLOW_MICROPHONE_TOGGLE = "disallow_microphone_toggle"; field public static final String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts"; field public static final String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media"; - field public static final String DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO = "no_near_field_communication_radio"; + field @FlaggedApi("android.nfc.enable_nfc_user_restriction") public static final String DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO = "no_near_field_communication_radio"; field public static final String DISALLOW_NETWORK_RESET = "no_network_reset"; field public static final String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam"; field public static final String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls"; @@ -43125,6 +43163,7 @@ package android.telephony { field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool"; field public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool"; field public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool"; + field @FlaggedApi("com.android.internal.telephony.flags.hide_roaming_icon") public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool"; field public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = "show_signal_strength_in_sim_status_bool"; field public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL = "show_video_call_charges_alert_dialog_bool"; field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool"; @@ -46719,6 +46758,7 @@ package android.text { method @NonNull public android.text.DynamicLayout.Builder setJustificationMode(int); method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig); method @NonNull public android.text.DynamicLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float); + method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.DynamicLayout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics); method @NonNull public android.text.DynamicLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic); method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean); method @NonNull public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean); @@ -46907,6 +46947,7 @@ package android.text { method public int getLineVisibleEnd(int); method public float getLineWidth(int); method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @IntRange(from=1) public final int getMaxLines(); + method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @Nullable public android.graphics.Paint.FontMetrics getMinimumFontMetrics(); method public int getOffsetForHorizontal(int, float); method public int getOffsetToLeftOf(int); method public int getOffsetToRightOf(int); @@ -46973,6 +47014,7 @@ package android.text { method @NonNull public android.text.Layout.Builder setLineSpacingAmount(float); method @NonNull public android.text.Layout.Builder setLineSpacingMultiplier(@FloatRange(from=0) float); method @NonNull public android.text.Layout.Builder setMaxLines(@IntRange(from=1) int); + method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.Layout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics); method @NonNull public android.text.Layout.Builder setRightIndents(@Nullable int[]); method @NonNull public android.text.Layout.Builder setTextDirectionHeuristic(@NonNull android.text.TextDirectionHeuristic); method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean); @@ -47244,6 +47286,7 @@ package android.text { method @NonNull public android.text.StaticLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig); method @NonNull public android.text.StaticLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float); method @NonNull public android.text.StaticLayout.Builder setMaxLines(@IntRange(from=0) int); + method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.StaticLayout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics); method public android.text.StaticLayout.Builder setText(CharSequence); method @NonNull public android.text.StaticLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic); method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean); @@ -59922,6 +59965,7 @@ package android.widget { method public int getMinHeight(); method public int getMinLines(); method public int getMinWidth(); + method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @Nullable public android.graphics.Paint.FontMetrics getMinimumFontMetrics(); method public final android.text.method.MovementMethod getMovementMethod(); method public int getOffsetForPosition(float, float); method public android.text.TextPaint getPaint(); @@ -60058,6 +60102,7 @@ package android.widget { method public void setMinHeight(int); method public void setMinLines(int); method public void setMinWidth(int); + method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics); method public final void setMovementMethod(android.text.method.MovementMethod); method public void setOnEditorActionListener(android.widget.TextView.OnEditorActionListener); method public void setPaintFlags(int); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 21ed098f448a4f5fa0882238d568557e9792133a..fd308ce2e85a74075470c2131a68f4f45e4eafa6 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -48,6 +48,7 @@ import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApkChecksum; import android.content.pm.ApplicationInfo; +import android.content.pm.ArchivedPackage; import android.content.pm.ChangedPackages; import android.content.pm.Checksum; import android.content.pm.ComponentInfo; @@ -3935,6 +3936,19 @@ public class ApplicationPackageManager extends PackageManager { } } + @Override + public @Nullable ArchivedPackage getArchivedPackage(@NonNull String packageName) { + try { + var parcel = mPM.getArchivedPackage(packageName, mContext.getUserId()); + if (parcel == null) { + return null; + } + return new ArchivedPackage(parcel); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + @Override public boolean canUserUninstall(String packageName, UserHandle user) { try { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 94e1292a75546619e43897c91b9a7e893f99aaa4..3bde39c03f255b0eb8c745e09f20893afeaffdc6 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5607,7 +5607,8 @@ public class Notification implements Parcelable // Use different highlighted colors for conversations' unread count if (p.mHighlightExpander) { pillColor = Colors.flattenAlpha(getColors(p).getTertiaryAccentColor(), bgColor); - textColor = Colors.flattenAlpha(getColors(p).getOnAccentTextColor(), pillColor); + textColor = Colors.flattenAlpha( + getColors(p).getOnTertiaryAccentTextColor(), pillColor); } contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor); contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor); @@ -12833,7 +12834,7 @@ public class Notification implements Parcelable private int mPrimaryAccentColor = COLOR_INVALID; private int mSecondaryAccentColor = COLOR_INVALID; private int mTertiaryAccentColor = COLOR_INVALID; - private int mOnAccentTextColor = COLOR_INVALID; + private int mOnTertiaryAccentTextColor = COLOR_INVALID; private int mErrorColor = COLOR_INVALID; private int mContrastColor = COLOR_INVALID; private int mRippleAlpha = 0x33; @@ -12908,7 +12909,7 @@ public class Notification implements Parcelable mPrimaryAccentColor = mPrimaryTextColor; mSecondaryAccentColor = mSecondaryTextColor; mTertiaryAccentColor = flattenAlpha(mPrimaryTextColor, mBackgroundColor); - mOnAccentTextColor = mBackgroundColor; + mOnTertiaryAccentTextColor = mBackgroundColor; mErrorColor = mPrimaryTextColor; mRippleAlpha = 0x33; } else { @@ -12930,7 +12931,7 @@ public class Notification implements Parcelable mPrimaryAccentColor = getColor(ta, 3, COLOR_INVALID); mSecondaryAccentColor = getColor(ta, 4, COLOR_INVALID); mTertiaryAccentColor = getColor(ta, 5, COLOR_INVALID); - mOnAccentTextColor = getColor(ta, 6, COLOR_INVALID); + mOnTertiaryAccentTextColor = getColor(ta, 6, COLOR_INVALID); mErrorColor = getColor(ta, 7, COLOR_INVALID); mRippleAlpha = Color.alpha(getColor(ta, 8, 0x33ffffff)); } @@ -12955,8 +12956,8 @@ public class Notification implements Parcelable if (mTertiaryAccentColor == COLOR_INVALID) { mTertiaryAccentColor = mContrastColor; } - if (mOnAccentTextColor == COLOR_INVALID) { - mOnAccentTextColor = ColorUtils.setAlphaComponent( + if (mOnTertiaryAccentTextColor == COLOR_INVALID) { + mOnTertiaryAccentTextColor = ColorUtils.setAlphaComponent( ContrastColorUtil.resolvePrimaryColor( ctx, mTertiaryAccentColor, nightMode), 0xFF); } @@ -13029,8 +13030,8 @@ public class Notification implements Parcelable } /** @return the theme's text color to be used on the tertiary accent color. */ - public @ColorInt int getOnAccentTextColor() { - return mOnAccentTextColor; + public @ColorInt int getOnTertiaryAccentTextColor() { + return mOnTertiaryAccentTextColor; } /** diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index f99615fd2ef4f6652977a7f6e8ee7168731cbf4a..5a41c65535e6ca013fdbaf15d6e90a68c6be9692 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -27,3 +27,17 @@ flag { description: "Allow holders of INTERACT_ACROSS_USERS_FULL to suspend apps in different users." bug: "263464464" } + +flag { + name: "dedicated_device_control_enabled" + namespace: "enterprise" + description: "Allow the device management role holder to control which platform features are available on dedicated devices." + bug: "281964214" +} + +flag { + name: "security_log_v2_enabled" + namespace: "enterprise" + description: "Improve access to security logging in the context of Zero Trust." + bug: "295324350" +} diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java index b34f6788fb6014f0e29ca18c222ada189b10e8ff..06bff5df490a67690323808f5cf68dccd017b023 100644 --- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java +++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java @@ -58,6 +58,11 @@ public abstract class ActivityLifecycleItem extends ActivityTransactionItem { super(in); } + @Override + boolean isActivityLifecycleItem() { + return true; + } + /** A final lifecycle state that an activity should reach. */ @LifecycleState public abstract int getTargetState(); diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 8617386516afb7ccc88baccb39104870fdb04dc5..9c0cd39e81029076de6c9e25973b71314e1e9735 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -45,6 +45,13 @@ import java.util.Objects; */ public class ClientTransaction implements Parcelable, ObjectPoolItem { + /** + * List of transaction items that should be executed in order. Including both + * {@link ActivityLifecycleItem} and other {@link ClientTransactionItem}. + */ + @Nullable + private List<ClientTransactionItem> mTransactionItems; + /** A list of individual callbacks to a client. */ @UnsupportedAppUsage private List<ClientTransactionItem> mActivityCallbacks; @@ -64,9 +71,32 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { } /** - * Add a message to the end of the sequence of callbacks. + * Adds a message to the end of the sequence of transaction items. + * @param item A single message that can contain a client activity/window request/callback. + * TODO(b/260873529): replace both {@link #addCallback} and {@link #setLifecycleStateRequest}. + */ + public void addTransactionItem(@NonNull ClientTransactionItem item) { + if (mTransactionItems == null) { + mTransactionItems = new ArrayList<>(); + } + mTransactionItems.add(item); + } + + /** + * Gets the list of client window requests/callbacks. + * TODO(b/260873529): must be non null after remove the deprecated methods. + */ + @Nullable + public List<ClientTransactionItem> getTransactionItems() { + return mTransactionItems; + } + + /** + * Adds a message to the end of the sequence of callbacks. * @param activityCallback A single message that can contain a lifecycle request/callback. + * @deprecated use {@link #addTransactionItem(ClientTransactionItem)} instead. */ + @Deprecated public void addCallback(@NonNull ClientTransactionItem activityCallback) { if (mActivityCallbacks == null) { mActivityCallbacks = new ArrayList<>(); @@ -74,25 +104,35 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { mActivityCallbacks.add(activityCallback); } - /** Get the list of callbacks. */ + /** + * Gets the list of callbacks. + * @deprecated use {@link #getTransactionItems()} instead. + */ @Nullable @VisibleForTesting @UnsupportedAppUsage + @Deprecated public List<ClientTransactionItem> getCallbacks() { return mActivityCallbacks; } - /** Get the target state lifecycle request. */ + /** + * Gets the target state lifecycle request. + * @deprecated use {@link #getTransactionItems()} instead. + */ @VisibleForTesting(visibility = PACKAGE) @UnsupportedAppUsage + @Deprecated public ActivityLifecycleItem getLifecycleStateRequest() { return mLifecycleStateRequest; } /** - * Set the lifecycle state in which the client should be after executing the transaction. + * Sets the lifecycle state in which the client should be after executing the transaction. * @param stateRequest A lifecycle request initialized with right parameters. + * @deprecated use {@link #addTransactionItem(ClientTransactionItem)} instead. */ + @Deprecated public void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) { mLifecycleStateRequest = stateRequest; } @@ -103,6 +143,14 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { * requested by transaction items. */ public void preExecute(@NonNull ClientTransactionHandler clientTransactionHandler) { + if (mTransactionItems != null) { + final int size = mTransactionItems.size(); + for (int i = 0; i < size; ++i) { + mTransactionItems.get(i).preExecute(clientTransactionHandler); + } + return; + } + if (mActivityCallbacks != null) { final int size = mActivityCallbacks.size(); for (int i = 0; i < size; ++i) { @@ -147,6 +195,13 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { @Override public void recycle() { + if (mTransactionItems != null) { + int size = mTransactionItems.size(); + for (int i = 0; i < size; i++) { + mTransactionItems.get(i).recycle(); + } + mTransactionItems = null; + } if (mActivityCallbacks != null) { int size = mActivityCallbacks.size(); for (int i = 0; i < size; i++) { @@ -165,8 +220,15 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { // Parcelable implementation /** Write to Parcel. */ + @SuppressWarnings("AndroidFrameworkEfficientParcelable") // Item class is not final. @Override public void writeToParcel(@NonNull Parcel dest, int flags) { + final boolean writeTransactionItems = mTransactionItems != null; + dest.writeBoolean(writeTransactionItems); + if (writeTransactionItems) { + dest.writeParcelableList(mTransactionItems, flags); + } + dest.writeParcelable(mLifecycleStateRequest, flags); final boolean writeActivityCallbacks = mActivityCallbacks != null; dest.writeBoolean(writeActivityCallbacks); @@ -177,11 +239,20 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { /** Read from Parcel. */ private ClientTransaction(@NonNull Parcel in) { - mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(), android.app.servertransaction.ActivityLifecycleItem.class); + final boolean readTransactionItems = in.readBoolean(); + if (readTransactionItems) { + mTransactionItems = new ArrayList<>(); + in.readParcelableList(mTransactionItems, getClass().getClassLoader(), + ClientTransactionItem.class); + } + + mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(), + ActivityLifecycleItem.class); final boolean readActivityCallbacks = in.readBoolean(); if (readActivityCallbacks) { mActivityCallbacks = new ArrayList<>(); - in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(), android.app.servertransaction.ClientTransactionItem.class); + in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(), + ClientTransactionItem.class); } } @@ -209,7 +280,8 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { return false; } final ClientTransaction other = (ClientTransaction) o; - return Objects.equals(mActivityCallbacks, other.mActivityCallbacks) + return Objects.equals(mTransactionItems, other.mTransactionItems) + && Objects.equals(mActivityCallbacks, other.mActivityCallbacks) && Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest) && mClient == other.mClient; } @@ -217,6 +289,7 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { @Override public int hashCode() { int result = 17; + result = 31 * result + Objects.hashCode(mTransactionItems); result = 31 * result + Objects.hashCode(mActivityCallbacks); result = 31 * result + Objects.hashCode(mLifecycleStateRequest); result = 31 * result + Objects.hashCode(mClient); @@ -227,6 +300,22 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { void dump(@NonNull String prefix, @NonNull PrintWriter pw, @NonNull ClientTransactionHandler transactionHandler) { pw.append(prefix).println("ClientTransaction{"); + if (mTransactionItems != null) { + pw.append(prefix).print(" transactionItems=["); + final String itemPrefix = prefix + " "; + final int size = mTransactionItems.size(); + if (size > 0) { + pw.println(); + for (int i = 0; i < size; i++) { + mTransactionItems.get(i).dump(itemPrefix, pw, transactionHandler); + } + pw.append(prefix).println(" ]"); + } else { + pw.println("]"); + } + pw.append(prefix).println("}"); + return; + } pw.append(prefix).print(" callbacks=["); final String itemPrefix = prefix + " "; final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0; diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java index 07e5a7dc5f0272695f6d09059b45468217d463e3..f94e22de06e5e7bc027c341f20a236297682ca3e 100644 --- a/core/java/android/app/servertransaction/ClientTransactionItem.java +++ b/core/java/android/app/servertransaction/ClientTransactionItem.java @@ -72,6 +72,13 @@ public abstract class ClientTransactionItem implements BaseClientRequest, Parcel return null; } + /** + * Whether this is a {@link ActivityLifecycleItem}. + */ + boolean isActivityLifecycleItem() { + return false; + } + /** Dumps this transaction item. */ void dump(@NonNull String prefix, @NonNull PrintWriter pw, @NonNull ClientTransactionHandler transactionHandler) { diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index 066f9fe8497042505b74134346e05648d0ab9447..9f5e0dc14cca77aa98942cfdfa04fdaf98fa7954 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -28,6 +28,7 @@ import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; import static android.app.servertransaction.TransactionExecutorHelper.getShortActivityName; import static android.app.servertransaction.TransactionExecutorHelper.getStateName; import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState; +import static android.app.servertransaction.TransactionExecutorHelper.shouldExcludeLastLifecycleState; import static android.app.servertransaction.TransactionExecutorHelper.tId; import static android.app.servertransaction.TransactionExecutorHelper.transactionToString; @@ -61,6 +62,9 @@ public class TransactionExecutor { private final PendingTransactionActions mPendingActions = new PendingTransactionActions(); private final TransactionExecutorHelper mHelper = new TransactionExecutorHelper(); + /** Keeps track of display ids whose Configuration got updated within a transaction. */ + private final ArraySet<Integer> mConfigUpdatedDisplayIds = new ArraySet<>(); + /** Initialize an instance with transaction handler, that will execute all requested actions. */ public TransactionExecutor(@NonNull ClientTransactionHandler clientTransactionHandler) { mTransactionHandler = clientTransactionHandler; @@ -79,15 +83,52 @@ public class TransactionExecutor { Slog.d(TAG, transactionToString(transaction, mTransactionHandler)); } - executeCallbacks(transaction); - executeLifecycleState(transaction); + if (transaction.getTransactionItems() != null) { + executeTransactionItems(transaction); + } else { + // TODO(b/260873529): cleanup after launch. + executeCallbacks(transaction); + executeLifecycleState(transaction); + } + + if (!mConfigUpdatedDisplayIds.isEmpty()) { + // Whether this transaction should trigger DisplayListener#onDisplayChanged. + final ClientTransactionListenerController controller = + ClientTransactionListenerController.getInstance(); + final int displayCount = mConfigUpdatedDisplayIds.size(); + for (int i = 0; i < displayCount; i++) { + final int displayId = mConfigUpdatedDisplayIds.valueAt(i); + controller.onDisplayChanged(displayId); + } + mConfigUpdatedDisplayIds.clear(); + } mPendingActions.clear(); if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction"); } - /** Cycle through all states requested by callbacks and execute them at proper times. */ + /** Cycles through all transaction items and execute them at proper times. */ @VisibleForTesting + public void executeTransactionItems(@NonNull ClientTransaction transaction) { + final List<ClientTransactionItem> items = transaction.getTransactionItems(); + final int size = items.size(); + for (int i = 0; i < size; i++) { + final ClientTransactionItem item = items.get(i); + if (item.isActivityLifecycleItem()) { + executeLifecycleItem(transaction, (ActivityLifecycleItem) item); + } else { + executeNonLifecycleItem(transaction, item, + shouldExcludeLastLifecycleState(items, i)); + } + } + } + + /** + * Cycle through all states requested by callbacks and execute them at proper times. + * @deprecated use {@link #executeTransactionItems} instead. + */ + @VisibleForTesting + @Deprecated public void executeCallbacks(@NonNull ClientTransaction transaction) { final List<ClientTransactionItem> callbacks = transaction.getCallbacks(); if (callbacks == null || callbacks.isEmpty()) { @@ -105,83 +146,78 @@ public class TransactionExecutor { // Index of the last callback that requests some post-execution state. final int lastCallbackRequestingState = lastCallbackRequestingState(transaction); - // Keep track of display ids whose Configuration got updated with this transaction. - ArraySet<Integer> configUpdatedDisplays = null; - final int size = callbacks.size(); for (int i = 0; i < size; ++i) { final ClientTransactionItem item = callbacks.get(i); - final IBinder token = item.getActivityToken(); - ActivityClientRecord r = mTransactionHandler.getActivityClient(token); - - if (token != null && r == null - && mTransactionHandler.getActivitiesToBeDestroyed().containsKey(token)) { - // The activity has not been created but has been requested to destroy, so all - // transactions for the token are just like being cancelled. - Slog.w(TAG, "Skip pre-destroyed transaction item:\n" + item); - continue; - } - if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item); + // Skip the very last transition and perform it by explicit state request instead. final int postExecutionState = item.getPostExecutionState(); + final boolean shouldExcludeLastLifecycleState = postExecutionState != UNDEFINED + && i == lastCallbackRequestingState && finalState == postExecutionState; + executeNonLifecycleItem(transaction, item, shouldExcludeLastLifecycleState); + } + } - if (item.shouldHaveDefinedPreExecutionState()) { - final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r, - item.getPostExecutionState()); - if (closestPreExecutionState != UNDEFINED) { - cycleToPath(r, closestPreExecutionState, transaction); - } - } + private void executeNonLifecycleItem(@NonNull ClientTransaction transaction, + @NonNull ClientTransactionItem item, boolean shouldExcludeLastLifecycleState) { + final IBinder token = item.getActivityToken(); + ActivityClientRecord r = mTransactionHandler.getActivityClient(token); - // Can't read flag from isolated process. - final boolean isSyncWindowConfigUpdateFlagEnabled = !Process.isIsolated() - && syncWindowConfigUpdateFlag(); - final Context configUpdatedContext = isSyncWindowConfigUpdateFlagEnabled - ? item.getContextToUpdate(mTransactionHandler) - : null; - final Configuration preExecutedConfig = configUpdatedContext != null - ? new Configuration(configUpdatedContext.getResources().getConfiguration()) - : null; - - item.execute(mTransactionHandler, mPendingActions); - - if (configUpdatedContext != null) { - final Configuration postExecutedConfig = configUpdatedContext.getResources() - .getConfiguration(); - if (!areConfigurationsEqualForDisplay(postExecutedConfig, preExecutedConfig)) { - if (configUpdatedDisplays == null) { - configUpdatedDisplays = new ArraySet<>(); - } - configUpdatedDisplays.add(configUpdatedContext.getDisplayId()); - } - } + if (token != null && r == null + && mTransactionHandler.getActivitiesToBeDestroyed().containsKey(token)) { + // The activity has not been created but has been requested to destroy, so all + // transactions for the token are just like being cancelled. + Slog.w(TAG, "Skip pre-destroyed transaction item:\n" + item); + return; + } - item.postExecute(mTransactionHandler, mPendingActions); - if (r == null) { - // Launch activity request will create an activity record. - r = mTransactionHandler.getActivityClient(token); - } + if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item); + final int postExecutionState = item.getPostExecutionState(); - if (postExecutionState != UNDEFINED && r != null) { - // Skip the very last transition and perform it by explicit state request instead. - final boolean shouldExcludeLastTransition = - i == lastCallbackRequestingState && finalState == postExecutionState; - cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction); + if (item.shouldHaveDefinedPreExecutionState()) { + final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r, + postExecutionState); + if (closestPreExecutionState != UNDEFINED) { + cycleToPath(r, closestPreExecutionState, transaction); } } - if (configUpdatedDisplays != null) { - final ClientTransactionListenerController controller = - ClientTransactionListenerController.getInstance(); - final int displayCount = configUpdatedDisplays.size(); - for (int i = 0; i < displayCount; i++) { - final int displayId = configUpdatedDisplays.valueAt(i); - controller.onDisplayChanged(displayId); + // Can't read flag from isolated process. + final boolean isSyncWindowConfigUpdateFlagEnabled = !Process.isIsolated() + && syncWindowConfigUpdateFlag(); + final Context configUpdatedContext = isSyncWindowConfigUpdateFlagEnabled + ? item.getContextToUpdate(mTransactionHandler) + : null; + final Configuration preExecutedConfig = configUpdatedContext != null + ? new Configuration(configUpdatedContext.getResources().getConfiguration()) + : null; + + item.execute(mTransactionHandler, mPendingActions); + + if (configUpdatedContext != null) { + final Configuration postExecutedConfig = configUpdatedContext.getResources() + .getConfiguration(); + if (!areConfigurationsEqualForDisplay(postExecutedConfig, preExecutedConfig)) { + mConfigUpdatedDisplayIds.add(configUpdatedContext.getDisplayId()); } } + + item.postExecute(mTransactionHandler, mPendingActions); + if (r == null) { + // Launch activity request will create an activity record. + r = mTransactionHandler.getActivityClient(token); + } + + if (postExecutionState != UNDEFINED && r != null) { + cycleToPath(r, postExecutionState, shouldExcludeLastLifecycleState, transaction); + } } - /** Transition to the final state if requested by the transaction. */ + /** + * Transition to the final state if requested by the transaction. + * @deprecated use {@link #executeTransactionItems} instead + */ + @Deprecated private void executeLifecycleState(@NonNull ClientTransaction transaction) { final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest(); if (lifecycleItem == null) { @@ -189,6 +225,11 @@ public class TransactionExecutor { return; } + executeLifecycleItem(transaction, lifecycleItem); + } + + private void executeLifecycleItem(@NonNull ClientTransaction transaction, + @NonNull ActivityLifecycleItem lifecycleItem) { final IBinder token = lifecycleItem.getActivityToken(); final ActivityClientRecord r = mTransactionHandler.getActivityClient(token); if (DEBUG_RESOLVER) { diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java index 7e89a5b45a2d44369ef480f004fd1b0de724ebba..dfbccb41d045729594eda00c10b826c3d398837e 100644 --- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -236,21 +236,39 @@ public class TransactionExecutorHelper { * index 1 will be returned, because ActivityResult request on position 1 will be the last * request that moves activity to the RESUMED state where it will eventually end. */ - static int lastCallbackRequestingState(ClientTransaction transaction) { + static int lastCallbackRequestingState(@NonNull ClientTransaction transaction) { final List<ClientTransactionItem> callbacks = transaction.getCallbacks(); - if (callbacks == null || callbacks.size() == 0) { + if (callbacks == null || callbacks.isEmpty() + || transaction.getLifecycleStateRequest() == null) { return -1; } + return lastCallbackRequestingStateIndex(callbacks, 0, callbacks.size() - 1, + transaction.getLifecycleStateRequest().getActivityToken()); + } + /** + * Returns the index of the last callback between the start index and last index that requests + * the state for the given activity token in which that activity will be after execution. + * If there is a group of callbacks in the end that requests the same specific state or doesn't + * request any - we will find the first one from such group. + * + * E.g. ActivityResult requests RESUMED post-execution state, Configuration does not request any + * specific state. If there is a sequence + * Configuration - ActivityResult - Configuration - ActivityResult + * index 1 will be returned, because ActivityResult request on position 1 will be the last + * request that moves activity to the RESUMED state where it will eventually end. + */ + private static int lastCallbackRequestingStateIndex(@NonNull List<ClientTransactionItem> items, + int startIndex, int lastIndex, @NonNull IBinder activityToken) { // Go from the back of the list to front, look for the request closes to the beginning that // requests the state in which activity will end after all callbacks are executed. int lastRequestedState = UNDEFINED; int lastRequestingCallback = -1; - for (int i = callbacks.size() - 1; i >= 0; i--) { - final ClientTransactionItem callback = callbacks.get(i); - final int postExecutionState = callback.getPostExecutionState(); - if (postExecutionState != UNDEFINED) { - // Found a callback that requests some post-execution state. + for (int i = lastIndex; i >= startIndex; i--) { + final ClientTransactionItem item = items.get(i); + final int postExecutionState = item.getPostExecutionState(); + if (postExecutionState != UNDEFINED && activityToken.equals(item.getActivityToken())) { + // Found a callback that requests some post-execution state for the given activity. if (lastRequestedState == UNDEFINED || lastRequestedState == postExecutionState) { // It's either a first-from-end callback that requests state or it requests // the same state as the last one. In both cases, we will use it as the new @@ -266,6 +284,53 @@ public class TransactionExecutorHelper { return lastRequestingCallback; } + /** + * For the transaction item at {@code currentIndex}, if it is requesting post execution state, + * whether or not to exclude the last state. This only returns {@code true} when there is a + * following explicit {@link ActivityLifecycleItem} requesting the same state for the same + * activity, so that last state will be covered by the following {@link ActivityLifecycleItem}. + */ + static boolean shouldExcludeLastLifecycleState(@NonNull List<ClientTransactionItem> items, + int currentIndex) { + final ClientTransactionItem item = items.get(currentIndex); + final IBinder activityToken = item.getActivityToken(); + final int postExecutionState = item.getPostExecutionState(); + if (activityToken == null || postExecutionState == UNDEFINED) { + // Not a transaction item requesting post execution state. + return false; + } + final int nextLifecycleItemIndex = findNextLifecycleItemIndex(items, currentIndex + 1, + activityToken); + if (nextLifecycleItemIndex == -1) { + // No following ActivityLifecycleItem for this activity token. + return false; + } + final ActivityLifecycleItem lifecycleItem = + (ActivityLifecycleItem) items.get(nextLifecycleItemIndex); + if (postExecutionState != lifecycleItem.getTargetState()) { + // The explicit ActivityLifecycleItem is not requesting the same state. + return false; + } + // Only exclude for the first non-lifecycle item that requests the same specific state. + return currentIndex == lastCallbackRequestingStateIndex(items, currentIndex, + nextLifecycleItemIndex - 1, activityToken); + } + + /** + * Finds the index of the next {@link ActivityLifecycleItem} for the given activity token. + */ + private static int findNextLifecycleItemIndex(@NonNull List<ClientTransactionItem> items, + int startIndex, @NonNull IBinder activityToken) { + final int size = items.size(); + for (int i = startIndex; i < size; i++) { + final ClientTransactionItem item = items.get(i); + if (item.isActivityLifecycleItem() && item.getActivityToken().equals(activityToken)) { + return i; + } + } + return -1; + } + /** Dump transaction to string. */ static String transactionToString(@NonNull ClientTransaction transaction, @NonNull ClientTransactionHandler transactionHandler) { diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java index 570ecaa47b4e49539fe389399bae7424bf5b74fa..c99a45764de7f3c23cd49525b75f7046c04e7ecb 100644 --- a/core/java/android/companion/CompanionDeviceService.java +++ b/core/java/android/companion/CompanionDeviceService.java @@ -17,6 +17,7 @@ package android.companion; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; @@ -140,24 +141,28 @@ public abstract class CompanionDeviceService extends Service { * Companion app receives {@link #onDeviceEvent(AssociationInfo, int)} callback * with this event if the device comes into BLE range. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) public static final int DEVICE_EVENT_BLE_APPEARED = 0; /** * Companion app receives {@link #onDeviceEvent(AssociationInfo, int)} callback * with this event if the device is no longer in BLE range. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) public static final int DEVICE_EVENT_BLE_DISAPPEARED = 1; /** * Companion app receives {@link #onDeviceEvent(AssociationInfo, int)} callback * with this event when the bluetooth device is connected. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) public static final int DEVICE_EVENT_BT_CONNECTED = 2; /** * Companion app receives {@link #onDeviceEvent(AssociationInfo, int)} callback * with this event if the bluetooth device is disconnected. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) public static final int DEVICE_EVENT_BT_DISCONNECTED = 3; /** @@ -165,6 +170,7 @@ public abstract class CompanionDeviceService extends Service { * {@link #onDeviceEvent(AssociationInfo, int)} if it reports that a device has appeared on its * own. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) public static final int DEVICE_EVENT_SELF_MANAGED_APPEARED = 4; /** @@ -172,6 +178,7 @@ public abstract class CompanionDeviceService extends Service { * {@link #onDeviceEvent(AssociationInfo, int)} if it reports that a device has disappeared on * its own. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) public static final int DEVICE_EVENT_SELF_MANAGED_DISAPPEARED = 5; private final Stub mRemote = new Stub(); @@ -348,6 +355,7 @@ public abstract class CompanionDeviceService extends Service { * @param associationInfo A record for the companion device. * @param event Associated companion device's event. */ + @FlaggedApi(Flags.FLAG_DEVICE_PRESENCE) @MainThread public void onDeviceEvent(@NonNull AssociationInfo associationInfo, @DeviceEvent int event) { diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig index 4f9c849865fccba9a0f4b3306b66c8dec3984043..6e33dff3a379fa0af50f996aae0aa555772a4bec 100644 --- a/core/java/android/companion/flags.aconfig +++ b/core/java/android/companion/flags.aconfig @@ -19,4 +19,11 @@ flag { namespace: "companion" description: "Enable Association tag APIs " bug: "289241123" -} \ No newline at end of file +} + +flag { + name: "device_presence" + namespace: "companion" + description: "Enable device presence APIs" + bug: "283000075" +} diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7b6bad31539af4030708e80f9436ab65954b6da7..ffc4805df2649addfd58a6a9d4c77cf6e2c25d45 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -6340,6 +6340,7 @@ public class Intent implements Parcelable, Cloneable { * the package is being archived. Either by removing the existing APK, or by installing * a package without an APK. */ + @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING) public static final String EXTRA_ARCHIVAL = "android.intent.extra.ARCHIVAL"; /** diff --git a/core/java/android/content/pm/ArchivedActivity.java b/core/java/android/content/pm/ArchivedActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..5139e2dedd723a7662e89938dc7e37c5fdec8d89 --- /dev/null +++ b/core/java/android/content/pm/ArchivedActivity.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2023 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.content.pm; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; + +import com.android.internal.util.DataClass; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Objects; + +@DataClass(genBuilder = false, genConstructor = false, genSetters = true) +@FlaggedApi(Flags.FLAG_ARCHIVING) +public final class ArchivedActivity { + /** The label for the activity. */ + private @NonNull CharSequence mLabel; + /** The component name of this activity. */ + private @NonNull ComponentName mComponentName; + /** + * Icon of the activity in the app's locale. if null then the default icon would be shown in the + * launcher. + */ + private @Nullable Drawable mIcon; + /** Monochrome icon, if defined, of the activity. */ + private @Nullable Drawable mMonochromeIcon; + + public ArchivedActivity(@NonNull CharSequence label, @NonNull ComponentName componentName) { + Objects.requireNonNull(label); + Objects.requireNonNull(componentName); + mLabel = label; + mComponentName = componentName; + } + + /* @hide */ + ArchivedActivity(@NonNull ArchivedActivityParcel parcel) { + mLabel = parcel.title; + mComponentName = parcel.originalComponentName; + mIcon = drawableFromCompressedBitmap(parcel.iconBitmap); + mMonochromeIcon = drawableFromCompressedBitmap(parcel.monochromeIconBitmap); + } + + /* @hide */ + @NonNull ArchivedActivityParcel getParcel() { + var parcel = new ArchivedActivityParcel(); + parcel.title = mLabel.toString(); + parcel.originalComponentName = mComponentName; + parcel.iconBitmap = mIcon == null ? null : + bytesFromBitmap(drawableToBitmap(mIcon)); + parcel.monochromeIconBitmap = mMonochromeIcon == null ? null : + bytesFromBitmap(drawableToBitmap(mMonochromeIcon)); + return parcel; + } + + /** + * Convert a generic drawable into a bitmap. + * @hide + */ + public static Bitmap drawableToBitmap(Drawable drawable) { + return drawableToBitmap(drawable, /* maxIconSize= */ Integer.MAX_VALUE); + } + + /** + * Same as above, but. + * @hide + */ + public static Bitmap drawableToBitmap(Drawable drawable, int maxIconSize) { + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + + } + + Bitmap bitmap; + if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { + // Needed for drawables that are just a single color. + bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + } else { + bitmap = + Bitmap.createBitmap( + drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), + Bitmap.Config.ARGB_8888); + } + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + if (bitmap.getWidth() > maxIconSize || bitmap.getHeight() > maxIconSize) { + var scaledBitmap = Bitmap.createScaledBitmap(bitmap, maxIconSize, maxIconSize, true); + if (scaledBitmap != bitmap) { + bitmap.recycle(); + } + return scaledBitmap; + } + return bitmap; + } + + /** + * Compress bitmap to PNG format. + * The bitmap is going to be recycled. + * @hide + */ + public static byte[] bytesFromBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream( + bitmap.getByteCount())) { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); + return baos.toByteArray(); + } catch (IOException ignored) { + return null; + } + } + + private static Drawable drawableFromCompressedBitmap(byte[] bytes) { + if (bytes == null) { + return null; + } + return new BitmapDrawable(null /*res*/, new ByteArrayInputStream(bytes)); + } + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedActivity.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * The label for the activity. + */ + @DataClass.Generated.Member + public @NonNull CharSequence getLabel() { + return mLabel; + } + + /** + * The component name of this activity. + */ + @DataClass.Generated.Member + public @NonNull ComponentName getComponentName() { + return mComponentName; + } + + /** + * Icon of the activity in the app's locale. if null then the default icon would be shown in the + * launcher. + */ + @DataClass.Generated.Member + public @Nullable Drawable getIcon() { + return mIcon; + } + + /** + * Monochrome icon, if defined, of the activity. + */ + @DataClass.Generated.Member + public @Nullable Drawable getMonochromeIcon() { + return mMonochromeIcon; + } + + /** + * The label for the activity. + */ + @DataClass.Generated.Member + public @NonNull ArchivedActivity setLabel(@NonNull CharSequence value) { + mLabel = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mLabel); + return this; + } + + /** + * The component name of this activity. + */ + @DataClass.Generated.Member + public @NonNull ArchivedActivity setComponentName(@NonNull ComponentName value) { + mComponentName = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mComponentName); + return this; + } + + /** + * Icon of the activity in the app's locale. if null then the default icon would be shown in the + * launcher. + */ + @DataClass.Generated.Member + public @NonNull ArchivedActivity setIcon(@NonNull Drawable value) { + mIcon = value; + return this; + } + + /** + * Monochrome icon, if defined, of the activity. + */ + @DataClass.Generated.Member + public @NonNull ArchivedActivity setMonochromeIcon(@NonNull Drawable value) { + mMonochromeIcon = value; + return this; + } + + @DataClass.Generated( + time = 1698173429911L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivity.java", + inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivity extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/ArchivedPackage.java b/core/java/android/content/pm/ArchivedPackage.java new file mode 100644 index 0000000000000000000000000000000000000000..42795db356843175e42581706e0f30888b0cc83d --- /dev/null +++ b/core/java/android/content/pm/ArchivedPackage.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2023 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.content.pm; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Intent; + +import com.android.internal.util.DataClass; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@DataClass(genBuilder = false, genConstructor = false, genSetters = true) +@FlaggedApi(Flags.FLAG_ARCHIVING) +public final class ArchivedPackage { + /** Name of the package as used to identify it in the system */ + private @NonNull String mPackageName; + /** Signing certificates used to sign the package. */ + private @NonNull SigningInfo mSigningInfo; + /** + * The version number of the package, as specified by the <manifest>tag's + * {@link android.R.styleable#AndroidManifest_versionCode versionCode} attribute. + */ + private int mVersionCode = 0; + /** + * The major version number of the package, as specified by the <manifest>tag's + * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute. + */ + private int mVersionCodeMajor = 0; + /** + * This is the SDK version number that the application is targeting, as specified by the + * <manifest> tag's {@link android.R.styleable#AndroidManifestUsesSdk_targetSdkVersion} + * attribute. + */ + private int mTargetSdkVersion = 0; + /** + * Package data will default to device protected storage. Specified by the <manifest> + * tag's {@link android.R.styleable#AndroidManifestApplication_defaultToDeviceProtectedStorage} + * attribute. + */ + private @Nullable String mDefaultToDeviceProtectedStorage; + /** + * If {@code true} this app would like to run under the legacy storage model. Specified by the + * <manifest> tag's + * {@link android.R.styleable#AndroidManifestApplication_requestLegacyExternalStorage} + * attribute. + */ + private @Nullable String mRequestLegacyExternalStorage; + /** + * If {@code true} the user is prompted to keep the app's data on uninstall. Specified by the + * <manifest> tag's + * {@link android.R.styleable#AndroidManifestApplication_hasFragileUserData} attribute. + */ + private @Nullable String mUserDataFragile; + /** + * List of the package's activities that specify {@link Intent#ACTION_MAIN} and + * {@link Intent#CATEGORY_LAUNCHER}. + * @see LauncherApps#getActivityList + */ + private @NonNull List<ArchivedActivity> mLauncherActivities; + + public ArchivedPackage(@NonNull String packageName, @NonNull SigningInfo signingInfo, + @NonNull List<ArchivedActivity> launcherActivities) { + Objects.requireNonNull(packageName); + Objects.requireNonNull(signingInfo); + Objects.requireNonNull(launcherActivities); + this.mPackageName = packageName; + this.mSigningInfo = signingInfo; + this.mLauncherActivities = launcherActivities; + } + + /** + * Constructs the archived package from parcel. + * @hide + */ + public ArchivedPackage(@NonNull ArchivedPackageParcel parcel) { + mPackageName = parcel.packageName; + mSigningInfo = new SigningInfo(parcel.signingDetails); + mVersionCode = parcel.versionCode; + mVersionCodeMajor = parcel.versionCodeMajor; + mTargetSdkVersion = parcel.targetSdkVersion; + mDefaultToDeviceProtectedStorage = parcel.defaultToDeviceProtectedStorage; + mRequestLegacyExternalStorage = parcel.requestLegacyExternalStorage; + mUserDataFragile = parcel.userDataFragile; + mLauncherActivities = new ArrayList<>(); + if (parcel.archivedActivities != null) { + for (var activityParcel : parcel.archivedActivities) { + mLauncherActivities.add(new ArchivedActivity(activityParcel)); + } + } + } + + /* @hide */ + ArchivedPackageParcel getParcel() { + var parcel = new ArchivedPackageParcel(); + parcel.packageName = mPackageName; + parcel.signingDetails = mSigningInfo.getSigningDetails(); + parcel.versionCode = mVersionCode; + parcel.versionCodeMajor = mVersionCodeMajor; + parcel.targetSdkVersion = mTargetSdkVersion; + parcel.defaultToDeviceProtectedStorage = mDefaultToDeviceProtectedStorage; + parcel.requestLegacyExternalStorage = mRequestLegacyExternalStorage; + parcel.userDataFragile = mUserDataFragile; + + parcel.archivedActivities = new ArchivedActivityParcel[mLauncherActivities.size()]; + for (int i = 0, size = parcel.archivedActivities.length; i < size; ++i) { + parcel.archivedActivities[i] = mLauncherActivities.get(i).getParcel(); + } + + return parcel; + } + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedPackage.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Name of the package as used to identify it in the system + */ + @DataClass.Generated.Member + public @NonNull String getPackageName() { + return mPackageName; + } + + /** + * Signing certificates used to sign the package. + */ + @DataClass.Generated.Member + public @NonNull SigningInfo getSigningInfo() { + return mSigningInfo; + } + + /** + * The version number of the package, as specified by the <manifest>tag's + * {@link android.R.styleable#AndroidManifest_versionCode versionCode} attribute. + */ + @DataClass.Generated.Member + public int getVersionCode() { + return mVersionCode; + } + + /** + * The major version number of the package, as specified by the <manifest>tag's + * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute. + */ + @DataClass.Generated.Member + public int getVersionCodeMajor() { + return mVersionCodeMajor; + } + + /** + * This is the SDK version number that the application is targeting, as specified by the + * <manifest> tag's {@link android.R.styleable#AndroidManifestUsesSdk_targetSdkVersion} + * attribute. + */ + @DataClass.Generated.Member + public int getTargetSdkVersion() { + return mTargetSdkVersion; + } + + /** + * Package data will default to device protected storage. Specified by the <manifest> + * tag's {@link android.R.styleable#AndroidManifestApplication_defaultToDeviceProtectedStorage} + * attribute. + */ + @DataClass.Generated.Member + public @Nullable String getDefaultToDeviceProtectedStorage() { + return mDefaultToDeviceProtectedStorage; + } + + /** + * If {@code true} this app would like to run under the legacy storage model. Specified by the + * <manifest> tag's + * {@link android.R.styleable#AndroidManifestApplication_requestLegacyExternalStorage} + * attribute. + */ + @DataClass.Generated.Member + public @Nullable String getRequestLegacyExternalStorage() { + return mRequestLegacyExternalStorage; + } + + /** + * If {@code true} the user is prompted to keep the app's data on uninstall. Specified by the + * <manifest> tag's + * {@link android.R.styleable#AndroidManifestApplication_hasFragileUserData} attribute. + */ + @DataClass.Generated.Member + public @Nullable String getUserDataFragile() { + return mUserDataFragile; + } + + /** + * List of the package's activities that specify {@link Intent#ACTION_MAIN} and + * {@link Intent#CATEGORY_LAUNCHER}. + * + * @see LauncherApps#getActivityList + */ + @DataClass.Generated.Member + public @NonNull List<ArchivedActivity> getLauncherActivities() { + return mLauncherActivities; + } + + /** + * Name of the package as used to identify it in the system + */ + @DataClass.Generated.Member + public @NonNull ArchivedPackage setPackageName(@NonNull String value) { + mPackageName = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + return this; + } + + /** + * Signing certificates used to sign the package. + */ + @DataClass.Generated.Member + public @NonNull ArchivedPackage setSigningInfo(@NonNull SigningInfo value) { + mSigningInfo = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSigningInfo); + return this; + } + + /** + * The version number of the package, as specified by the <manifest>tag's + * {@link android.R.styleable#AndroidManifest_versionCode versionCode} attribute. + */ + @DataClass.Generated.Member + public @NonNull ArchivedPackage setVersionCode( int value) { + mVersionCode = value; + return this; + } + + /** + * The major version number of the package, as specified by the <manifest>tag's + * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute. + */ + @DataClass.Generated.Member + public @NonNull ArchivedPackage setVersionCodeMajor( int value) { + mVersionCodeMajor = value; + return this; + } + + /** + * This is the SDK version number that the application is targeting, as specified by the + * <manifest> tag's {@link android.R.styleable#AndroidManifestUsesSdk_targetSdkVersion} + * attribute. + */ + @DataClass.Generated.Member + public @NonNull ArchivedPackage setTargetSdkVersion( int value) { + mTargetSdkVersion = value; + return this; + } + + /** + * Package data will default to device protected storage. Specified by the <manifest> + * tag's {@link android.R.styleable#AndroidManifestApplication_defaultToDeviceProtectedStorage} + * attribute. + */ + @DataClass.Generated.Member + public @NonNull ArchivedPackage setDefaultToDeviceProtectedStorage(@NonNull String value) { + mDefaultToDeviceProtectedStorage = value; + return this; + } + + /** + * If {@code true} this app would like to run under the legacy storage model. Specified by the + * <manifest> tag's + * {@link android.R.styleable#AndroidManifestApplication_requestLegacyExternalStorage} + * attribute. + */ + @DataClass.Generated.Member + public @NonNull ArchivedPackage setRequestLegacyExternalStorage(@NonNull String value) { + mRequestLegacyExternalStorage = value; + return this; + } + + /** + * If {@code true} the user is prompted to keep the app's data on uninstall. Specified by the + * <manifest> tag's + * {@link android.R.styleable#AndroidManifestApplication_hasFragileUserData} attribute. + */ + @DataClass.Generated.Member + public @NonNull ArchivedPackage setUserDataFragile(@NonNull String value) { + mUserDataFragile = value; + return this; + } + + /** + * List of the package's activities that specify {@link Intent#ACTION_MAIN} and + * {@link Intent#CATEGORY_LAUNCHER}. + * + * @see LauncherApps#getActivityList + */ + @DataClass.Generated.Member + public @NonNull ArchivedPackage setLauncherActivities(@NonNull List<ArchivedActivity> value) { + mLauncherActivities = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mLauncherActivities); + return this; + } + + @DataClass.Generated( + time = 1697824890503L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedPackage.java", + inputSignatures = "private @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate int mVersionCode\nprivate int mVersionCodeMajor\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable java.lang.String mDefaultToDeviceProtectedStorage\nprivate @android.annotation.Nullable java.lang.String mRequestLegacyExternalStorage\nprivate @android.annotation.Nullable java.lang.String mUserDataFragile\nprivate @android.annotation.NonNull java.util.List<android.content.pm.ArchivedActivity> mLauncherActivities\n android.content.pm.ArchivedPackageParcel getParcel()\nclass ArchivedPackage extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index edb07ce423c10a2db6b81ce18001c7625dbf1499..59ed0453bc0132d31ffb748df428a17cc3dddabd 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -16,6 +16,7 @@ package android.content.pm; +import android.content.pm.ArchivedPackageParcel; import android.content.pm.IPackageDeleteObserver2; import android.content.pm.IPackageInstallerCallback; import android.content.pm.IPackageInstallerSession; @@ -82,4 +83,11 @@ interface IPackageInstaller { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})") void requestUnarchive(String packageName, String callerPackageName, in UserHandle userHandle); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)") + void installPackageArchived(in ArchivedPackageParcel archivedPackageParcel, + in PackageInstaller.SessionParams params, + in IntentSender statusReceiver, + String installerPackageName, in UserHandle userHandle); + } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index d837aae350969784fe67a25cbc11f549898b2a46..cd8938d1dd7762a1ceb458ec4bddcbe06e55afa7 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1000,6 +1000,37 @@ public class PackageInstaller { } } + /** + * Install package in an archived state. + * + * @param archivedPackage archived package data such as package name, signature etc. + * @param sessionParams used to create an underlying installation session + * @param statusReceiver Called when the state of the session changes. Intents + * sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the + * individual status codes on how to handle them. + * @see #createSession + * @see PackageInstaller.Session#commit + */ + @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) + @FlaggedApi(Flags.FLAG_ARCHIVING) + public void installPackageArchived(@NonNull ArchivedPackage archivedPackage, + @NonNull SessionParams sessionParams, + @NonNull IntentSender statusReceiver) { + Objects.requireNonNull(archivedPackage, "archivedPackage cannot be null"); + Objects.requireNonNull(sessionParams, "sessionParams cannot be null"); + Objects.requireNonNull(statusReceiver, "statusReceiver cannot be null"); + try { + mInstaller.installPackageArchived( + archivedPackage.getParcel(), + sessionParams, + statusReceiver, + mInstallerPackageName, + new UserHandle(mUserId)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** {@hide} */ @SystemApi @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b15c9e4fa15bd7bfdc3626d54a77b92a1e27cea6..6d4276d4f47a3614c37ccc457c1fc22d01308a91 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -11026,6 +11026,16 @@ public abstract class PackageManager { "makeUidVisible not implemented in subclass"); } + /** + * Return archived package info for the package or null if the package is not installed. + * @see PackageInstaller#installPackageArchived + */ + @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING) + public @Nullable ArchivedPackage getArchivedPackage(@NonNull String packageName) { + throw new UnsupportedOperationException( + "getArchivedPackage not implemented in subclass"); + } + // Some of the flags don't affect the query result, but let's be conservative and cache // each combination of flags separately. diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java index ee9aaca3ed43e7604a2c3999ddace9806af87167..554de0c2ea7b897b3d3e2bb3e853028eb9392405 100644 --- a/core/java/android/content/pm/SigningInfo.java +++ b/core/java/android/content/pm/SigningInfo.java @@ -126,6 +126,12 @@ public final class SigningInfo implements Parcelable { mSigningDetails.writeToParcel(dest, parcelableFlags); } + /* @hide */ + @NonNull + SigningDetails getSigningDetails() { + return mSigningDetails; + } + public static final @android.annotation.NonNull Parcelable.Creator<SigningInfo> CREATOR = new Parcelable.Creator<SigningInfo>() { @Override diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index bf77681bbbbd6cea1134cf3d5a1a93a8200716fd..db7055b1756d4ab460a83e27dcb8b8c73e6939d1 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -357,7 +357,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mCameraRepeatingSurface = mRepeatingRequestImageReader.getSurface(); } mRepeatingRequestImageCallback = new CameraOutputImageCallback( - mRepeatingRequestImageReader); + mRepeatingRequestImageReader, true /*pruneOlderBuffers*/); mRepeatingRequestImageReader .setOnImageAvailableListener(mRepeatingRequestImageCallback, mHandler); } @@ -398,7 +398,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT); } - mBurstCaptureImageCallback = new CameraOutputImageCallback(mBurstCaptureImageReader); + mBurstCaptureImageCallback = new CameraOutputImageCallback(mBurstCaptureImageReader, + false /*pruneOlderBuffers*/); mBurstCaptureImageReader.setOnImageAvailableListener(mBurstCaptureImageCallback, mHandler); mCameraBurstSurface = mBurstCaptureImageReader.getSurface(); @@ -1106,7 +1107,9 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } for (Pair<Image, TotalCaptureResult> captureStage : mCaptureStageMap.values()) { - captureStage.first.close(); + if (captureStage.first != null) { + captureStage.first.close(); + } } mCaptureStageMap.clear(); } @@ -1207,6 +1210,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { if (mImageProcessor != null) { if (mCapturePendingMap.indexOfKey(timestamp) >= 0) { Image img = mCapturePendingMap.get(timestamp).first; + mCapturePendingMap.remove(timestamp); mCaptureStageMap.put(stageId, new Pair<>(img, result)); checkAndFireBurstProcessing(); } else { @@ -1303,6 +1307,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { reader.detachImage(img); if (mCapturePendingMap.indexOfKey(timestamp) >= 0) { Integer stageId = mCapturePendingMap.get(timestamp).second; + mCapturePendingMap.remove(timestamp); Pair<Image, TotalCaptureResult> captureStage = mCaptureStageMap.get(stageId); if (captureStage != null) { @@ -1402,9 +1407,11 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private HashMap<Long, Pair<Image, OnImageAvailableListener>> mImageListenerMap = new HashMap<>(); private boolean mOutOfBuffers = false; + private final boolean mPruneOlderBuffers; - CameraOutputImageCallback(ImageReader imageReader) { + CameraOutputImageCallback(ImageReader imageReader, boolean pruneOlderBuffers) { mImageReader = imageReader; + mPruneOlderBuffers = pruneOlderBuffers; } @Override @@ -1447,6 +1454,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { ArrayList<Long> removedTs = new ArrayList<>(); for (long ts : timestamps) { if (ts < timestamp) { + if (!mPruneOlderBuffers) { + Log.w(TAG, "Unexpected older image with ts: " + ts); + continue; + } Log.e(TAG, "Dropped image with ts: " + ts); Pair<Image, OnImageAvailableListener> entry = mImageListenerMap.get(ts); if (entry.second != null) { diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS index 861e4409b0149f1409a9674db6d342236c9a55fa..6952e5d78d9891662ecb77c4423529638e0b90bf 100644 --- a/core/java/android/hardware/hdmi/OWNERS +++ b/core/java/android/hardware/hdmi/OWNERS @@ -2,5 +2,4 @@ include /services/core/java/com/android/server/display/OWNERS -marvinramin@google.com -lcnathalie@google.com +quxiangfang@google.com diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig index 55b0b4261763e3066ab8b60ea102d0abdc85aacd..cd50ace036def70da35457d552221e9df24ffc96 100644 --- a/core/java/android/nfc/flags.aconfig +++ b/core/java/android/nfc/flags.aconfig @@ -13,3 +13,10 @@ flag { description: "Flag for NFC reader option API changes" bug: "291187960" } + +flag { + name: "enable_nfc_user_restriction" + namespace: "nfc" + description: "Flag for NFC user restriction" + bug: "291187960" +} diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 9034ff10286b8f7da2cdf133db5c81ba81b39d92..72bc2113f93f37d954ff73ffe7b4ea15963af92a 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -23,6 +23,7 @@ import android.Manifest; import android.accounts.AccountManager; import android.annotation.ColorInt; import android.annotation.DrawableRes; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -58,6 +59,7 @@ import android.graphics.BitmapFactory; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.location.LocationManager; +import android.nfc.Flags; import android.provider.Settings; import android.util.AndroidException; import android.util.ArraySet; @@ -1871,6 +1873,7 @@ public class UserManager { * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) * @see #getUserRestrictions() */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_USER_RESTRICTION) public static final String DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO = "no_near_field_communication_radio"; diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java index 65a1da6b81b84a883c2c72e9f7b052896d7debb3..4c8188801eff6b49e8cb5386cb83f38a362b30f4 100644 --- a/core/java/android/text/BoringLayout.java +++ b/core/java/android/text/BoringLayout.java @@ -190,7 +190,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth, boolean useFallbackLineSpacing) { return replaceOrMake(source, paint, outerWidth, align, 1.0f, 0.0f, metrics, includePad, - ellipsize, ellipsizedWidth, useFallbackLineSpacing, false /* useBoundsForWidth */); + ellipsize, ellipsizedWidth, useFallbackLineSpacing, false /* useBoundsForWidth */, + null /* minimumFontMetrics */); } /** @hide */ @@ -199,7 +200,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback @NonNull Alignment align, float spacingMultiplier, float spacingAmount, @NonNull BoringLayout.Metrics metrics, boolean includePad, @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth, - boolean useFallbackLineSpacing, boolean useBoundsForWidth) { + boolean useFallbackLineSpacing, boolean useBoundsForWidth, + @Nullable Paint.FontMetrics minimumFontMetrics) { boolean trust; if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) { @@ -270,7 +272,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback spacingAdd, includePad, false /* fallbackLineSpacing */, outerwidth /* ellipsizedWidth */, null /* ellipsize */, 1 /* maxLines */, BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */, - null /* rightIndents */, JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false); + null /* rightIndents */, JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false, + null); mEllipsizedWidth = outerwidth; mEllipsizedStart = 0; @@ -343,7 +346,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback ellipsizedWidth, ellipsize, 1 /* maxLines */, BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */, null /* rightIndents */, JUSTIFICATION_MODE_NONE, - LineBreakConfig.NONE, metrics, false /* useBoundsForWidth */); + LineBreakConfig.NONE, metrics, false /* useBoundsForWidth */, null); } /** @hide */ @@ -359,12 +362,13 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback int ellipsizedWidth, TextUtils.TruncateAt ellipsize, Metrics metrics, - boolean useBoundsForWidth) { + boolean useBoundsForWidth, + @Nullable Paint.FontMetrics minimumFontMetrics) { this(text, paint, width, align, TextDirectionHeuristics.LTR, spacingMult, spacingAdd, includePad, fallbackLineSpacing, ellipsizedWidth, ellipsize, 1 /* maxLines */, Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE, null, null, Layout.JUSTIFICATION_MODE_NONE, - LineBreakConfig.NONE, metrics, useBoundsForWidth); + LineBreakConfig.NONE, metrics, useBoundsForWidth, minimumFontMetrics); } /* package */ BoringLayout( @@ -387,12 +391,13 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback int justificationMode, LineBreakConfig lineBreakConfig, Metrics metrics, - boolean useBoundsForWidth) { + boolean useBoundsForWidth, + @Nullable Paint.FontMetrics minimumFontMetrics) { super(text, paint, width, align, textDir, spacingMult, spacingAdd, includePad, fallbackLineSpacing, ellipsizedWidth, ellipsize, maxLines, breakStrategy, hyphenationFrequency, leftIndents, rightIndents, justificationMode, - lineBreakConfig, useBoundsForWidth); + lineBreakConfig, useBoundsForWidth, minimumFontMetrics); boolean trust; @@ -548,6 +553,15 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing, @Nullable Metrics metrics) { + return isBoring(text, paint, textDir, useFallbackLineSpacing, null, metrics); + } + + /** + * @hide + */ + public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint, + @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing, + @Nullable Paint.FontMetrics minimumFontMetrics, @Nullable Metrics metrics) { final int textLength = text.length(); if (hasAnyInterestingChars(text, textLength)) { return null; // There are some interesting characters. Not boring. @@ -570,6 +584,19 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback fm.reset(); } + if (ClientFlags.fixLineHeightForLocale()) { + if (minimumFontMetrics == null) { + paint.getFontMetricsIntForLocale(fm); + } else { + fm.set(minimumFontMetrics); + // Because the font metrics is provided by public APIs, adjust the top/bottom with + // ascent/descent: top must be smaller than ascent, bottom must be larger than + // descent. + fm.top = Math.min(fm.top, fm.ascent); + fm.bottom = Math.max(fm.bottom, fm.descent); + } + } + TextLine line = TextLine.obtain(); line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT, Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null, diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java index e17a955121b0df02cd47c3e407f1d1831ac18cd3..0421d5aaa69b11bb13c9134e1bfa5c27cc85664c 100644 --- a/core/java/android/text/ClientFlags.java +++ b/core/java/android/text/ClientFlags.java @@ -47,4 +47,11 @@ public class ClientFlags { public static boolean useBoundsForWidth() { return TextFlags.isFeatureEnabled(Flags.FLAG_USE_BOUNDS_FOR_WIDTH); } + + /** + * @see Flags#fixLineHeightForLocale() + */ + public static boolean fixLineHeightForLocale() { + return TextFlags.isFeatureEnabled(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE); + } } diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index a0cd0748f5094c977411dc53518c967c450ffd20..7b9cb6afd6a0d0a4d457d5f589468e26575984a2 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -16,6 +16,7 @@ package android.text; +import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE; import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN; import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH; @@ -314,6 +315,43 @@ public class DynamicLayout extends Layout { return this; } + /** + * Set the minimum font metrics used for line spacing. + * + * <p> + * {@code null} is the default value. If {@code null} is set or left as default, the + * font metrics obtained by {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} is + * used. + * + * <p> + * The minimum meaning here is the minimum value of line spacing: maximum value of + * {@link Paint#ascent()}, minimum value of {@link Paint#descent()}. + * + * <p> + * By setting this value, each line will have minimum line spacing regardless of the text + * rendered. For example, usually Japanese script has larger vertical metrics than Latin + * script. By setting the metrics obtained by + * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} for Japanese or leave it + * {@code null} if the Paint's locale is Japanese, the line spacing for Japanese is reserved + * if the text is an English text. If the vertical metrics of the text is larger than + * Japanese, for example Burmese, the bigger font metrics is used. + * + * @param minimumFontMetrics A minimum font metrics. Passing {@code null} for using the + * value obtained by + * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} + * @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics) + * @see android.widget.TextView#getMinimumFontMetrics() + * @see Layout#getMinimumFontMetrics() + * @see Layout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * @see StaticLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + */ + @NonNull + @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) + public Builder setMinimumFontMetrics(@Nullable Paint.FontMetrics minimumFontMetrics) { + mMinimumFontMetrics = minimumFontMetrics; + return this; + } + /** * Build the {@link DynamicLayout} after options have been set. * @@ -347,6 +385,7 @@ public class DynamicLayout extends Layout { private int mEllipsizedWidth; private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE; private boolean mUseBoundsForWidth; + private @Nullable Paint.FontMetrics mMinimumFontMetrics; private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); @@ -422,7 +461,7 @@ public class DynamicLayout extends Layout { false /* fallbackLineSpacing */, ellipsizedWidth, ellipsize, Integer.MAX_VALUE /* maxLines */, breakStrategy, hyphenationFrequency, null /* leftIndents */, null /* rightIndents */, justificationMode, - lineBreakConfig, false /* useBoundsForWidth */); + lineBreakConfig, false /* useBoundsForWidth */, null /* minimumFontMetrics */); final Builder b = Builder.obtain(base, paint, width) .setAlignment(align) @@ -448,7 +487,7 @@ public class DynamicLayout extends Layout { b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize, Integer.MAX_VALUE /* maxLines */, b.mBreakStrategy, b.mHyphenationFrequency, null /* leftIndents */, null /* rightIndents */, b.mJustificationMode, - b.mLineBreakConfig, b.mUseBoundsForWidth); + b.mLineBreakConfig, b.mUseBoundsForWidth, b.mMinimumFontMetrics); mDisplay = b.mDisplay; mIncludePad = b.mIncludePad; @@ -476,6 +515,7 @@ public class DynamicLayout extends Layout { mBase = b.mBase; mFallbackLineSpacing = b.mFallbackLineSpacing; mUseBoundsForWidth = b.mUseBoundsForWidth; + mMinimumFontMetrics = b.mMinimumFontMetrics; if (b.mEllipsize != null) { mInts = new PackedIntVector(COLUMNS_ELLIPSIZE); mEllipsizedWidth = b.mEllipsizedWidth; @@ -672,6 +712,7 @@ public class DynamicLayout extends Layout { .setAddLastLineLineSpacing(!islast) .setIncludePad(false) .setUseBoundsForWidth(mUseBoundsForWidth) + .setMinimumFontMetrics(mMinimumFontMetrics) .setCalculateBounds(true); reflowed = b.buildPartialStaticLayoutForDynamicLayout(true /* trackpadding */, reflowed); @@ -1324,6 +1365,7 @@ public class DynamicLayout extends Layout { private Rect mTempRect = new Rect(); private boolean mUseBoundsForWidth; + @Nullable Paint.FontMetrics mMinimumFontMetrics; @UnsupportedAppUsage private static StaticLayout sStaticLayout = null; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 4f4dea7801713ef652b992c19b97a0aa50b73a16..47c29d96855868c527ea4c482bff571d8de6fc65 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -16,6 +16,7 @@ package android.text; +import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE; import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH; import android.annotation.FlaggedApi; @@ -287,7 +288,7 @@ public abstract class Layout { this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, spacingMult, spacingAdd, false, false, 0, null, Integer.MAX_VALUE, BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null, null, - JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false); + JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false, null); } /** @@ -336,7 +337,8 @@ public abstract class Layout { int[] rightIndents, int justificationMode, LineBreakConfig lineBreakConfig, - boolean useBoundsForWidth + boolean useBoundsForWidth, + Paint.FontMetrics minimumFontMetrics ) { if (width < 0) @@ -371,6 +373,7 @@ public abstract class Layout { mJustificationMode = justificationMode; mLineBreakConfig = lineBreakConfig; mUseBoundsForWidth = useBoundsForWidth; + mMinimumFontMetrics = minimumFontMetrics; } /** @@ -3332,6 +3335,7 @@ public abstract class Layout { private int mJustificationMode; private LineBreakConfig mLineBreakConfig; private boolean mUseBoundsForWidth; + private @Nullable Paint.FontMetrics mMinimumFontMetrics; /** @hide */ @IntDef(prefix = { "DIR_" }, value = { @@ -3787,12 +3791,48 @@ public abstract class Layout { return this; } + /** + * Set the minimum font metrics used for line spacing. + * + * <p> + * {@code null} is the default value. If {@code null} is set or left it as default, the font + * metrics obtained by {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} is used. + * + * <p> + * The minimum meaning here is the minimum value of line spacing: maximum value of + * {@link Paint#ascent()}, minimum value of {@link Paint#descent()}. + * + * <p> + * By setting this value, each line will have minimum line spacing regardless of the text + * rendered. For example, usually Japanese script has larger vertical metrics than Latin + * script. By setting the metrics obtained by + * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} for Japanese or leave it + * {@code null} if the Paint's locale is Japanese, the line spacing for Japanese is reserved + * if the text is an English text. If the vertical metrics of the text is larger than + * Japanese, for example Burmese, the bigger font metrics is used. + * + * @param minimumFontMetrics A minimum font metrics. Passing {@code null} for using the + * value obtained by + * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} + * @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics) + * @see android.widget.TextView#getMinimumFontMetrics() + * @see Layout#getMinimumFontMetrics() + * @see StaticLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * @see DynamicLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + */ + @NonNull + @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) + public Builder setMinimumFontMetrics(@Nullable Paint.FontMetrics minimumFontMetrics) { + mMinimumFontMetrics = minimumFontMetrics; + return this; + } + private BoringLayout.Metrics isBoring() { if (mStart != 0 || mEnd != mText.length()) { // BoringLayout only support entire text. return null; } BoringLayout.Metrics metrics = BoringLayout.isBoring(mText, mPaint, mTextDir, - mFallbackLineSpacing, null); + mFallbackLineSpacing, mMinimumFontMetrics, null); if (metrics == null) { return null; } @@ -3833,7 +3873,8 @@ public abstract class Layout { mText, mPaint, mWidth, mAlignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mFallbackLineSpacing, mEllipsizedWidth, mEllipsize, mMaxLines, mBreakStrategy, mHyphenationFrequency, mLeftIndents, mRightIndents, - mJustificationMode, mLineBreakConfig, metrics, mUseBoundsForWidth); + mJustificationMode, mLineBreakConfig, metrics, mUseBoundsForWidth, + mMinimumFontMetrics); } } @@ -3858,6 +3899,7 @@ public abstract class Layout { private int mJustificationMode = JUSTIFICATION_MODE_NONE; private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE; private boolean mUseBoundsForWidth; + private Paint.FontMetrics mMinimumFontMetrics; } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -4164,4 +4206,22 @@ public abstract class Layout { public boolean getUseBoundsForWidth() { return mUseBoundsForWidth; } + + /** + * Get the minimum font metrics used for line spacing. + * + * @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics) + * @see android.widget.TextView#getMinimumFontMetrics() + * @see Layout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * @see StaticLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * @see DynamicLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * + * @return a minimum font metrics. {@code null} for using the value obtained by + * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} + */ + @Nullable + @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) + public Paint.FontMetrics getMinimumFontMetrics() { + return mMinimumFontMetrics; + } } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 01279cea073f2c3034a760a04b80315ccf35ee03..77e616b358cba716fb805cf57784ef50b4db0ecb 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -16,6 +16,7 @@ package android.text; +import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE; import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH; import android.annotation.FlaggedApi; @@ -459,6 +460,43 @@ public class StaticLayout extends Layout { return this; } + /** + * Set the minimum font metrics used for line spacing. + * + * <p> + * {@code null} is the default value. If {@code null} is set or left as default, the + * font metrics obtained by {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} is + * used. + * + * <p> + * The minimum meaning here is the minimum value of line spacing: maximum value of + * {@link Paint#ascent()}, minimum value of {@link Paint#descent()}. + * + * <p> + * By setting this value, each line will have minimum line spacing regardless of the text + * rendered. For example, usually Japanese script has larger vertical metrics than Latin + * script. By setting the metrics obtained by + * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} for Japanese or leave it + * {@code null} if the Paint's locale is Japanese, the line spacing for Japanese is reserved + * if the text is an English text. If the vertical metrics of the text is larger than + * Japanese, for example Burmese, the bigger font metrics is used. + * + * @param minimumFontMetrics A minimum font metrics. Passing {@code null} for using the + * value obtained by + * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} + * @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics) + * @see android.widget.TextView#getMinimumFontMetrics() + * @see Layout#getMinimumFontMetrics() + * @see Layout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * @see DynamicLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + */ + @NonNull + @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) + public Builder setMinimumFontMetrics(@Nullable Paint.FontMetrics minimumFontMetrics) { + mMinimumFontMetrics = minimumFontMetrics; + return this; + } + /** * Build the {@link StaticLayout} after options have been set. * @@ -520,6 +558,7 @@ public class StaticLayout extends Layout { private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE; private boolean mUseBoundsForWidth; private boolean mCalculateBounds; + @Nullable private Paint.FontMetrics mMinimumFontMetrics; private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); @@ -550,7 +589,8 @@ public class StaticLayout extends Layout { null, // rightIndents JUSTIFICATION_MODE_NONE, null, // lineBreakConfig, - false // useBoundsForWidth + false, // useBoundsForWidth + null // minimumFontMetrics ); mColumns = COLUMNS_ELLIPSIZE; @@ -627,7 +667,8 @@ public class StaticLayout extends Layout { b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd, b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize, b.mMaxLines, b.mBreakStrategy, b.mHyphenationFrequency, b.mLeftIndents, - b.mRightIndents, b.mJustificationMode, b.mLineBreakConfig, b.mUseBoundsForWidth); + b.mRightIndents, b.mJustificationMode, b.mLineBreakConfig, b.mUseBoundsForWidth, + b.mMinimumFontMetrics); mColumns = columnSize; if (b.mEllipsize != null) { @@ -711,6 +752,35 @@ public class StaticLayout extends Layout { indents = null; } + int defaultTop; + int defaultAscent; + int defaultDescent; + int defaultBottom; + if (ClientFlags.fixLineHeightForLocale()) { + if (b.mMinimumFontMetrics != null) { + defaultTop = (int) Math.floor(b.mMinimumFontMetrics.top); + defaultAscent = Math.round(b.mMinimumFontMetrics.ascent); + defaultDescent = Math.round(b.mMinimumFontMetrics.descent); + defaultBottom = (int) Math.ceil(b.mMinimumFontMetrics.bottom); + } else { + paint.getFontMetricsIntForLocale(fm); + defaultTop = fm.top; + defaultAscent = fm.ascent; + defaultDescent = fm.descent; + defaultBottom = fm.bottom; + } + + // Because the font metrics is provided by public APIs, adjust the top/bottom with + // ascent/descent: top must be smaller than ascent, bottom must be larger than descent. + defaultTop = Math.min(defaultTop, defaultAscent); + defaultBottom = Math.max(defaultBottom, defaultDescent); + } else { + defaultTop = 0; + defaultAscent = 0; + defaultDescent = 0; + defaultBottom = 0; + } + final LineBreaker lineBreaker = new LineBreaker.Builder() .setBreakStrategy(b.mBreakStrategy) .setHyphenationFrequency(getBaseHyphenationFrequency(b.mHyphenationFrequency)) @@ -889,7 +959,10 @@ public class StaticLayout extends Layout { // measuring int here = paraStart; - int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0; + int fmTop = defaultTop; + int fmBottom = defaultBottom; + int fmAscent = defaultAscent; + int fmDescent = defaultDescent; int fmCacheIndex = 0; int spanEndCacheIndex = 0; int breakIndex = 0; @@ -982,7 +1055,15 @@ public class StaticLayout extends Layout { && mLineCount < mMaximumVisibleLineCount) { final MeasuredParagraph measuredPara = MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null); - paint.getFontMetricsInt(fm); + if (ClientFlags.fixLineHeightForLocale()) { + fm.top = defaultTop; + fm.ascent = defaultAscent; + fm.descent = defaultDescent; + fm.bottom = defaultBottom; + } else { + paint.getFontMetricsInt(fm); + } + v = out(source, bufEnd, bufEnd, fm.ascent, fm.descent, fm.top, fm.bottom, diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java index b8b30c230e5e24c315a3993107d4995fe33cd1f1..24663862400dc8ba5925f3e3d9f470341442c693 100644 --- a/core/java/android/text/TextFlags.java +++ b/core/java/android/text/TextFlags.java @@ -58,6 +58,7 @@ public final class TextFlags { Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN, Flags.FLAG_PHRASE_STRICT_FALLBACK, Flags.FLAG_USE_BOUNDS_FOR_WIDTH, + Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE, }; /** @@ -69,6 +70,7 @@ public final class TextFlags { Flags.noBreakNoHyphenationSpan(), Flags.phraseStrictFallback(), Flags.useBoundsForWidth(), + Flags.fixLineHeightForLocale(), }; /** diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index fb24211e591a9e88cb46418c7a4864120024d7d9..90663c7ad38ed8df44a2a9eff38755987c3cb671 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1283,7 +1283,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @AnimationType int animationType, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) { - ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION); if ((types & mTypesBeingCancelled) != 0) { final boolean monitoredAnimation = animationType == ANIMATION_TYPE_SHOW || animationType == ANIMATION_TYPE_HIDE; @@ -1295,12 +1294,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation ImeTracker.forLatency().onHideCancelled(statsToken, PHASE_CLIENT_ANIMATION_CANCEL, ActivityThread::currentApplication); } + ImeTracker.forLogging().onCancelled(statsToken, + ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION); } throw new IllegalStateException("Cannot start a new insets animation of " + Type.toString(types) + " while an existing " + Type.toString(mTypesBeingCancelled) + " is being cancelled."); } + ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION); if (types == 0) { // nothing to animate. listener.onCancelled(null); @@ -1309,8 +1311,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); return; } - ImeTracker.forLogging().onProgress(statsToken, - ImeTracker.PHASE_CLIENT_DISABLED_USER_ANIMATION); if (DEBUG) Log.d(TAG, "controlAnimation types: " + types); mLastStartedAnimTypes |= types; diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 42b3e38b544fa9bd1a466bd59f1a2f2108b3222b..57011e8794540226b3d0e5901e8995392709a9e9 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -363,14 +363,6 @@ public final class ContentCaptureManager { public static final String DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER = "enable_content_protection_receiver"; - /** - * Sets the size of the app blocklist for the content protection flow. - * - * @hide - */ - public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = - "content_protection_apps_blocklist_size"; - /** * Sets the size of the in-memory ring buffer for the content protection flow. * @@ -440,8 +432,6 @@ public final class ContentCaptureManager { /** @hide */ public static final boolean DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER = false; /** @hide */ - public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 5000; - /** @hide */ public static final int DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE = 150; /** @hide */ public static final List<List<String>> DEFAULT_CONTENT_PROTECTION_REQUIRED_GROUPS = diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java index f9d8b083ee9ca7bed81a424cae35f1c28e056c4d..1bc7353971156fcfd9dccfb71e506b29c7dd0c20 100644 --- a/core/java/android/view/inputmethod/ImeTracker.java +++ b/core/java/android/view/inputmethod/ImeTracker.java @@ -172,7 +172,6 @@ public interface ImeTracker { PHASE_CLIENT_HANDLE_HIDE_INSETS, PHASE_CLIENT_APPLY_ANIMATION, PHASE_CLIENT_CONTROL_ANIMATION, - PHASE_CLIENT_DISABLED_USER_ANIMATION, PHASE_CLIENT_COLLECT_SOURCE_CONTROLS, PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW, PHASE_CLIENT_REQUEST_IME_SHOW, @@ -292,9 +291,6 @@ public interface ImeTracker { /** Started the IME window insets show animation. */ int PHASE_CLIENT_CONTROL_ANIMATION = ImeProtoEnums.PHASE_CLIENT_CONTROL_ANIMATION; - /** Checked that the IME is controllable. */ - int PHASE_CLIENT_DISABLED_USER_ANIMATION = ImeProtoEnums.PHASE_CLIENT_DISABLED_USER_ANIMATION; - /** Collecting insets source controls. */ int PHASE_CLIENT_COLLECT_SOURCE_CONTROLS = ImeProtoEnums.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a0628c4b95804d2ee043d2e0c217e45a4b1339d5..6da6a64dc0423bc93bfa53fdfdad4cad0703c571 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -28,6 +28,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_C import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY; import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; +import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE; import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH; import android.R; @@ -865,6 +866,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private final boolean mUseTextPaddingForUiTranslation; private boolean mUseBoundsForWidth; + @Nullable private Paint.FontMetrics mMinimumFontMetrics; @ViewDebug.ExportedProperty(category = "text") @UnsupportedAppUsage @@ -4900,6 +4902,58 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mUseBoundsForWidth; } + /** + * Set the minimum font metrics used for line spacing. + * + * <p> + * {@code null} is the default value. If {@code null} is set or left as default, the font + * metrics obtained by {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} is used. + * + * <p> + * The minimum meaning here is the minimum value of line spacing: maximum value of + * {@link Paint#ascent()}, minimum value of {@link Paint#descent()}. + * + * <p> + * By setting this value, each line will have minimum line spacing regardless of the text + * rendered. For example, usually Japanese script has larger vertical metrics than Latin script. + * By setting the metrics obtained by {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} + * for Japanese or leave it {@code null} if the TextView's locale or system locale is Japanese, + * the line spacing for Japanese is reserved if the TextView contains English text. If the + * vertical metrics of the text is larger than Japanese, for example Burmese, the bigger font + * metrics is used. + * + * @param minimumFontMetrics A minimum font metrics. Passing {@code null} for using the value + * obtained by + * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} + * @see #getMinimumFontMetrics() + * @see Layout#getMinimumFontMetrics() + * @see Layout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * @see StaticLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * @see DynamicLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + */ + @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) + public void setMinimumFontMetrics(@Nullable Paint.FontMetrics minimumFontMetrics) { + mMinimumFontMetrics = minimumFontMetrics; + } + + /** + * Get the minimum font metrics used for line spacing. + * + * @see #setMinimumFontMetrics(Paint.FontMetrics) + * @see Layout#getMinimumFontMetrics() + * @see Layout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * @see StaticLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * @see DynamicLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics) + * + * @return a minimum font metrics. {@code null} for using the value obtained by + * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} + */ + @Nullable + @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) + public Paint.FontMetrics getMinimumFontMetrics() { + return mMinimumFontMetrics; + } + /** * @return whether fallback line spacing is enabled, {@code true} by default * @@ -10683,7 +10737,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (hintBoring == UNKNOWN_BORING) { hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, - isFallbackLineSpacingForBoringLayout(), mHintBoring); + isFallbackLineSpacingForBoringLayout(), + mMinimumFontMetrics, mHintBoring); if (hintBoring != null) { mHintBoring = hintBoring; } @@ -10732,7 +10787,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE) .setLineBreakConfig(LineBreakConfig.getLineBreakConfig( mLineBreakStyle, mLineBreakWordStyle)) - .setUseBoundsForWidth(mUseBoundsForWidth); + .setUseBoundsForWidth(mUseBoundsForWidth) + .setMinimumFontMetrics(mMinimumFontMetrics); if (shouldEllipsize) { builder.setEllipsize(mEllipsize) .setEllipsizedWidth(ellipsisWidth); @@ -10796,12 +10852,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mLineBreakStyle, mLineBreakWordStyle)) .setUseBoundsForWidth(mUseBoundsForWidth) .setEllipsize(getKeyListener() == null ? effectiveEllipsize : null) - .setEllipsizedWidth(ellipsisWidth); + .setEllipsizedWidth(ellipsisWidth) + .setMinimumFontMetrics(mMinimumFontMetrics); result = builder.build(); } else { if (boring == UNKNOWN_BORING) { boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, - isFallbackLineSpacingForBoringLayout(), mBoring); + isFallbackLineSpacingForBoringLayout(), mMinimumFontMetrics, mBoring); if (boring != null) { mBoring = boring; } @@ -10815,7 +10872,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener wantWidth, alignment, mSpacingMult, mSpacingAdd, boring, mIncludePad, null, wantWidth, isFallbackLineSpacingForBoringLayout(), - mUseBoundsForWidth); + mUseBoundsForWidth, mMinimumFontMetrics); } else { result = new BoringLayout( mTransformed, @@ -10829,7 +10886,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener wantWidth, null, boring, - mUseBoundsForWidth); + mUseBoundsForWidth, + mMinimumFontMetrics); } if (useSaved) { @@ -10841,7 +10899,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener wantWidth, alignment, mSpacingMult, mSpacingAdd, boring, mIncludePad, effectiveEllipsize, ellipsisWidth, isFallbackLineSpacingForBoringLayout(), - mUseBoundsForWidth); + mUseBoundsForWidth, mMinimumFontMetrics); } else { result = new BoringLayout( mTransformed, @@ -10855,7 +10913,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ellipsisWidth, effectiveEllipsize, boring, - mUseBoundsForWidth); + mUseBoundsForWidth, + mMinimumFontMetrics); } } } @@ -10874,7 +10933,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE) .setLineBreakConfig(LineBreakConfig.getLineBreakConfig( mLineBreakStyle, mLineBreakWordStyle)) - .setUseBoundsForWidth(mUseBoundsForWidth); + .setUseBoundsForWidth(mUseBoundsForWidth) + .setMinimumFontMetrics(mMinimumFontMetrics); if (shouldEllipsize) { builder.setEllipsize(effectiveEllipsize) .setEllipsizedWidth(ellipsisWidth); @@ -11002,7 +11062,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (des < 0) { boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, - isFallbackLineSpacingForBoringLayout(), mBoring); + isFallbackLineSpacingForBoringLayout(), mMinimumFontMetrics, mBoring); if (boring != null) { mBoring = boring; } @@ -11042,7 +11102,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (hintDes < 0) { hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, - isFallbackLineSpacingForBoringLayout(), mHintBoring); + isFallbackLineSpacingForBoringLayout(), mMinimumFontMetrics, + mHintBoring); if (hintBoring != null) { mHintBoring = hintBoring; } @@ -11254,7 +11315,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setTextDirection(getTextDirectionHeuristic()) .setLineBreakConfig(LineBreakConfig.getLineBreakConfig( mLineBreakStyle, mLineBreakWordStyle)) - .setUseBoundsForWidth(mUseBoundsForWidth); + .setUseBoundsForWidth(mUseBoundsForWidth) + .setMinimumFontMetrics(mMinimumFontMetrics); final StaticLayout layout = layoutBuilder.build(); diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 79b3b4f686b40383a26626edc4a3fdb0ca520efc..4705dc5db90ba30161afbe2bcfd27f179798490e 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -23,7 +23,7 @@ flag { } flag { - name: "dimmer_refactor" + name: "introduce_smoother_dimmer" namespace: "windowing_frontend" description: "Refactor dim to fix flickers" bug: "295291019" diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 7e9cef720a136ee74b85b92c4fc50acc9c7754dc..e6b036cfaa19d6277ff8a743921f170dda33a3b2 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -58,6 +58,9 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD; @@ -276,7 +279,11 @@ public class InteractionJankMonitor { public static final int CUJ_LAUNCHER_UNFOLD_ANIM = 83; - private static final int LAST_CUJ = CUJ_LAUNCHER_UNFOLD_ANIM; + public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84; + public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85; + public static final int CUJ_PREDICTIVE_BACK_HOME = 86; + + private static final int LAST_CUJ = CUJ_PREDICTIVE_BACK_HOME; private static final int NO_STATSD_LOGGING = -1; // Used to convert CujType to InteractionType enum value for statsd logging. @@ -370,6 +377,12 @@ public class InteractionJankMonitor { CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_HIDE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_HIDE_ANIMATION; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_DOUBLE_TAP_DIVIDER; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNFOLD_ANIM] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNFOLD_ANIM; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME; } private static class InstanceHolder { @@ -473,6 +486,9 @@ public class InteractionJankMonitor { CUJ_IME_INSETS_HIDE_ANIMATION, CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER, CUJ_LAUNCHER_UNFOLD_ANIM, + CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY, + CUJ_PREDICTIVE_BACK_CROSS_TASK, + CUJ_PREDICTIVE_BACK_HOME, }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -1108,6 +1124,12 @@ public class InteractionJankMonitor { return "SPLIT_SCREEN_DOUBLE_TAP_DIVIDER"; case CUJ_LAUNCHER_UNFOLD_ANIM: return "LAUNCHER_UNFOLD_ANIM"; + case CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY: + return "PREDICTIVE_BACK_CROSS_ACTIVITY"; + case CUJ_PREDICTIVE_BACK_CROSS_TASK: + return "PREDICTIVE_BACK_CROSS_TASK"; + case CUJ_PREDICTIVE_BACK_HOME: + return "PREDICTIVE_BACK_HOME"; } return "UNKNOWN"; } diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp index f97d41b6a122eaffe3a794c2b117c4019e688e07..262f5e8e761e2f2373b76b788c3e8c8a48dcb988 100644 --- a/core/jni/android_view_InputDevice.cpp +++ b/core/jni/android_view_InputDevice.cpp @@ -42,13 +42,6 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi return NULL; } - // b/274058082: Pass a copy of the key character map to avoid concurrent - // access - std::shared_ptr<KeyCharacterMap> map = deviceInfo.getKeyCharacterMap(); - if (map != nullptr) { - map = std::make_shared<KeyCharacterMap>(*map); - } - ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(deviceInfo.getIdentifier().descriptor.c_str())); if (!descriptorObj.get()) { @@ -67,9 +60,14 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi ? layoutInfo->layoutType.c_str() : NULL)); + std::shared_ptr<KeyCharacterMap> map = deviceInfo.getKeyCharacterMap(); + std::unique_ptr<KeyCharacterMap> mapCopy; + if (map != nullptr) { + mapCopy = std::make_unique<KeyCharacterMap>(*map); + } ScopedLocalRef<jobject> kcmObj(env, android_view_KeyCharacterMap_create(env, deviceInfo.getId(), - map)); + std::move(mapCopy))); if (!kcmObj.get()) { return NULL; } diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp index 7f69e22fb0d1d1572802d09c4768e01357e14a17..a79e37afd4cd59bc35c6521bc7158d1696fa4efa 100644 --- a/core/jni/android_view_KeyCharacterMap.cpp +++ b/core/jni/android_view_KeyCharacterMap.cpp @@ -48,7 +48,7 @@ static struct { class NativeKeyCharacterMap { public: - NativeKeyCharacterMap(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) + NativeKeyCharacterMap(int32_t deviceId, std::unique_ptr<KeyCharacterMap> map) : mDeviceId(deviceId), mMap(std::move(map)) {} ~NativeKeyCharacterMap() { @@ -58,16 +58,18 @@ public: return mDeviceId; } - inline const std::shared_ptr<KeyCharacterMap> getMap() const { return mMap; } + inline const std::unique_ptr<KeyCharacterMap>& getMap() const { + return mMap; + } private: int32_t mDeviceId; - std::shared_ptr<KeyCharacterMap> mMap; + std::unique_ptr<KeyCharacterMap> mMap; }; jobject android_view_KeyCharacterMap_create(JNIEnv* env, int32_t deviceId, - const std::shared_ptr<KeyCharacterMap> kcm) { - NativeKeyCharacterMap* nativeMap = new NativeKeyCharacterMap(deviceId, kcm); + std::unique_ptr<KeyCharacterMap> kcm) { + NativeKeyCharacterMap* nativeMap = new NativeKeyCharacterMap(deviceId, std::move(kcm)); if (!nativeMap) { return nullptr; } @@ -91,7 +93,7 @@ static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) return 0; } - std::shared_ptr<KeyCharacterMap> kcm = nullptr; + std::unique_ptr<KeyCharacterMap> kcm; // Check if map is a null character map if (parcel->readBool()) { kcm = KeyCharacterMap::readFromParcel(parcel); @@ -99,7 +101,7 @@ static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) return 0; } } - NativeKeyCharacterMap* map = new NativeKeyCharacterMap(deviceId, kcm); + NativeKeyCharacterMap* map = new NativeKeyCharacterMap(deviceId, std::move(kcm)); return reinterpret_cast<jlong>(map); } @@ -230,9 +232,9 @@ static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jlong ptr, } static jboolean nativeEquals(JNIEnv* env, jobject clazz, jlong ptr1, jlong ptr2) { - const std::shared_ptr<KeyCharacterMap>& map1 = + const std::unique_ptr<KeyCharacterMap>& map1 = (reinterpret_cast<NativeKeyCharacterMap*>(ptr1))->getMap(); - const std::shared_ptr<KeyCharacterMap>& map2 = + const std::unique_ptr<KeyCharacterMap>& map2 = (reinterpret_cast<NativeKeyCharacterMap*>(ptr2))->getMap(); if (map1 == nullptr || map2 == nullptr) { return map1 == map2; diff --git a/core/jni/android_view_KeyCharacterMap.h b/core/jni/android_view_KeyCharacterMap.h index be0335380f872f93078b2d8e4d666227fc3b4f71..a8aabd1d464a218e111f6dfb4fbb22727d357ce1 100644 --- a/core/jni/android_view_KeyCharacterMap.h +++ b/core/jni/android_view_KeyCharacterMap.h @@ -25,7 +25,7 @@ namespace android { /* Creates a KeyCharacterMap object from the given information. */ extern jobject android_view_KeyCharacterMap_create(JNIEnv* env, int32_t deviceId, - const std::shared_ptr<KeyCharacterMap> kcm); + std::unique_ptr<KeyCharacterMap> kcm); } // namespace android diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index 7f3e01432dd9d78323c5cd8786bced973582c864..9430ba6a939a07e53d9c22f7fd6fd98d0e912045 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -857,7 +857,8 @@ public class NotificationTest { assertEquals(cDay.getPrimaryAccentColor(), cNight.getPrimaryAccentColor()); assertEquals(cDay.getSecondaryAccentColor(), cNight.getSecondaryAccentColor()); assertEquals(cDay.getTertiaryAccentColor(), cNight.getTertiaryAccentColor()); - assertEquals(cDay.getOnAccentTextColor(), cNight.getOnAccentTextColor()); + assertEquals(cDay.getOnTertiaryAccentTextColor(), + cNight.getOnTertiaryAccentTextColor()); assertEquals(cDay.getProtectionColor(), cNight.getProtectionColor()); assertEquals(cDay.getContrastColor(), cNight.getContrastColor()); assertEquals(cDay.getRippleAlpha(), cNight.getRippleAlpha()); @@ -1830,7 +1831,7 @@ public class NotificationTest { assertThat(c.getPrimaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID); assertThat(c.getSecondaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID); assertThat(c.getTertiaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID); - assertThat(c.getOnAccentTextColor()).isNotEqualTo(Notification.COLOR_INVALID); + assertThat(c.getOnTertiaryAccentTextColor()).isNotEqualTo(Notification.COLOR_INVALID); assertThat(c.getErrorColor()).isNotEqualTo(Notification.COLOR_INVALID); assertThat(c.getContrastColor()).isNotEqualTo(Notification.COLOR_INVALID); assertThat(c.getRippleAlpha()).isAtLeast(0x00); @@ -1848,7 +1849,7 @@ public class NotificationTest { assertContrastIsAtLeast(c.getTertiaryAccentColor(), c.getBackgroundColor(), 1); // The text that is used within the accent color DOES need to have contrast - assertContrastIsAtLeast(c.getOnAccentTextColor(), c.getTertiaryAccentColor(), 4.5); + assertContrastIsAtLeast(c.getOnTertiaryAccentTextColor(), c.getTertiaryAccentColor(), 4.5); } private void resolveColorsInNightMode(boolean nightMode, Notification.Colors c, int rawColor, diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java index 531404bffd5080ecfa9291b6117ba5e1ebadb939..d10cf16914083f4d2809060d7cda415281ba816f 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java @@ -62,4 +62,24 @@ public class ClientTransactionTests { verify(callback2, times(1)).preExecute(clientTransactionHandler); verify(stateRequest, times(1)).preExecute(clientTransactionHandler); } + + @Test + public void testPreExecuteTransactionItems() { + final ClientTransactionItem callback1 = mock(ClientTransactionItem.class); + final ClientTransactionItem callback2 = mock(ClientTransactionItem.class); + final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class); + final ClientTransactionHandler clientTransactionHandler = + mock(ClientTransactionHandler.class); + + final ClientTransaction transaction = ClientTransaction.obtain(null /* client */); + transaction.addTransactionItem(callback1); + transaction.addTransactionItem(callback2); + transaction.addTransactionItem(stateRequest); + + transaction.preExecute(clientTransactionHandler); + + verify(callback1, times(1)).preExecute(clientTransactionHandler); + verify(callback2, times(1)).preExecute(clientTransactionHandler); + verify(stateRequest, times(1)).preExecute(clientTransactionHandler); + } } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java index 44a4d580dbc0339939d6be5056dbd83194207c0c..f2b0f2e622b8327026c3870c24464273f36bb2a0 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java @@ -246,6 +246,31 @@ public class TransactionExecutorTests { inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any()); } + @Test + public void testExecuteTransactionItems_transactionResolution() { + ClientTransactionItem callback1 = mock(ClientTransactionItem.class); + when(callback1.getPostExecutionState()).thenReturn(UNDEFINED); + ClientTransactionItem callback2 = mock(ClientTransactionItem.class); + when(callback2.getPostExecutionState()).thenReturn(UNDEFINED); + ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class); + IBinder token = mock(IBinder.class); + when(stateRequest.getActivityToken()).thenReturn(token); + when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class)); + + ClientTransaction transaction = ClientTransaction.obtain(null /* client */); + transaction.addTransactionItem(callback1); + transaction.addTransactionItem(callback2); + transaction.addTransactionItem(stateRequest); + + transaction.preExecute(mTransactionHandler); + mExecutor.execute(transaction); + + InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, stateRequest); + inOrder.verify(callback1).execute(eq(mTransactionHandler), any()); + inOrder.verify(callback2).execute(eq(mTransactionHandler), any()); + inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any()); + } + @Test public void testDoNotLaunchDestroyedActivity() { final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>(); @@ -278,13 +303,44 @@ public class TransactionExecutorTests { assertTrue(activitiesToBeDestroyed.isEmpty()); } + @Test + public void testExecuteTransactionItems_doNotLaunchDestroyedActivity() { + final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>(); + when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed); + // Assume launch transaction is still in queue, so there is no client record. + when(mTransactionHandler.getActivityClient(any())).thenReturn(null); + + // An incoming destroy transaction enters binder thread (preExecute). + final IBinder token = mock(IBinder.class); + final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */); + destroyTransaction.addTransactionItem( + DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */)); + destroyTransaction.preExecute(mTransactionHandler); + // The activity should be added to to-be-destroyed container. + assertEquals(1, activitiesToBeDestroyed.size()); + + // A previous queued launch transaction runs on main thread (execute). + final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */); + final LaunchActivityItem launchItem = + spy(new LaunchActivityItemBuilder().setActivityToken(token).build()); + launchTransaction.addTransactionItem(launchItem); + mExecutor.execute(launchTransaction); + + // The launch transaction should not be executed because its token is in the + // to-be-destroyed container. + verify(launchItem, never()).execute(any(), any()); + + // After the destroy transaction has been executed, the token should be removed. + mExecutor.execute(destroyTransaction); + assertTrue(activitiesToBeDestroyed.isEmpty()); + } + @Test public void testActivityResultRequiredStateResolution() { when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class)); PostExecItem postExecItem = new PostExecItem(ON_RESUME); - IBinder token = mock(IBinder.class); ClientTransaction transaction = ClientTransaction.obtain(null /* client */); transaction.addCallback(postExecItem); @@ -299,6 +355,26 @@ public class TransactionExecutorTests { verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction)); } + @Test + public void testExecuteTransactionItems_activityResultRequiredStateResolution() { + when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class)); + + PostExecItem postExecItem = new PostExecItem(ON_RESUME); + + ClientTransaction transaction = ClientTransaction.obtain(null /* client */); + transaction.addTransactionItem(postExecItem); + + // Verify resolution that should get to onPause + mClientRecord.setState(ON_RESUME); + mExecutor.executeTransactionItems(transaction); + verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction)); + + // Verify resolution that should get to onStart + mClientRecord.setState(ON_STOP); + mExecutor.executeTransactionItems(transaction); + verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction)); + } + @Test public void testClosestStateResolutionForSameState() { final int[] allStates = new int[] { @@ -444,6 +520,18 @@ public class TransactionExecutorTests { mExecutor.executeCallbacks(transaction); } + @Test(expected = IllegalArgumentException.class) + public void testExecuteTransactionItems_activityItemNullRecordThrowsException() { + final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class); + when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED); + final IBinder token = mock(IBinder.class); + final ClientTransaction transaction = ClientTransaction.obtain(null /* client */); + transaction.addTransactionItem(activityItem); + when(mTransactionHandler.getActivityClient(token)).thenReturn(null); + + mExecutor.executeTransactionItems(transaction); + } + @Test public void testActivityItemExecute() { final IBinder token = mock(IBinder.class); @@ -464,6 +552,26 @@ public class TransactionExecutorTests { inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any()); } + @Test + public void testExecuteTransactionItems_activityItemExecute() { + final IBinder token = mock(IBinder.class); + final ClientTransaction transaction = ClientTransaction.obtain(null /* client */); + final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class); + when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED); + when(activityItem.getActivityToken()).thenReturn(token); + transaction.addTransactionItem(activityItem); + final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class); + transaction.addTransactionItem(stateRequest); + when(stateRequest.getActivityToken()).thenReturn(token); + when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class)); + + mExecutor.execute(transaction); + + final InOrder inOrder = inOrder(activityItem, stateRequest); + inOrder.verify(activityItem).execute(eq(mTransactionHandler), eq(mClientRecord), any()); + inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any()); + } + private static int[] shuffledArray(int[] inputArray) { final List<Integer> list = Arrays.stream(inputArray).boxed().collect(Collectors.toList()); Collections.shuffle(list); diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 7d047c93520f490998f98040586d57e6e9ab42df..4aa62c503a41b8b10af4765735d39136058a684a 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -285,6 +285,10 @@ public class TransactionParcelTests { 78 /* configChanges */); ClientTransaction transaction = ClientTransaction.obtain(null /* client */); + transaction.addTransactionItem(callback1); + transaction.addTransactionItem(callback2); + transaction.addTransactionItem(lifecycleRequest); + transaction.addCallback(callback1); transaction.addCallback(callback2); transaction.setLifecycleStateRequest(lifecycleRequest); diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 9fde0fd6e6ab69f72aa4de2e20a154c68dc368e0..4eaa01309ab129a59fc14ec5f7a82f206661fce3 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -2113,6 +2113,31 @@ public class Paint { * The recommended additional space to add between lines of text. */ public float leading; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof FontMetrics)) return false; + FontMetrics that = (FontMetrics) o; + return that.top == top && that.ascent == ascent && that.descent == descent + && that.bottom == bottom && that.leading == leading; + } + + @Override + public int hashCode() { + return Objects.hash(top, ascent, descent, bottom, leading); + } + + @Override + public String toString() { + return "FontMetrics{" + + "top=" + top + + ", ascent=" + ascent + + ", descent=" + descent + + ", bottom=" + bottom + + ", leading=" + leading + + '}'; + } } /** @@ -2309,6 +2334,33 @@ public class Paint { */ public int leading; + /** + * Set values from {@link FontMetricsInt}. + * @param fontMetricsInt a font metrics. + */ + @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) + public void set(@NonNull FontMetricsInt fontMetricsInt) { + top = fontMetricsInt.top; + ascent = fontMetricsInt.ascent; + descent = fontMetricsInt.descent; + bottom = fontMetricsInt.bottom; + leading = fontMetricsInt.leading; + } + + /** + * Set values from {@link FontMetrics} with rounding accordingly. + * @param fontMetrics a font metrics. + */ + @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) + public void set(@NonNull FontMetrics fontMetrics) { + // See GraphicsJNI::set_metrics_int method for consistency. + top = (int) Math.floor(fontMetrics.top); + ascent = Math.round(fontMetrics.ascent); + descent = Math.round(fontMetrics.descent); + bottom = (int) Math.ceil(fontMetrics.bottom); + leading = Math.round(fontMetrics.leading); + } + @Override public String toString() { return "FontMetricsInt: top=" + top + " ascent=" + ascent + " descent=" + descent + " bottom=" + bottom + diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 2f1eec15e4155291004d85b976df0b526a2d78b1..49606f0bf4857c4ed1463b2834a21beefc2c5e7b 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -292,8 +292,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Resets the isolated navigation and updates the container. final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction(); final WindowContainerTransaction wct = transactionRecord.getTransaction(); - mPresenter.setTaskFragmentIsolatedNavigation(wct, - containerToUnpin.getTaskFragmentToken(), false /* isolated */); + mPresenter.setTaskFragmentIsolatedNavigation(wct, containerToUnpin, + false /* isolated */); updateContainer(wct, containerToUnpin); transactionRecord.apply(false /* shouldApplyIndependently */); updateCallbackIfNecessary(); @@ -880,19 +880,17 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } - // Skip resolving if the activity is on a pinned TaskFragmentContainer. - // TODO(b/243518738): skip resolving for overlay container. - final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null; - if (container != null && taskContainer != null - && taskContainer.isTaskFragmentContainerPinned(container)) { + // Skip resolving if the activity is on an isolated navigated TaskFragmentContainer. + if (container != null && container.isIsolatedNavigationEnabled()) { return true; } + final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null; if (!isOnReparent && taskContainer != null && taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */) != container) { // Do not resolve if the launched activity is not the top-most container (excludes - // the pinned container) in the Task. + // the pinned and overlay container) in the Task. return true; } @@ -1312,15 +1310,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @GuardedBy("mLock") TaskFragmentContainer resolveStartActivityIntent(@NonNull WindowContainerTransaction wct, int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) { - // Skip resolving if started from pinned TaskFragmentContainer. - // TODO(b/243518738): skip resolving for overlay container. + // Skip resolving if started from an isolated navigated TaskFragmentContainer. if (launchingActivity != null) { final TaskFragmentContainer taskFragmentContainer = getContainerWithActivity( launchingActivity); - final TaskContainer taskContainer = - taskFragmentContainer != null ? taskFragmentContainer.getTaskContainer() : null; - if (taskContainer != null && taskContainer.isTaskFragmentContainerPinned( - taskFragmentContainer)) { + if (taskFragmentContainer != null + && taskFragmentContainer.isIsolatedNavigationEnabled()) { return null; } } @@ -1755,31 +1750,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen false /* shouldFinishDependent */, mPresenter, wct, this); } - /** - * Returns the topmost not finished container in Task of given task id. - */ - @GuardedBy("mLock") - @Nullable - TaskFragmentContainer getTopActiveContainer(int taskId) { - final TaskContainer taskContainer = mTaskContainers.get(taskId); - if (taskContainer == null) { - return null; - } - final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers(); - for (int i = containers.size() - 1; i >= 0; i--) { - final TaskFragmentContainer container = containers.get(i); - if (!container.isFinished() && (container.getRunningActivityCount() > 0 - // We may be waiting for the top TaskFragment to become non-empty after - // creation. In that case, we don't want to treat the TaskFragment below it as - // top active, otherwise it may incorrectly launch placeholder on top of the - // pending TaskFragment. - || container.isWaitingActivityAppear())) { - return container; - } - } - return null; - } - /** * Updates the presentation of the container. If the container is part of the split or should * have a placeholder, it will also update the other part of the split. @@ -1968,7 +1938,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen /** Whether or not to allow activity in this container to launch placeholder. */ @GuardedBy("mLock") private boolean allowLaunchPlaceholder(@NonNull TaskFragmentContainer container) { - final TaskFragmentContainer topContainer = getTopActiveContainer(container.getTaskId()); + final TaskFragmentContainer topContainer = container.getTaskContainer() + .getTopNonFinishingTaskFragmentContainer(); if (container != topContainer) { // The container is not the top most. if (!container.isVisible()) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 66e76c59565200a8303074d36fc193303c153476..faf7c3999402a61ecd2fd5b2e022f613a0dbadf1 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -388,13 +388,26 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { return; } - setTaskFragmentIsolatedNavigation(wct, secondaryContainer.getTaskFragmentToken(), - !isStacked /* isolatedNav */); + setTaskFragmentIsolatedNavigation(wct, secondaryContainer, !isStacked /* isolatedNav */); if (isStacked && !splitPinRule.isSticky()) { secondaryContainer.getTaskContainer().removeSplitPinContainer(); } } + /** + * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer} + */ + void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct, + @NonNull TaskFragmentContainer taskFragmentContainer, + boolean isolatedNavigationEnabled) { + if (taskFragmentContainer.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) { + return; + } + taskFragmentContainer.setIsolatedNavigationEnabled(isolatedNavigationEnabled); + setTaskFragmentIsolatedNavigation(wct, taskFragmentContainer.getTaskFragmentToken(), + isolatedNavigationEnabled); + } + /** * Resizes the task fragment if it was already registered. Skips the operation if the container * creation has not been reported from the server yet. diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index f4427aaba182891a7c7d95d4715ef930ff6b7243..9e533808ccc0356afccc0aa4ca70859d2bfd6759 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -191,11 +191,20 @@ class TaskContainer { @Nullable TaskFragmentContainer getTopNonFinishingTaskFragmentContainer(boolean includePin) { + return getTopNonFinishingTaskFragmentContainer(includePin, false /* includeOverlay */); + } + + @Nullable + TaskFragmentContainer getTopNonFinishingTaskFragmentContainer(boolean includePin, + boolean includeOverlay) { for (int i = mContainers.size() - 1; i >= 0; i--) { final TaskFragmentContainer container = mContainers.get(i); if (!includePin && isTaskFragmentContainerPinned(container)) { continue; } + if (!includeOverlay && container.isOverlay()) { + continue; + } if (!container.isFinished()) { return container; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 2ba5c9b320f1ea9857c02f5460e8c866a8ae980d..3e7f99b96421550d331857d1cf827d0a74093614 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -160,6 +160,9 @@ class TaskFragmentContainer { */ private boolean mHasCrossProcessActivities; + /** Whether this TaskFragment enable isolated navigation. */ + private boolean mIsIsolatedNavigationEnabled; + /** * @see #TaskFragmentContainer(Activity, Intent, TaskContainer, SplitController, * TaskFragmentContainer, String) @@ -805,6 +808,16 @@ class TaskFragmentContainer { mLastCompanionTaskFragment = fragmentToken; } + /** Returns whether to enable isolated navigation or not. */ + boolean isIsolatedNavigationEnabled() { + return mIsIsolatedNavigationEnabled; + } + + /** Sets whether to enable isolated navigation or not. */ + void setIsolatedNavigationEnabled(boolean isolatedNavigationEnabled) { + mIsIsolatedNavigationEnabled = isolatedNavigationEnabled; + } + /** * Adds the pending appeared activity that has requested to be launched in this task fragment. * @see android.app.ActivityClient#isRequestedToLaunchInTaskFragment diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index af8017ae9544a0b7785ff8ae86d36575bd025aac..405f0b2f51dc89f6a314dbe17eb10074c3485ef2 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder; import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS; import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_BOUNDS; import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_TAG; @@ -364,6 +365,54 @@ public class OverlayPresentationTest { assertThat(overlayContainer.getOverlayTag()).isEqualTo(TEST_OVERLAY_CREATE_PARAMS.getTag()); } + @Test + public void testGetTopNonFishingTaskFragmentContainerWithOverlay() { + final TaskFragmentContainer overlayContainer = + createTestOverlayContainer(TASK_ID, "test1"); + + // Add a SplitPinContainer, the overlay should be on top + final Activity primaryActivity = createMockActivity(); + final Activity secondaryActivity = createMockActivity(); + + final TaskFragmentContainer primaryContainer = + createMockTaskFragmentContainer(primaryActivity); + final TaskFragmentContainer secondaryContainer = + createMockTaskFragmentContainer(secondaryActivity); + final SplitPairRule splitPairRule = createSplitPairRuleBuilder( + activityActivityPair -> true /* activityPairPredicate */, + activityIntentPair -> true /* activityIntentPairPredicate */, + parentWindowMetrics -> true /* parentWindowMetricsPredicate */).build(); + mSplitController.registerSplit(mTransaction, primaryContainer, primaryActivity, + secondaryContainer, splitPairRule, splitPairRule.getDefaultSplitAttributes()); + SplitPinRule splitPinRule = new SplitPinRule.Builder(new SplitAttributes.Builder().build(), + parentWindowMetrics -> true /* parentWindowMetricsPredicate */).build(); + mSplitController.pinTopActivityStack(TASK_ID, splitPinRule); + final TaskFragmentContainer topPinnedContainer = mSplitController.getTaskContainer(TASK_ID) + .getSplitPinContainer().getSecondaryContainer(); + + // Add a normal container after the overlay, the overlay should still on top, + // and the SplitPinContainer should also on top of the normal one. + final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity); + + final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); + + assertThat(taskContainer.getTaskFragmentContainers()) + .containsExactly(primaryContainer, container, secondaryContainer, overlayContainer) + .inOrder(); + + assertWithMessage("The pinned container must be returned excluding the overlay") + .that(taskContainer.getTopNonFinishingTaskFragmentContainer()) + .isEqualTo(topPinnedContainer); + + assertThat(taskContainer.getTopNonFinishingTaskFragmentContainer(false)) + .isEqualTo(container); + + assertWithMessage("The overlay container must be returned since it's always on top") + .that(taskContainer.getTopNonFinishingTaskFragmentContainer( + false /* includePin */, true /* includeOverlay */)) + .isEqualTo(overlayContainer); + } + /** * A simplified version of {@link SplitController.ActivityStartMonitor * #createOrUpdateOverlayTaskFragmentIfNeeded} @@ -375,6 +424,15 @@ public class OverlayPresentationTest { taskId, mIntent, mActivity); } + /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */ + @NonNull + private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) { + final TaskFragmentContainer container = mSplitController.newContainer(activity, + activity.getTaskId()); + setupTaskFragmentInfo(container, activity); + return container; + } + @NonNull private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag) { TaskFragmentContainer overlayContainer = mSplitController.newContainer( diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 6c0b3cf7971d5362d9c2e6ae2a151e8338dddbb3..96839c57d745e571c8a8dbf92fbf47b5153bd6ab 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -48,13 +48,12 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealM import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -175,52 +174,6 @@ public class SplitControllerTest { mActivity = createMockActivity(); } - @Test - public void testGetTopActiveContainer() { - final TaskContainer taskContainer = createTestTaskContainer(); - // tf1 has no running activity so is not active. - final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, - new Intent(), taskContainer, mSplitController, null /* pairedPrimaryContainer */); - // tf2 has running activity so is active. - final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class); - doReturn(1).when(tf2).getRunningActivityCount(); - taskContainer.addTaskFragmentContainer(tf2); - // tf3 is finished so is not active. - final TaskFragmentContainer tf3 = mock(TaskFragmentContainer.class); - doReturn(true).when(tf3).isFinished(); - doReturn(false).when(tf3).isWaitingActivityAppear(); - taskContainer.addTaskFragmentContainer(tf3); - mSplitController.mTaskContainers.put(TASK_ID, taskContainer); - - assertWithMessage("Must return tf2 because tf3 is not active.") - .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2); - - taskContainer.removeTaskFragmentContainer(tf3); - - assertWithMessage("Must return tf2 because tf2 has running activity.") - .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2); - - taskContainer.removeTaskFragmentContainer(tf2); - - assertWithMessage("Must return tf because we are waiting for tf1 to appear.") - .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf1); - - final TaskFragmentInfo info = mock(TaskFragmentInfo.class); - doReturn(new ArrayList<>()).when(info).getActivities(); - doReturn(true).when(info).isEmpty(); - tf1.setInfo(mTransaction, info); - - assertWithMessage("Must return tf because we are waiting for tf1 to become non-empty after" - + " creation.") - .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf1); - - doReturn(false).when(info).isEmpty(); - tf1.setInfo(mTransaction, info); - - assertWithMessage("Must return null because tf1 becomes empty.") - .that(mSplitController.getTopActiveContainer(TASK_ID)).isNull(); - } - @Test public void testOnTaskFragmentVanished() { final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID); @@ -306,7 +259,9 @@ public class SplitControllerTest { mSplitController.updateContainer(mTransaction, tf); - verify(mSplitController, never()).getTopActiveContainer(TASK_ID); + TaskContainer taskContainer = tf.getTaskContainer(); + spyOn(taskContainer); + verify(taskContainer, never()).getTopNonFinishingTaskFragmentContainer(); // Verify if tf is not in split, dismissPlaceholderIfNecessary won't be called. doReturn(false).when(mSplitController).shouldContainerBeExpanded(tf); @@ -321,7 +276,7 @@ public class SplitControllerTest { doReturn(tf).when(splitContainer).getSecondaryContainer(); doReturn(createTestTaskContainer()).when(splitContainer).getTaskContainer(); doReturn(createSplitRule(mActivity, mActivity)).when(splitContainer).getSplitRule(); - final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); + taskContainer = mSplitController.getTaskContainer(TASK_ID); taskContainer.addSplitContainer(splitContainer); // Add a mock SplitContainer on top of splitContainer final SplitContainer splitContainer2 = mock(SplitContainer.class); @@ -596,13 +551,12 @@ public class SplitControllerTest { } @Test - public void testResolveStartActivityIntent_skipIfPinned() { + public void testResolveStartActivityIntent_skipIfIsolatedNavEnabled() { final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity); - final TaskContainer taskContainer = container.getTaskContainer(); - spyOn(taskContainer); + container.setIsolatedNavigationEnabled(true); + final Intent intent = new Intent(); setupSplitRule(mActivity, intent); - doReturn(true).when(taskContainer).isTaskFragmentContainerPinned(container); assertNull(mSplitController.resolveStartActivityIntent(mTransaction, TASK_ID, intent, mActivity)); } @@ -1569,9 +1523,9 @@ public class SplitControllerTest { addSplitTaskFragments(primaryActivity, thirdActivity); // Ensure another SplitContainer is added and the pinned TaskFragment still on top - assertTrue(taskContainer.getSplitContainers().size() == splitContainerCount + +1); - assertTrue(mSplitController.getTopActiveContainer(TASK_ID).getTopNonFinishingActivity() - == secondaryActivity); + assertEquals(taskContainer.getSplitContainers().size(), splitContainerCount + +1); + assertSame(taskContainer.getTopNonFinishingTaskFragmentContainer() + .getTopNonFinishingActivity(), secondaryActivity); } /** Creates a mock activity in the organizer process. */ diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 187964947b946e5e11f12650f07a546532d803ce..fd4522e024388d251c0e63008153ce12b2e62bed 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -172,4 +172,5 @@ android_library { kotlincflags: ["-Xjvm-default=all"], manifest: "AndroidManifest.xml", plugins: ["dagger2-compiler"], + use_resource_processor: true, } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 3790f04b56eb46de2b458d68cc8121160f2e0ab9..d08c573736d14967eb7191d194cacbbc8f406cb8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -16,6 +16,7 @@ package com.android.wm.shell.back; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION; @@ -70,7 +71,6 @@ import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; - import java.util.concurrent.atomic.AtomicBoolean; /** @@ -317,7 +317,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont executeRemoteCallWithTaskPermission(mController, "setBackToLauncherCallback", (controller) -> controller.registerAnimation( BackNavigationInfo.TYPE_RETURN_TO_HOME, - new BackAnimationRunner(callback, runner))); + new BackAnimationRunner( + callback, + runner, + controller.mContext, + CUJ_PREDICTIVE_BACK_HOME))); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java index 431df212f0995067605fad0f359e0e46bf7288b3..dc413b059fd766905ffdf0073286368ed02ece40 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java @@ -19,6 +19,7 @@ package com.android.wm.shell.back; import static android.view.WindowManager.TRANSIT_OLD_UNSET; import android.annotation.NonNull; +import android.content.Context; import android.os.RemoteException; import android.util.Log; import android.view.IRemoteAnimationFinishedCallback; @@ -27,16 +28,22 @@ import android.view.RemoteAnimationTarget; import android.window.IBackAnimationRunner; import android.window.IOnBackInvokedCallback; +import com.android.internal.jank.InteractionJankMonitor; +import com.android.wm.shell.common.InteractionJankMonitorUtils; + /** * Used to register the animation callback and runner, it will trigger result if gesture was finish * before it received IBackAnimationRunner#onAnimationStart, so the controller could continue * trigger the real back behavior. */ public class BackAnimationRunner { + private static final int NO_CUJ = -1; private static final String TAG = "ShellBackPreview"; private final IOnBackInvokedCallback mCallback; private final IRemoteAnimationRunner mRunner; + private final @InteractionJankMonitor.CujType int mCujType; + private final Context mContext; // Whether we are waiting to receive onAnimationStart private boolean mWaitingAnimation; @@ -45,9 +52,21 @@ public class BackAnimationRunner { private boolean mAnimationCancelled; public BackAnimationRunner( - @NonNull IOnBackInvokedCallback callback, @NonNull IRemoteAnimationRunner runner) { + @NonNull IOnBackInvokedCallback callback, + @NonNull IRemoteAnimationRunner runner, + @NonNull Context context, + @InteractionJankMonitor.CujType int cujType) { mCallback = callback; mRunner = runner; + mCujType = cujType; + mContext = context; + } + + public BackAnimationRunner( + @NonNull IOnBackInvokedCallback callback, + @NonNull IRemoteAnimationRunner runner, + @NonNull Context context) { + this(callback, runner, context, NO_CUJ); } /** Returns the registered animation runner */ @@ -70,10 +89,17 @@ public class BackAnimationRunner { new IRemoteAnimationFinishedCallback.Stub() { @Override public void onAnimationFinished() { + if (shouldMonitorCUJ(apps)) { + InteractionJankMonitorUtils.endTracing(mCujType); + } finishedCallback.run(); } }; mWaitingAnimation = false; + if (shouldMonitorCUJ(apps)) { + InteractionJankMonitorUtils.beginTracing( + mCujType, mContext, apps[0].leash, /* tag */ null); + } try { getRunner().onAnimationStart(TRANSIT_OLD_UNSET, apps, wallpapers, nonApps, callback); @@ -82,6 +108,10 @@ public class BackAnimationRunner { } } + private boolean shouldMonitorCUJ(RemoteAnimationTarget[] apps) { + return apps.length > 0 && mCujType != NO_CUJ; + } + void startGesture() { mWaitingAnimation = true; mAnimationCancelled = false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java index 114486e848f00a91d8c0c5fe3c6f5b41e43a07b6..24479d7b5f397818b30b8cae3aa87fda0b5cf9b3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java @@ -19,6 +19,7 @@ package com.android.wm.shell.back; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY; import static com.android.wm.shell.back.BackAnimationConstants.PROGRESS_COMMIT_THRESHOLD; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; @@ -135,7 +136,8 @@ public class CrossActivityAnimation extends ShellBackAnimation { @Inject public CrossActivityAnimation(Context context, BackAnimationBackground background) { mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); - mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner()); + mBackAnimationRunner = new BackAnimationRunner( + new Callback(), new Runner(), context, CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY); mBackground = background; mEnteringProgressSpring = new SpringAnimation(this, ENTER_PROGRESS_PROP); mEnteringProgressSpring.setSpring(new SpringForce() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index 209d8533ee7d0c0e46081e0a4a487b1d5ed7b2bc..fc5ff017ebe58c678a5e5abecb7233ba96725db4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -20,6 +20,7 @@ import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.window.BackEvent.EDGE_RIGHT; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_TASK; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; import android.animation.Animator; @@ -108,6 +109,7 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private final float[] mTmpTranslate = {0, 0, 0}; private final BackAnimationRunner mBackAnimationRunner; private final BackAnimationBackground mBackground; + private final Context mContext; private RemoteAnimationTarget mEnteringTarget; private RemoteAnimationTarget mClosingTarget; private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); @@ -120,8 +122,10 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { @Inject public CrossTaskBackAnimation(Context context, BackAnimationBackground background) { + mContext = context; mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); - mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner()); + mBackAnimationRunner = new BackAnimationRunner( + new Callback(), new Runner(), context, CUJ_PREDICTIVE_BACK_CROSS_TASK); mBackground = background; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java index aca638c1a5cf089b5baa8e70c9f7a38758597204..5254ff4661233f9dc734507556c3e0c411cee014 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java @@ -19,6 +19,7 @@ package com.android.wm.shell.back; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; import android.animation.Animator; @@ -97,7 +98,8 @@ public class CustomizeActivityAnimation extends ShellBackAnimation { SurfaceControl.Transaction transaction, Choreographer choreographer) { mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); mBackground = background; - mBackAnimationRunner = new BackAnimationRunner(new Callback(), new Runner()); + mBackAnimationRunner = new BackAnimationRunner( + new Callback(), new Runner(), context, CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY); mCustomAnimationLoader = new CustomAnimationLoader(context); mProgressSpring = new SpringAnimation(this, ENTER_PROGRESS_PROP); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index dddcbd4c96c03b3c1961cd53842946217633c476..f0da35df39eed73e5b3c990ebccc84a3eff78449 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -317,7 +317,8 @@ public class BubbleController implements ConfigurationChangeListener, mBubbleIconFactory = new BubbleIconFactory(context, context.getResources().getDimensionPixelSize(R.dimen.bubble_size), context.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size), - context.getResources().getColor(R.color.important_conversation), + context.getResources().getColor( + com.android.launcher3.icons.R.color.important_conversation), context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.importance_ring_stroke_width)); mDisplayController = displayController; @@ -949,7 +950,8 @@ public class BubbleController implements ConfigurationChangeListener, mBubbleIconFactory = new BubbleIconFactory(mContext, mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size), mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size), - mContext.getResources().getColor(R.color.important_conversation), + mContext.getResources().getColor( + com.android.launcher3.icons.R.color.important_conversation), mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.importance_ring_stroke_width)); @@ -988,7 +990,8 @@ public class BubbleController implements ConfigurationChangeListener, mBubbleIconFactory = new BubbleIconFactory(mContext, mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size), mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size), - mContext.getResources().getColor(R.color.important_conversation), + mContext.getResources().getColor( + com.android.launcher3.icons.R.color.important_conversation), mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.importance_ring_stroke_width)); mStackView.onDisplaySizeChanged(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index dc099d9abda4e5f1bc93cc8a30aa50aed53bc134..22e836aacfc593c6449115786bf5248cd7e41f12 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -113,7 +113,7 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl context, res.getDimensionPixelSize(R.dimen.bubble_size), res.getDimensionPixelSize(R.dimen.bubble_badge_size), - res.getColor(R.color.important_conversation), + res.getColor(com.android.launcher3.icons.R.color.important_conversation), res.getDimensionPixelSize(com.android.internal.R.dimen.importance_ring_stroke_width) ) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index e7d0f601ff5ae6cf08b72e6f5939a725f6fd8594..d6141cfd21baf1c08750129a550c826349e6b0f9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -568,8 +568,12 @@ public class BackAnimationControllerTest extends ShellTestCase { } private void registerAnimation(int type) { - mController.registerAnimation(type, - new BackAnimationRunner(mAnimatorCallback, mBackAnimationRunner)); + mController.registerAnimation( + type, + new BackAnimationRunner( + mAnimatorCallback, + mBackAnimationRunner, + mContext)); } private void unregisterAnimation(int type) { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 14602ef926d3495468f66817e0df0630a45e2ae3..7fac0c9776ace22c24cf50982121d1040fff954f 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -562,7 +562,11 @@ Frame CanvasContext::getFrame() { void CanvasContext::draw(bool solelyTextureViewUpdates) { if (auto grContext = getGrContext()) { if (grContext->abandoned()) { - LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw"); + if (grContext->isDeviceLost()) { + LOG_ALWAYS_FATAL("Lost GPU device unexpectedly"); + return; + } + LOG_ALWAYS_FATAL("GrContext is abandoned at start of CanvasContext::draw"); return; } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index 9355517d8bf9dadec33cf7397c26727793e588cb..d35dcb54c36d96e1896ed09424a702dcdbd337b5 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -43,6 +43,9 @@ import org.json.JSONException import android.widget.inline.InlinePresentationSpec import androidx.autofill.inline.v1.InlineSuggestionUi import com.android.credentialmanager.GetFlowUtils +import com.android.credentialmanager.getflow.CredentialEntryInfo +import com.android.credentialmanager.getflow.ProviderDisplayInfo +import com.android.credentialmanager.getflow.toProviderDisplayInfo import org.json.JSONObject import java.util.concurrent.Executors @@ -114,10 +117,14 @@ class CredentialAutofillService : AutofillService() { val providerList = GetFlowUtils.toProviderList( getCredResponse.candidateProviderDataList, this@CredentialAutofillService) + if (providerList.isEmpty()) { + return null + } var totalEntryCount = 0 providerList.forEach { provider -> totalEntryCount += provider.credentialEntryList.size } + val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerList) val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0 val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs @@ -129,15 +136,30 @@ class CredentialAutofillService : AutofillService() { var i = 0 val fillResponseBuilder = FillResponse.Builder() var emptyFillResponse = true - providerList.forEach {provider -> - // TODO(b/299321128): Before iterating the list, sort the list so that - // the relevant entries don't get truncated - provider.credentialEntryList.forEach entryLoop@ {entry -> - val autofillId: AutofillId? = entry.fillInIntent?.getParcelableExtra( - CredentialProviderService.EXTRA_AUTOFILL_ID, - AutofillId::class.java) - val pendingIntent = entry.pendingIntent + + providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ { + val primaryEntry = it.sortedCredentialEntryList.first() + // In regular CredMan bottomsheet, only one primary entry per username is displayed. + // But since the credential requests from different fields are allocated into a single + // request for autofill, there will be duplicate primary entries, especially for + // username/pw autofill fields. These primary entries will be the same entries except + // their autofillIds will point to different autofill fields. Process all primary + // fields. + // TODO(b/307435163): Merge credential options + it.sortedCredentialEntryList.forEach entryLoop@ { credentialEntry -> + if (!isSameCredentialEntry(primaryEntry, credentialEntry)) { + // Encountering different credential entry means all the duplicate primary + // entries have been processed. + return@usernameLoop + } + val autofillId: AutofillId? = credentialEntry + .fillInIntent + ?.getParcelableExtra( + CredentialProviderService.EXTRA_AUTOFILL_ID, + AutofillId::class.java) + val pendingIntent = credentialEntry.pendingIntent if (autofillId == null || pendingIntent == null) { + Log.e(TAG, "AutofillId or pendingIntent was missing from the entry.") return@entryLoop } var inlinePresentation: InlinePresentation? = null @@ -151,7 +173,7 @@ class CredentialAutofillService : AutofillService() { } val sliceBuilder = InlineSuggestionUi .newContentBuilder(pendingIntent) - .setTitle(entry.userName) + .setTitle(credentialEntry.userName) inlinePresentation = InlinePresentation( sliceBuilder.build().slice, spec, /* pinned= */ false) } @@ -169,8 +191,8 @@ class CredentialAutofillService : AutofillService() { Field.Builder().setPresentations( presentationBuilder.build()) .build()) - .setAuthentication(entry.pendingIntent.intentSender) - .setAuthenticationExtras(entry.fillInIntent.extras) + .setAuthentication(pendingIntent.intentSender) + .setAuthenticationExtras(credentialEntry.fillInIntent.extras) .build()) emptyFillResponse = false } @@ -292,4 +314,15 @@ class CredentialAutofillService : AutofillService() { } return result } + + private fun isSameCredentialEntry( + info1: CredentialEntryInfo, + info2: CredentialEntryInfo + ): Boolean { + return info1.providerId == info2.providerId && + info1.lastUsedTimeMillis == info2.lastUsedTimeMillis && + info1.credentialType == info2.credentialType && + info1.displayName == info2.displayName && + info1.userName == info2.userName + } } \ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt index 716f47450ae9b38370d1e467ca527e2d2963bf00..447a9d2aaa8daf58fb17205c63cf5d7ce8f93f98 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt @@ -203,9 +203,14 @@ enum class GetScreenState { UNLOCKED_AUTH_ENTRIES_ONLY, } -// IMPORTANT: new invocation should be mindful that this method will throw if more than 1 remote -// entry exists -private fun toProviderDisplayInfo( + +/** + * IMPORTANT: new invocation should be mindful that this method will throw if more than 1 remote + * entry exists + * + * @hide + */ +fun toProviderDisplayInfo( providerInfoList: List<ProviderInfo> ): ProviderDisplayInfo { val userNameToCredentialEntryMap = mutableMapOf<String, MutableList<CredentialEntryInfo>>() diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml index f1e028b405db1e371a01db4c90593fbc0924e02f..6979825c9a206452cb086aac63259305d216b417 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml @@ -25,6 +25,7 @@ <item name="editTextPreferenceStyle">@style/SettingsEditTextPreference.SettingsLib</item> <item name="dropdownPreferenceStyle">@style/SettingsDropdownPreference.SettingsLib</item> <item name="switchPreferenceStyle">@style/SettingsSwitchPreference.SettingsLib</item> + <item name="switchPreferenceCompatStyle">@style/SettingsSwitchPreferenceCompat.SettingsLib</item> <item name="seekBarPreferenceStyle">@style/SettingsSeekbarPreference.SettingsLib</item> <item name="footerPreferenceStyle">@style/Preference.Material</item> </style> @@ -67,6 +68,11 @@ <item name="iconSpaceReserved">@bool/settingslib_config_icon_space_reserved</item> </style> + <style name="SettingsSwitchPreferenceCompat.SettingsLib" parent="@style/Preference.SwitchPreferenceCompat.Material"> + <item name="layout">@layout/settingslib_preference</item> + <item name="iconSpaceReserved">@bool/settingslib_config_icon_space_reserved</item> + </style> + <style name="SettingsSeekbarPreference.SettingsLib" parent="@style/Preference.SeekBarPreference.Material"> <item name="iconSpaceReserved">@bool/settingslib_config_icon_space_reserved</item> </style> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml index c4b6047e14feb084b98c73023df988f216d05585..f44b16104f9942696f6f6d836454ec8ab6faf5b0 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml @@ -34,12 +34,17 @@ </style> <style name="Switch.SettingsLib" parent="@android:style/Widget.Material.CompoundButton.Switch"> - <item name="android:switchMinWidth">52dp</item> <item name="android:minHeight">@dimen/settingslib_preferred_minimum_touch_target</item> <item name="android:track">@drawable/settingslib_switch_track</item> <item name="android:thumb">@drawable/settingslib_switch_thumb</item> </style> + <style name="SwitchCompat.SettingsLib" parent="@style/Widget.AppCompat.CompoundButton.Switch"> + <item name="android:minHeight">@dimen/settingslib_preferred_minimum_touch_target</item> + <item name="track">@drawable/settingslib_switch_track</item> + <item name="android:thumb">@drawable/settingslib_switch_thumb</item> + </style> + <style name="HorizontalProgressBar.SettingsLib" parent="android:style/Widget.Material.ProgressBar.Horizontal"> <item name="android:progressDrawable">@drawable/settingslib_progress_horizontal</item> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml index 69c122c9992e99d24d88745b0d1ca8c18930e8e1..698f21d8d8a6f9ec12683107def46446c197f452 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml @@ -25,6 +25,7 @@ <item name="android:listPreferredItemPaddingRight">16dp</item> <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item> <item name="android:switchStyle">@style/Switch.SettingsLib</item> + <item name="switchStyle">@style/SwitchCompat.SettingsLib</item> <item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item> </style> diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt index a5f69ffec4b422c60cb70dbb2c45a634c25cb032..02d76304f077ab95bc10a42173a919b173709680 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt @@ -18,7 +18,7 @@ package com.android.settingslib.core.instrumentation import android.view.View import androidx.annotation.VisibleForTesting import androidx.preference.PreferenceGroupAdapter -import androidx.preference.SwitchPreference +import androidx.preference.TwoStatePreference import androidx.recyclerview.widget.RecyclerView import com.android.internal.jank.InteractionJankMonitor import java.util.concurrent.Executors @@ -43,7 +43,10 @@ object SettingsJankMonitor { * @param preference the clicked preference */ @JvmStatic - fun detectSwitchPreferenceClickJank(recyclerView: RecyclerView, preference: SwitchPreference) { + fun detectSwitchPreferenceClickJank( + recyclerView: RecyclerView, + preference: TwoStatePreference, + ) { val adapter = recyclerView.adapter as? PreferenceGroupAdapter ?: return val adapterPosition = adapter.getPreferenceAdapterPosition(preference) val viewHolder = recyclerView.findViewHolderForAdapterPosition(adapterPosition) ?: return diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index f4ca260d4d8918139477d371411e010b3c22b43b..a4a9290b16abc95862bce21bb38324e5a97e46fd 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -17,9 +17,10 @@ license { ], } -android_app { - name: "SettingsProvider", +android_library { + name: "SettingsProviderLib", defaults: ["platform_app_defaults"], + manifest: "AndroidManifestLib.xml", resource_dirs: ["res"], srcs: [ "src/**/*.java", @@ -32,38 +33,37 @@ android_app { ], static_libs: [ "device_config_service_flags_java", - "junit", "SettingsLibDeviceStateRotationLock", "SettingsLibDisplayUtils", ], platform_apis: true, +} + +android_app { + name: "SettingsProvider", + defaults: ["platform_app_defaults"], + resource_dirs: [], + static_libs: ["SettingsProviderLib"], + platform_apis: true, certificate: "platform", privileged: true, } android_test { name: "SettingsProviderTest", - // Note we statically link several classes to do some unit tests. It's not accessible otherwise - // because this test is not an instrumentation test. (because the target runs in the system process.) srcs: [ "test/**/*.java", - "src/android/provider/settings/backup/*", - "src/android/provider/settings/validators/*", - "src/com/android/providers/settings/GenerationRegistry.java", - "src/com/android/providers/settings/SettingsBackupAgent.java", - "src/com/android/providers/settings/SettingsState.java", - "src/com/android/providers/settings/SettingsHelper.java", - "src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java", ], static_libs: [ + // Note we statically link SettingsProviderLib to do some unit tests. It's not accessible otherwise + // because this test is not an instrumentation test. (because the target runs in the system process.) + "SettingsProviderLib", + "androidx.test.rules", - "device_config_service_flags_java", "flag-junit", + "junit", "mockito-target-minus-junit4", "platform-test-annotations", - "SettingsLibDeviceStateRotationLock", - "SettingsLibDisplayUtils", - "platform-test-annotations", "truth", ], libs: [ @@ -71,12 +71,7 @@ android_test { "android.test.mock", "unsupportedappusage", ], - resource_dirs: ["res"], - aaptflags: [ - "--auto-add-overlay", - "--extra-packages", - "com.android.providers.settings", - ], + resource_dirs: [], platform_apis: true, certificate: "platform", test_suites: ["device-tests"], diff --git a/packages/SettingsProvider/AndroidManifestLib.xml b/packages/SettingsProvider/AndroidManifestLib.xml new file mode 100644 index 0000000000000000000000000000000000000000..a20b539e2c10f280a5695201694b220fcd286856 --- /dev/null +++ b/packages/SettingsProvider/AndroidManifestLib.xml @@ -0,0 +1,2 @@ +<manifest package="com.android.providers.settings"> +</manifest> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 95d7039859b56657a8fac72d402c756dc4286a18..4e2fad0bece22753c3bb7fc4e912d9055ec4e282 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -40,6 +40,7 @@ import static com.android.providers.settings.SettingsState.getUserIdFromKey; import static com.android.providers.settings.SettingsState.isConfigSettingsKey; import static com.android.providers.settings.SettingsState.isGlobalSettingsKey; import static com.android.providers.settings.SettingsState.isSecureSettingsKey; +import static com.android.providers.settings.SettingsState.isSsaidSettingsKey; import static com.android.providers.settings.SettingsState.isSystemSettingsKey; import static com.android.providers.settings.SettingsState.makeKey; @@ -411,9 +412,6 @@ public class SettingsProvider extends ContentProvider { SettingsState.cacheSystemPackageNamesAndSystemSignature(getContext()); synchronized (mLock) { mSettingsRegistry.migrateAllLegacySettingsIfNeededLocked(); - for (UserInfo user : mUserManager.getAliveUsers()) { - mSettingsRegistry.ensureSettingsForUserLocked(user.id); - } mSettingsRegistry.syncSsaidTableOnStartLocked(); } mHandler.post(() -> { @@ -429,53 +427,65 @@ public class SettingsProvider extends ContentProvider { public Bundle call(String method, String name, Bundle args) { final int requestingUserId = getRequestingUserId(args); switch (method) { - case Settings.CALL_METHOD_GET_CONFIG -> { + case Settings.CALL_METHOD_GET_CONFIG: { Setting setting = getConfigSetting(name); return packageValueForCallResult(SETTINGS_TYPE_CONFIG, name, requestingUserId, setting, isTrackingGeneration(args)); } - case Settings.CALL_METHOD_GET_GLOBAL -> { + + case Settings.CALL_METHOD_GET_GLOBAL: { Setting setting = getGlobalSetting(name); return packageValueForCallResult(SETTINGS_TYPE_GLOBAL, name, requestingUserId, setting, isTrackingGeneration(args)); } - case Settings.CALL_METHOD_GET_SECURE -> { + + case Settings.CALL_METHOD_GET_SECURE: { Setting setting = getSecureSetting(name, requestingUserId); return packageValueForCallResult(SETTINGS_TYPE_SECURE, name, requestingUserId, setting, isTrackingGeneration(args)); } - case Settings.CALL_METHOD_GET_SYSTEM -> { + + case Settings.CALL_METHOD_GET_SYSTEM: { Setting setting = getSystemSetting(name, requestingUserId); return packageValueForCallResult(SETTINGS_TYPE_SYSTEM, name, requestingUserId, setting, isTrackingGeneration(args)); } - case Settings.CALL_METHOD_PUT_CONFIG -> { + + case Settings.CALL_METHOD_PUT_CONFIG: { String value = getSettingValue(args); final boolean makeDefault = getSettingMakeDefault(args); insertConfigSetting(name, value, makeDefault); + break; } - case Settings.CALL_METHOD_PUT_GLOBAL -> { + + case Settings.CALL_METHOD_PUT_GLOBAL: { String value = getSettingValue(args); String tag = getSettingTag(args); final boolean makeDefault = getSettingMakeDefault(args); final boolean overrideableByRestore = getSettingOverrideableByRestore(args); insertGlobalSetting(name, value, tag, makeDefault, requestingUserId, false, overrideableByRestore); + break; } - case Settings.CALL_METHOD_PUT_SECURE -> { + + case Settings.CALL_METHOD_PUT_SECURE: { String value = getSettingValue(args); String tag = getSettingTag(args); final boolean makeDefault = getSettingMakeDefault(args); final boolean overrideableByRestore = getSettingOverrideableByRestore(args); insertSecureSetting(name, value, tag, makeDefault, requestingUserId, false, overrideableByRestore); + break; } - case Settings.CALL_METHOD_PUT_SYSTEM -> { + + case Settings.CALL_METHOD_PUT_SYSTEM: { String value = getSettingValue(args); boolean overrideableByRestore = getSettingOverrideableByRestore(args); insertSystemSetting(name, value, requestingUserId, overrideableByRestore); + break; } - case Settings.CALL_METHOD_SET_ALL_CONFIG -> { + + case Settings.CALL_METHOD_SET_ALL_CONFIG: { String prefix = getSettingPrefix(args); Map<String, String> flags = getSettingFlags(args); Bundle result = new Bundle(); @@ -483,96 +493,120 @@ public class SettingsProvider extends ContentProvider { setAllConfigSettings(prefix, flags)); return result; } - case Settings.CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG -> { + + case Settings.CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG: { final int mode = getSyncDisabledMode(args); setSyncDisabledModeConfig(mode); + break; } - case Settings.CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG -> { + + case Settings.CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG: { Bundle result = new Bundle(); result.putInt(Settings.KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN, getSyncDisabledModeConfig()); return result; } - case Settings.CALL_METHOD_RESET_CONFIG -> { + + case Settings.CALL_METHOD_RESET_CONFIG: { final int mode = getResetModeEnforcingPermission(args); String prefix = getSettingPrefix(args); resetConfigSetting(mode, prefix); + break; } - case Settings.CALL_METHOD_RESET_GLOBAL -> { + + case Settings.CALL_METHOD_RESET_GLOBAL: { final int mode = getResetModeEnforcingPermission(args); String tag = getSettingTag(args); resetGlobalSetting(requestingUserId, mode, tag); + break; } - case Settings.CALL_METHOD_RESET_SECURE -> { + + case Settings.CALL_METHOD_RESET_SECURE: { final int mode = getResetModeEnforcingPermission(args); String tag = getSettingTag(args); resetSecureSetting(requestingUserId, mode, tag); + break; } - case Settings.CALL_METHOD_RESET_SYSTEM -> { + + case Settings.CALL_METHOD_RESET_SYSTEM: { final int mode = getResetModeEnforcingPermission(args); String tag = getSettingTag(args); resetSystemSetting(requestingUserId, mode, tag); + break; } - case Settings.CALL_METHOD_DELETE_CONFIG -> { - int rows = deleteConfigSetting(name) ? 1 : 0; + + case Settings.CALL_METHOD_DELETE_CONFIG: { + int rows = deleteConfigSetting(name) ? 1 : 0; Bundle result = new Bundle(); result.putInt(RESULT_ROWS_DELETED, rows); return result; } - case Settings.CALL_METHOD_DELETE_GLOBAL -> { + + case Settings.CALL_METHOD_DELETE_GLOBAL: { int rows = deleteGlobalSetting(name, requestingUserId, false) ? 1 : 0; Bundle result = new Bundle(); result.putInt(RESULT_ROWS_DELETED, rows); return result; } - case Settings.CALL_METHOD_DELETE_SECURE -> { + + case Settings.CALL_METHOD_DELETE_SECURE: { int rows = deleteSecureSetting(name, requestingUserId, false) ? 1 : 0; Bundle result = new Bundle(); result.putInt(RESULT_ROWS_DELETED, rows); return result; } - case Settings.CALL_METHOD_DELETE_SYSTEM -> { + + case Settings.CALL_METHOD_DELETE_SYSTEM: { int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0; Bundle result = new Bundle(); result.putInt(RESULT_ROWS_DELETED, rows); return result; } - case Settings.CALL_METHOD_LIST_CONFIG -> { + + case Settings.CALL_METHOD_LIST_CONFIG: { String prefix = getSettingPrefix(args); Bundle result = packageValuesForCallResult(prefix, getAllConfigFlags(prefix), isTrackingGeneration(args)); reportDeviceConfigAccess(prefix); return result; } - case Settings.CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG -> { + + case Settings.CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG: { RemoteCallback callback = args.getParcelable( Settings.CALL_METHOD_MONITOR_CALLBACK_KEY); setMonitorCallback(callback); + break; } - case Settings.CALL_METHOD_UNREGISTER_MONITOR_CALLBACK_CONFIG -> { + + case Settings.CALL_METHOD_UNREGISTER_MONITOR_CALLBACK_CONFIG: { clearMonitorCallback(); + break; } - case Settings.CALL_METHOD_LIST_GLOBAL -> { + + case Settings.CALL_METHOD_LIST_GLOBAL: { Bundle result = new Bundle(); result.putStringArrayList(RESULT_SETTINGS_LIST, buildSettingsList(getAllGlobalSettings(null))); return result; } - case Settings.CALL_METHOD_LIST_SECURE -> { + + case Settings.CALL_METHOD_LIST_SECURE: { Bundle result = new Bundle(); result.putStringArrayList(RESULT_SETTINGS_LIST, buildSettingsList(getAllSecureSettings(requestingUserId, null))); return result; } - case Settings.CALL_METHOD_LIST_SYSTEM -> { + + case Settings.CALL_METHOD_LIST_SYSTEM: { Bundle result = new Bundle(); result.putStringArrayList(RESULT_SETTINGS_LIST, buildSettingsList(getAllSystemSettings(requestingUserId, null))); return result; } - default -> { + + default: { Slog.w(LOG_TAG, "call() with invalid method: " + method); - } + } break; } return null; @@ -604,7 +638,7 @@ public class SettingsProvider extends ContentProvider { } switch (args.table) { - case TABLE_GLOBAL -> { + case TABLE_GLOBAL: { if (args.name != null) { Setting setting = getGlobalSetting(args.name); return packageSettingForQuery(setting, normalizedProjection); @@ -612,7 +646,8 @@ public class SettingsProvider extends ContentProvider { return getAllGlobalSettings(projection); } } - case TABLE_SECURE -> { + + case TABLE_SECURE: { final int userId = UserHandle.getCallingUserId(); if (args.name != null) { Setting setting = getSecureSetting(args.name, userId); @@ -621,7 +656,8 @@ public class SettingsProvider extends ContentProvider { return getAllSecureSettings(userId, projection); } } - case TABLE_SYSTEM -> { + + case TABLE_SYSTEM: { final int userId = UserHandle.getCallingUserId(); if (args.name != null) { Setting setting = getSystemSetting(args.name, userId); @@ -630,7 +666,8 @@ public class SettingsProvider extends ContentProvider { return getAllSystemSettings(userId, projection); } } - default -> { + + default: { throw new IllegalArgumentException("Invalid Uri path:" + uri); } } @@ -671,27 +708,30 @@ public class SettingsProvider extends ContentProvider { String value = values.getAsString(Settings.Secure.VALUE); switch (table) { - case TABLE_GLOBAL -> { + case TABLE_GLOBAL: { if (insertGlobalSetting(name, value, null, false, UserHandle.getCallingUserId(), false, /* overrideableByRestore */ false)) { - return Uri.withAppendedPath(Global.CONTENT_URI, name); + return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name); } - } - case TABLE_SECURE -> { + } break; + + case TABLE_SECURE: { if (insertSecureSetting(name, value, null, false, UserHandle.getCallingUserId(), false, /* overrideableByRestore */ false)) { - return Uri.withAppendedPath(Secure.CONTENT_URI, name); + return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name); } - } - case TABLE_SYSTEM -> { + } break; + + case TABLE_SYSTEM: { if (insertSystemSetting(name, value, UserHandle.getCallingUserId(), /* overridableByRestore */ false)) { return Uri.withAppendedPath(Settings.System.CONTENT_URI, name); } - } - default -> { + } break; + + default: { throw new IllegalArgumentException("Bad Uri path:" + uri); } } @@ -735,19 +775,22 @@ public class SettingsProvider extends ContentProvider { } switch (args.table) { - case TABLE_GLOBAL -> { + case TABLE_GLOBAL: { final int userId = UserHandle.getCallingUserId(); return deleteGlobalSetting(args.name, userId, false) ? 1 : 0; } - case TABLE_SECURE -> { + + case TABLE_SECURE: { final int userId = UserHandle.getCallingUserId(); return deleteSecureSetting(args.name, userId, false) ? 1 : 0; } - case TABLE_SYSTEM -> { + + case TABLE_SYSTEM: { final int userId = UserHandle.getCallingUserId(); return deleteSystemSetting(args.name, userId) ? 1 : 0; } - default -> { + + default: { throw new IllegalArgumentException("Bad Uri path:" + uri); } } @@ -773,21 +816,24 @@ public class SettingsProvider extends ContentProvider { String value = values.getAsString(Settings.Secure.VALUE); switch (args.table) { - case TABLE_GLOBAL -> { + case TABLE_GLOBAL: { final int userId = UserHandle.getCallingUserId(); return updateGlobalSetting(args.name, value, null, false, userId, false) ? 1 : 0; } - case TABLE_SECURE -> { + + case TABLE_SECURE: { final int userId = UserHandle.getCallingUserId(); return updateSecureSetting(args.name, value, null, false, userId, false) ? 1 : 0; } - case TABLE_SYSTEM -> { + + case TABLE_SYSTEM: { final int userId = UserHandle.getCallingUserId(); return updateSystemSetting(args.name, value, userId) ? 1 : 0; } - default -> { + + default: { throw new IllegalArgumentException("Invalid Uri path:" + uri); } } @@ -988,38 +1034,27 @@ public class SettingsProvider extends ContentProvider { private void registerBroadcastReceivers() { IntentFilter userFilter = new IntentFilter(); - userFilter.addAction(Intent.ACTION_USER_ADDED); userFilter.addAction(Intent.ACTION_USER_REMOVED); userFilter.addAction(Intent.ACTION_USER_STOPPED); getContext().registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction() == null) { - return; - } final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, - UserHandle.USER_NULL); - if (userId == UserHandle.USER_NULL) { - return; - } + UserHandle.USER_SYSTEM); switch (intent.getAction()) { - case Intent.ACTION_USER_ADDED -> { - synchronized (mLock) { - mSettingsRegistry.ensureSettingsForUserLocked(userId); - } - } - case Intent.ACTION_USER_REMOVED -> { + case Intent.ACTION_USER_REMOVED: { synchronized (mLock) { mSettingsRegistry.removeUserStateLocked(userId, true); } - } - case Intent.ACTION_USER_STOPPED -> { + } break; + + case Intent.ACTION_USER_STOPPED: { synchronized (mLock) { mSettingsRegistry.removeUserStateLocked(userId, false); } - } + } break; } } }, userFilter); @@ -1315,24 +1350,26 @@ public class SettingsProvider extends ContentProvider { // Perform the mutation. synchronized (mLock) { switch (operation) { - case MUTATION_OPERATION_INSERT -> { + case MUTATION_OPERATION_INSERT: { enforceDeviceConfigWritePermission(getContext(), Collections.singleton(name)); return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM, name, value, null, makeDefault, true, callingPackage, false, null, /* overrideableByRestore */ false); } - case MUTATION_OPERATION_DELETE -> { + + case MUTATION_OPERATION_DELETE: { enforceDeviceConfigWritePermission(getContext(), Collections.singleton(name)); return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM, name, false, null); } - case MUTATION_OPERATION_RESET -> { + + case MUTATION_OPERATION_RESET: { enforceDeviceConfigWritePermission(getContext(), getAllConfigFlags(prefix).keySet()); - return mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG, + mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM, callingPackage, mode, null, prefix); - } + } return true; } } @@ -1486,7 +1523,7 @@ public class SettingsProvider extends ContentProvider { enforceHasAtLeastOnePermission(Manifest.permission.WRITE_SECURE_SETTINGS); // Resolve the userId on whose behalf the call is made. - final int callingUserId = resolveCallingUserIdEnforcingPermissions(requestingUserId); + final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); // If this is a setting that is currently restricted for this user, do not allow // unrestricting changes. @@ -1499,25 +1536,28 @@ public class SettingsProvider extends ContentProvider { // Perform the mutation. synchronized (mLock) { switch (operation) { - case MUTATION_OPERATION_INSERT -> { + case MUTATION_OPERATION_INSERT: { return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, name, value, tag, makeDefault, callingPackage, forceNotify, CRITICAL_GLOBAL_SETTINGS, overrideableByRestore); } - case MUTATION_OPERATION_DELETE -> { + + case MUTATION_OPERATION_DELETE: { return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, name, forceNotify, CRITICAL_GLOBAL_SETTINGS); } - case MUTATION_OPERATION_UPDATE -> { + + case MUTATION_OPERATION_UPDATE: { return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, name, value, tag, makeDefault, callingPackage, forceNotify, CRITICAL_GLOBAL_SETTINGS); } - case MUTATION_OPERATION_RESET -> { - return mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_GLOBAL, + + case MUTATION_OPERATION_RESET: { + mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, callingPackage, mode, tag); - } + } return true; } } @@ -1540,12 +1580,12 @@ public class SettingsProvider extends ContentProvider { } // Resolve the userId on whose behalf the call is made. - final int callingUserId = resolveCallingUserIdEnforcingPermissions(userId); + final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); // The relevant "calling package" userId will be the owning userId for some // profiles, and we can't do the lookup inside our [lock held] loop, so work out // up front who the effective "new SSAID" user ID for that settings name will be. - final int ssaidUserId = resolveOwningUserIdForSecureSetting(callingUserId, + final int ssaidUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, Settings.Secure.ANDROID_ID); final PackageInfo ssaidCallingPkg = getCallingPackageInfo(ssaidUserId); @@ -1560,7 +1600,7 @@ public class SettingsProvider extends ContentProvider { for (int i = 0; i < nameCount; i++) { String name = names.get(i); // Determine the owning user as some profile settings are cloned from the parent. - final int owningUserId = resolveOwningUserIdForSecureSetting(callingUserId, + final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name); if (!isSecureSettingAccessible(name)) { @@ -1598,13 +1638,13 @@ public class SettingsProvider extends ContentProvider { } // Resolve the userId on whose behalf the call is made. - final int callingUserId = resolveCallingUserIdEnforcingPermissions(requestingUserId); + final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); // Ensure the caller can access the setting. enforceSettingReadable(name, SETTINGS_TYPE_SECURE, UserHandle.getCallingUserId()); // Determine the owning user as some profile settings are cloned from the parent. - final int owningUserId = resolveOwningUserIdForSecureSetting(callingUserId, name); + final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name); if (!isSecureSettingAccessible(name)) { // This caller is not permitted to access this setting. Pretend the setting doesn't @@ -1771,7 +1811,7 @@ public class SettingsProvider extends ContentProvider { enforceHasAtLeastOnePermission(Manifest.permission.WRITE_SECURE_SETTINGS); // Resolve the userId on whose behalf the call is made. - final int callingUserId = resolveCallingUserIdEnforcingPermissions(requestingUserId); + final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); // If this is a setting that is currently restricted for this user, do not allow // unrestricting changes. @@ -1780,7 +1820,7 @@ public class SettingsProvider extends ContentProvider { } // Determine the owning user as some profile settings are cloned from the parent. - final int owningUserId = resolveOwningUserIdForSecureSetting(callingUserId, name); + final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name); // Only the owning user can change the setting. if (owningUserId != callingUserId) { @@ -1792,25 +1832,28 @@ public class SettingsProvider extends ContentProvider { // Mutate the value. synchronized (mLock) { switch (operation) { - case MUTATION_OPERATION_INSERT -> { + case MUTATION_OPERATION_INSERT: { return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name, value, tag, makeDefault, callingPackage, forceNotify, CRITICAL_SECURE_SETTINGS, overrideableByRestore); } - case MUTATION_OPERATION_DELETE -> { + + case MUTATION_OPERATION_DELETE: { return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name, forceNotify, CRITICAL_SECURE_SETTINGS); } - case MUTATION_OPERATION_UPDATE -> { + + case MUTATION_OPERATION_UPDATE: { return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name, value, tag, makeDefault, callingPackage, forceNotify, CRITICAL_SECURE_SETTINGS); } - case MUTATION_OPERATION_RESET -> { - return mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SECURE, + + case MUTATION_OPERATION_RESET: { + mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SECURE, UserHandle.USER_SYSTEM, callingPackage, mode, tag); - } + } return true; } } @@ -1823,7 +1866,7 @@ public class SettingsProvider extends ContentProvider { } // Resolve the userId on whose behalf the call is made. - final int callingUserId = resolveCallingUserIdEnforcingPermissions(userId); + final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId); synchronized (mLock) { List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_SYSTEM, callingUserId); @@ -1860,7 +1903,7 @@ public class SettingsProvider extends ContentProvider { } // Resolve the userId on whose behalf the call is made. - final int callingUserId = resolveCallingUserIdEnforcingPermissions(requestingUserId); + final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId); // Ensure the caller can access the setting. enforceSettingReadable(name, SETTINGS_TYPE_SYSTEM, UserHandle.getCallingUserId()); @@ -1935,7 +1978,7 @@ public class SettingsProvider extends ContentProvider { } // Resolve the userId on whose behalf the call is made. - final int callingUserId = resolveCallingUserIdEnforcingPermissions(runAsUserId); + final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(runAsUserId); if (isSettingRestrictedForUser(name, callingUserId, value, Binder.getCallingUid())) { Slog.e(LOG_TAG, "UserId: " + callingUserId + " is disallowed to change system " @@ -1969,30 +2012,37 @@ public class SettingsProvider extends ContentProvider { // Mutate the value. synchronized (mLock) { switch (operation) { - case MUTATION_OPERATION_INSERT -> { + case MUTATION_OPERATION_INSERT: { validateSystemSettingValue(name, value); success = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM, owningUserId, name, value, null, false, callingPackage, false, null, overrideableByRestore); + break; } - case MUTATION_OPERATION_DELETE -> { + + case MUTATION_OPERATION_DELETE: { success = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM, owningUserId, name, false, null); + break; } - case MUTATION_OPERATION_UPDATE -> { + + case MUTATION_OPERATION_UPDATE: { validateSystemSettingValue(name, value); success = mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM, owningUserId, name, value, null, false, callingPackage, false, null); + break; } - case MUTATION_OPERATION_RESET -> { + + case MUTATION_OPERATION_RESET: { success = mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SYSTEM, runAsUserId, callingPackage, mode, tag); + break; } - default -> { + + default: success = false; Slog.e(LOG_TAG, "Unknown operation code: " + operation); - } } } @@ -2063,8 +2113,8 @@ public class SettingsProvider extends ContentProvider { * Returns {@code true} if the specified secure setting should be accessible to the caller. */ private boolean isSecureSettingAccessible(String name) { - return switch (name) { - case "bluetooth_address" -> + switch (name) { + case "bluetooth_address": // BluetoothManagerService for some reason stores the Android's Bluetooth MAC // address in this secure setting. Secure settings can normally be read by any app, // which thus enables them to bypass the recently introduced restrictions on access @@ -2072,23 +2122,22 @@ public class SettingsProvider extends ContentProvider { // To mitigate this we make this setting available only to callers privileged to see // this device's MAC addresses, same as through public API // BluetoothAdapter.getAddress() (see BluetoothManagerService for details). - getContext().checkCallingOrSelfPermission(Manifest.permission.LOCAL_MAC_ADDRESS) - == PackageManager.PERMISSION_GRANTED; - default -> true; - }; + return getContext().checkCallingOrSelfPermission( + Manifest.permission.LOCAL_MAC_ADDRESS) == PackageManager.PERMISSION_GRANTED; + default: + return true; + } } - private int resolveOwningUserIdForSecureSetting(int userId, String setting) { - // no need to lock because sSecureCloneToManagedSettings is never modified - return resolveOwningUserId(userId, sSecureCloneToManagedSettings, setting); + private int resolveOwningUserIdForSecureSettingLocked(int userId, String setting) { + return resolveOwningUserIdLocked(userId, sSecureCloneToManagedSettings, setting); } - @GuardedBy("mLock") private int resolveOwningUserIdForSystemSettingLocked(int userId, String setting) { final int parentId; // Resolves dependency if setting has a dependency and the calling user has a parent if (sSystemCloneFromParentOnDependency.containsKey(setting) - && (parentId = getGroupParent(userId)) != userId) { + && (parentId = getGroupParentLocked(userId)) != userId) { // The setting has a dependency and the profile has a parent String dependency = sSystemCloneFromParentOnDependency.get(setting); // Lookup the dependency setting as ourselves, some callers may not have access to it. @@ -2102,11 +2151,11 @@ public class SettingsProvider extends ContentProvider { Binder.restoreCallingIdentity(token); } } - return resolveOwningUserId(userId, sSystemCloneToManagedSettings, setting); + return resolveOwningUserIdLocked(userId, sSystemCloneToManagedSettings, setting); } - private int resolveOwningUserId(int userId, Set<String> keys, String name) { - final int parentId = getGroupParent(userId); + private int resolveOwningUserIdLocked(int userId, Set<String> keys, String name) { + final int parentId = getGroupParentLocked(userId); if (parentId != userId && keys.contains(name)) { return parentId; } @@ -2125,8 +2174,9 @@ public class SettingsProvider extends ContentProvider { } switch (operation) { - // Insert updates. - case MUTATION_OPERATION_INSERT, MUTATION_OPERATION_UPDATE -> { + case MUTATION_OPERATION_INSERT: + // Insert updates. + case MUTATION_OPERATION_UPDATE: { if (Settings.System.PUBLIC_SETTINGS.contains(name)) { return; } @@ -2142,8 +2192,9 @@ public class SettingsProvider extends ContentProvider { warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( packageInfo.applicationInfo.targetSdkVersion, name); - } - case MUTATION_OPERATION_DELETE -> { + } break; + + case MUTATION_OPERATION_DELETE: { if (Settings.System.PUBLIC_SETTINGS.contains(name) || Settings.System.PRIVATE_SETTINGS.contains(name)) { throw new IllegalArgumentException("You cannot delete system defined" @@ -2161,26 +2212,34 @@ public class SettingsProvider extends ContentProvider { warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( packageInfo.applicationInfo.targetSdkVersion, name); - } + } break; } } - private static Set<String> getInstantAppAccessibleSettings(int settingsType) { - return switch (settingsType) { - case SETTINGS_TYPE_GLOBAL -> Global.INSTANT_APP_SETTINGS; - case SETTINGS_TYPE_SECURE -> Secure.INSTANT_APP_SETTINGS; - case SETTINGS_TYPE_SYSTEM -> Settings.System.INSTANT_APP_SETTINGS; - default -> throw new IllegalArgumentException("Invalid settings type: " + settingsType); - }; + private Set<String> getInstantAppAccessibleSettings(int settingsType) { + switch (settingsType) { + case SETTINGS_TYPE_GLOBAL: + return Settings.Global.INSTANT_APP_SETTINGS; + case SETTINGS_TYPE_SECURE: + return Settings.Secure.INSTANT_APP_SETTINGS; + case SETTINGS_TYPE_SYSTEM: + return Settings.System.INSTANT_APP_SETTINGS; + default: + throw new IllegalArgumentException("Invalid settings type: " + settingsType); + } } - private static Set<String> getOverlayInstantAppAccessibleSettings(int settingsType) { - return switch (settingsType) { - case SETTINGS_TYPE_GLOBAL -> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS; - case SETTINGS_TYPE_SYSTEM -> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS; - case SETTINGS_TYPE_SECURE -> OVERLAY_ALLOWED_SECURE_INSTANT_APP_SETTINGS; - default -> throw new IllegalArgumentException("Invalid settings type: " + settingsType); - }; + private Set<String> getOverlayInstantAppAccessibleSettings(int settingsType) { + switch (settingsType) { + case SETTINGS_TYPE_GLOBAL: + return OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS; + case SETTINGS_TYPE_SYSTEM: + return OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS; + case SETTINGS_TYPE_SECURE: + return OVERLAY_ALLOWED_SECURE_INSTANT_APP_SETTINGS; + default: + throw new IllegalArgumentException("Invalid settings type: " + settingsType); + } } @GuardedBy("mLock") @@ -2211,7 +2270,7 @@ public class SettingsProvider extends ContentProvider { switch (settingName) { // missing READ_PRIVILEGED_PHONE_STATE permission protection // see alternative API {@link SubscriptionManager#getPreferredDataSubscriptionId() - case Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION -> { + case Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION: // app-compat handling, not break apps targeting on previous SDKs. if (CompatChanges.isChangeEnabled( ENFORCE_READ_PERMISSION_FOR_MULTI_SIM_DATA_CALL)) { @@ -2219,7 +2278,7 @@ public class SettingsProvider extends ContentProvider { Manifest.permission.READ_PRIVILEGED_PHONE_STATE, "access global settings MULTI_SIM_DATA_CALL_SUBSCRIPTION"); } - } + break; } if (!ai.isInstantApp()) { return; @@ -2247,22 +2306,23 @@ public class SettingsProvider extends ContentProvider { final Set<String> readableFields; final ArrayMap<String, Integer> readableFieldsWithMaxTargetSdk; switch (settingsType) { - case SETTINGS_TYPE_GLOBAL -> { + case SETTINGS_TYPE_GLOBAL: allFields = sAllGlobalSettings; readableFields = sReadableGlobalSettings; readableFieldsWithMaxTargetSdk = sReadableGlobalSettingsWithMaxTargetSdk; - } - case SETTINGS_TYPE_SYSTEM -> { + break; + case SETTINGS_TYPE_SYSTEM: allFields = sAllSystemSettings; readableFields = sReadableSystemSettings; readableFieldsWithMaxTargetSdk = sReadableSystemSettingsWithMaxTargetSdk; - } - case SETTINGS_TYPE_SECURE -> { + break; + case SETTINGS_TYPE_SECURE: allFields = sAllSecureSettings; readableFields = sReadableSecureSettings; readableFieldsWithMaxTargetSdk = sReadableSecureSettingsWithMaxTargetSdk; - } - default -> throw new IllegalArgumentException("Invalid settings type: " + settingsType); + break; + default: + throw new IllegalArgumentException("Invalid settings type: " + settingsType); } if (allFields.contains(settingName)) { @@ -2320,7 +2380,7 @@ public class SettingsProvider extends ContentProvider { throw new IllegalStateException("Calling package doesn't exist"); } - private int getGroupParent(int userId) { + private int getGroupParentLocked(int userId) { // Most frequent use case. if (userId == UserHandle.USER_SYSTEM) { return userId; @@ -2420,7 +2480,7 @@ public class SettingsProvider extends ContentProvider { } } - private static int resolveCallingUserIdEnforcingPermissions(int requestingUserId) { + private static int resolveCallingUserIdEnforcingPermissionsLocked(int requestingUserId) { if (requestingUserId == UserHandle.getCallingUserId()) { return requestingUserId; } @@ -2594,28 +2654,28 @@ public class SettingsProvider extends ContentProvider { private static int getResetModeEnforcingPermission(Bundle args) { final int mode = (args != null) ? args.getInt(Settings.CALL_METHOD_RESET_MODE_KEY) : 0; switch (mode) { - case Settings.RESET_MODE_UNTRUSTED_DEFAULTS -> { + case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: { if (!isCallerSystemOrShellOrRootOnDebuggableBuild()) { throw new SecurityException("Only system, shell/root on a " + "debuggable build can reset to untrusted defaults"); } return mode; } - case Settings.RESET_MODE_UNTRUSTED_CHANGES -> { + case Settings.RESET_MODE_UNTRUSTED_CHANGES: { if (!isCallerSystemOrShellOrRootOnDebuggableBuild()) { throw new SecurityException("Only system, shell/root on a " + "debuggable build can reset untrusted changes"); } return mode; } - case Settings.RESET_MODE_TRUSTED_DEFAULTS -> { + case Settings.RESET_MODE_TRUSTED_DEFAULTS: { if (!isCallerSystemOrShellOrRootOnDebuggableBuild()) { throw new SecurityException("Only system, shell/root on a " + "debuggable build can reset to trusted defaults"); } return mode; } - case Settings.RESET_MODE_PACKAGE_DEFAULTS -> { + case Settings.RESET_MODE_PACKAGE_DEFAULTS: { return mode; } } @@ -2676,18 +2736,21 @@ public class SettingsProvider extends ContentProvider { String column = cursor.getColumnName(i); switch (column) { - case Settings.NameValueTable._ID -> { + case Settings.NameValueTable._ID: { values[i] = setting.getId(); - } - case Settings.NameValueTable.NAME -> { + } break; + + case Settings.NameValueTable.NAME: { values[i] = setting.getName(); - } - case Settings.NameValueTable.VALUE -> { + } break; + + case Settings.NameValueTable.VALUE: { values[i] = setting.getValue(); - } - case Settings.NameValueTable.IS_PRESERVED_IN_RESTORE -> { + } break; + + case Settings.NameValueTable.IS_PRESERVED_IN_RESTORE: { values[i] = String.valueOf(setting.isValuePreservedInRestore()); - } + } break; } } @@ -2699,11 +2762,19 @@ public class SettingsProvider extends ContentProvider { } private String resolveCallingPackage() { - return switch (Binder.getCallingUid()) { - case Process.ROOT_UID -> "root"; - case Process.SHELL_UID -> "com.android.shell"; - default -> getCallingPackage(); - }; + switch (Binder.getCallingUid()) { + case Process.ROOT_UID: { + return "root"; + } + + case Process.SHELL_UID: { + return "com.android.shell"; + } + + default: { + return getCallingPackage(); + } + } } private static final class Arguments { @@ -2725,17 +2796,17 @@ public class SettingsProvider extends ContentProvider { public Arguments(Uri uri, String where, String[] whereArgs, boolean supportAll) { final int segmentSize = uri.getPathSegments().size(); switch (segmentSize) { - case 1 -> { + case 1: { if (where != null && (WHERE_PATTERN_WITH_PARAM_NO_BRACKETS.matcher(where).matches() - || WHERE_PATTERN_WITH_PARAM_IN_BRACKETS.matcher(where).matches()) + || WHERE_PATTERN_WITH_PARAM_IN_BRACKETS.matcher(where).matches()) && whereArgs.length == 1) { name = whereArgs[0]; table = computeTableForSetting(uri, name); return; } else if (where != null && (WHERE_PATTERN_NO_PARAM_NO_BRACKETS.matcher(where).matches() - || WHERE_PATTERN_NO_PARAM_IN_BRACKETS.matcher(where).matches())) { + || WHERE_PATTERN_NO_PARAM_IN_BRACKETS.matcher(where).matches())) { final int startIndex = Math.max(where.indexOf("'"), where.indexOf("\"")) + 1; final int endIndex = Math.max(where.lastIndexOf("'"), @@ -2748,14 +2819,15 @@ public class SettingsProvider extends ContentProvider { table = computeTableForSetting(uri, null); return; } - } - case 2 -> { + } break; + + case 2: { if (where == null && whereArgs == null) { name = uri.getPathSegments().get(1); table = computeTableForSetting(uri, name); return; } - } + } break; } EventLogTags.writeUnsupportedSettingsQuery( @@ -2888,7 +2960,6 @@ public class SettingsProvider extends ContentProvider { mBackupManager = new BackupManager(getContext()); } - @GuardedBy("mLock") private void generateUserKeyLocked(int userId) { // Generate a random key for each user used for creating a new ssaid. final byte[] keyBytes = new byte[32]; @@ -2912,7 +2983,6 @@ public class SettingsProvider extends ContentProvider { return ByteBuffer.allocate(4).putInt(data.length).array(); } - @GuardedBy("mLock") public Setting generateSsaidLocked(PackageInfo callingPkg, int userId) { // Read the user's key from the ssaid table. Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY); @@ -2974,7 +3044,6 @@ public class SettingsProvider extends ContentProvider { return getSettingLocked(SETTINGS_TYPE_SSAID, userId, uid); } - @GuardedBy("mLock") private void syncSsaidTableOnStartLocked() { // Verify that each user's packages and ssaid's are in sync. for (UserInfo user : mUserManager.getAliveUsers()) { @@ -3009,17 +3078,15 @@ public class SettingsProvider extends ContentProvider { } } - @GuardedBy("mLock") public List<String> getSettingsNamesLocked(int type, int userId) { final int key = makeKey(type, userId); - SettingsState settingsState = mSettingsStates.get(key); + SettingsState settingsState = peekSettingsStateLocked(key); if (settingsState == null) { return new ArrayList<>(); } return settingsState.getSettingNamesLocked(); } - @GuardedBy("mLock") public SparseBooleanArray getKnownUsersLocked() { SparseBooleanArray users = new SparseBooleanArray(); for (int i = mSettingsStates.size()-1; i >= 0; i--) { @@ -3028,19 +3095,17 @@ public class SettingsProvider extends ContentProvider { return users; } - @GuardedBy("mLock") @Nullable public SettingsState getSettingsLocked(int type, int userId) { final int key = makeKey(type, userId); - return mSettingsStates.get(key); + return peekSettingsStateLocked(key); } - @GuardedBy("mLock") - public void ensureSettingsForUserLocked(int userId) { + public boolean ensureSettingsForUserLocked(int userId) { // First make sure this user actually exists. if (mUserManager.getUserInfo(userId) == null) { Slog.wtf(LOG_TAG, "Requested user " + userId + " does not exist"); - return; + return false; } // Migrate the setting for this user if needed. @@ -3078,9 +3143,9 @@ public class SettingsProvider extends ContentProvider { // Upgrade the settings to the latest version. UpgradeController upgrader = new UpgradeController(userId); upgrader.upgradeIfNeededLocked(); + return true; } - @GuardedBy("mLock") private void ensureSettingsStateLocked(int key) { if (mSettingsStates.get(key) == null) { final int maxBytesPerPackage = getMaxBytesPerPackageForType(getTypeFromKey(key)); @@ -3090,7 +3155,6 @@ public class SettingsProvider extends ContentProvider { } } - @GuardedBy("mLock") public void removeUserStateLocked(int userId, boolean permanently) { // We always keep the global settings in memory. @@ -3102,7 +3166,12 @@ public class SettingsProvider extends ContentProvider { mSettingsStates.remove(systemKey); systemSettingsState.destroyLocked(null); } else { - systemSettingsState.destroyLocked(() -> mSettingsStates.remove(systemKey)); + systemSettingsState.destroyLocked(new Runnable() { + @Override + public void run() { + mSettingsStates.remove(systemKey); + } + }); } } @@ -3114,7 +3183,12 @@ public class SettingsProvider extends ContentProvider { mSettingsStates.remove(secureKey); secureSettingsState.destroyLocked(null); } else { - secureSettingsState.destroyLocked(() -> mSettingsStates.remove(secureKey)); + secureSettingsState.destroyLocked(new Runnable() { + @Override + public void run() { + mSettingsStates.remove(secureKey); + } + }); } } @@ -3126,7 +3200,12 @@ public class SettingsProvider extends ContentProvider { mSettingsStates.remove(ssaidKey); ssaidSettingsState.destroyLocked(null); } else { - ssaidSettingsState.destroyLocked(() -> mSettingsStates.remove(ssaidKey)); + ssaidSettingsState.destroyLocked(new Runnable() { + @Override + public void run() { + mSettingsStates.remove(ssaidKey); + } + }); } } @@ -3134,7 +3213,6 @@ public class SettingsProvider extends ContentProvider { mGenerationRegistry.onUserRemoved(userId); } - @GuardedBy("mLock") public boolean insertSettingLocked(int type, int userId, String name, String value, String tag, boolean makeDefault, String packageName, boolean forceNotify, Set<String> criticalSettings, boolean overrideableByRestore) { @@ -3142,7 +3220,6 @@ public class SettingsProvider extends ContentProvider { packageName, forceNotify, criticalSettings, overrideableByRestore); } - @GuardedBy("mLock") public boolean insertSettingLocked(int type, int userId, String name, String value, String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName, boolean forceNotify, Set<String> criticalSettings, boolean overrideableByRestore) { @@ -3155,7 +3232,7 @@ public class SettingsProvider extends ContentProvider { boolean success = false; boolean wasUnsetNonPredefinedSetting = false; - SettingsState settingsState = mSettingsStates.get(key); + SettingsState settingsState = peekSettingsStateLocked(key); if (settingsState != null) { if (!isSettingPreDefined(name, type) && !settingsState.hasSetting(name)) { wasUnsetNonPredefinedSetting = true; @@ -3187,10 +3264,9 @@ public class SettingsProvider extends ContentProvider { * Set Config Settings using consumed keyValues, returns true if the keyValues can be set, * false otherwise. */ - @GuardedBy("mLock") public boolean setConfigSettingsLocked(int key, String prefix, Map<String, String> keyValues, String packageName) { - SettingsState settingsState = mSettingsStates.get(key); + SettingsState settingsState = peekSettingsStateLocked(key); if (settingsState != null) { if (settingsState.isNewConfigBannedLocked(prefix, keyValues)) { return false; @@ -3207,13 +3283,12 @@ public class SettingsProvider extends ContentProvider { return true; } - @GuardedBy("mLock") public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify, Set<String> criticalSettings) { final int key = makeKey(type, userId); boolean success = false; - SettingsState settingsState = mSettingsStates.get(key); + SettingsState settingsState = peekSettingsStateLocked(key); if (settingsState != null) { success = settingsState.deleteSettingLocked(name); } @@ -3231,14 +3306,13 @@ public class SettingsProvider extends ContentProvider { return success; } - @GuardedBy("mLock") public boolean updateSettingLocked(int type, int userId, String name, String value, String tag, boolean makeDefault, String packageName, boolean forceNotify, Set<String> criticalSettings) { final int key = makeKey(type, userId); boolean success = false; - SettingsState settingsState = mSettingsStates.get(key); + SettingsState settingsState = peekSettingsStateLocked(key); if (settingsState != null) { success = settingsState.updateSettingLocked(name, value, tag, makeDefault, packageName); @@ -3257,11 +3331,10 @@ public class SettingsProvider extends ContentProvider { return success; } - @GuardedBy("mLock") public Setting getSettingLocked(int type, int userId, String name) { final int key = makeKey(type, userId); - SettingsState settingsState = mSettingsStates.get(key); + SettingsState settingsState = peekSettingsStateLocked(key); if (settingsState == null) { return null; } @@ -3279,18 +3352,16 @@ public class SettingsProvider extends ContentProvider { return Global.SECURE_FRP_MODE.equals(setting.getName()); } - @GuardedBy("mLock") public boolean resetSettingsLocked(int type, int userId, String packageName, int mode, String tag) { return resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/ null); } - @GuardedBy("mLock") public boolean resetSettingsLocked(int type, int userId, String packageName, int mode, String tag, @Nullable String prefix) { final int key = makeKey(type, userId); - SettingsState settingsState = mSettingsStates.get(key); + SettingsState settingsState = peekSettingsStateLocked(key); if (settingsState == null) { return false; } @@ -3298,7 +3369,7 @@ public class SettingsProvider extends ContentProvider { boolean success = false; banConfigurationIfNecessary(type, prefix, settingsState); switch (mode) { - case Settings.RESET_MODE_PACKAGE_DEFAULTS -> { + case Settings.RESET_MODE_PACKAGE_DEFAULTS: { for (String name : settingsState.getSettingNamesLocked()) { boolean someSettingChanged = false; Setting setting = settingsState.getSettingLocked(name); @@ -3318,8 +3389,9 @@ public class SettingsProvider extends ContentProvider { success = true; } } - } - case Settings.RESET_MODE_UNTRUSTED_DEFAULTS -> { + } break; + + case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: { for (String name : settingsState.getSettingNamesLocked()) { boolean someSettingChanged = false; Setting setting = settingsState.getSettingLocked(name); @@ -3339,8 +3411,9 @@ public class SettingsProvider extends ContentProvider { success = true; } } - } - case Settings.RESET_MODE_UNTRUSTED_CHANGES -> { + } break; + + case Settings.RESET_MODE_UNTRUSTED_CHANGES: { for (String name : settingsState.getSettingNamesLocked()) { boolean someSettingChanged = false; Setting setting = settingsState.getSettingLocked(name); @@ -3366,8 +3439,9 @@ public class SettingsProvider extends ContentProvider { success = true; } } - } - case Settings.RESET_MODE_TRUSTED_DEFAULTS -> { + } break; + + case Settings.RESET_MODE_TRUSTED_DEFAULTS: { for (String name : settingsState.getSettingNamesLocked()) { Setting setting = settingsState.getSettingLocked(name); boolean someSettingChanged = false; @@ -3390,12 +3464,11 @@ public class SettingsProvider extends ContentProvider { success = true; } } - } + } break; } return success; } - @GuardedBy("mLock") public void removeSettingsForPackageLocked(String packageName, int userId) { // Global and secure settings are signature protected. Apps signed // by the platform certificate are generally not uninstalled and @@ -3409,7 +3482,6 @@ public class SettingsProvider extends ContentProvider { } } - @GuardedBy("mLock") public void onUidRemovedLocked(int uid) { final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, UserHandle.getUserId(uid)); @@ -3418,7 +3490,19 @@ public class SettingsProvider extends ContentProvider { } } - @GuardedBy("mLock") + @Nullable + private SettingsState peekSettingsStateLocked(int key) { + SettingsState settingsState = mSettingsStates.get(key); + if (settingsState != null) { + return settingsState; + } + + if (!ensureSettingsForUserLocked(getUserIdFromKey(key))) { + return null; + } + return mSettingsStates.get(key); + } + private void migrateAllLegacySettingsIfNeededLocked() { final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); File globalFile = getSettingsFile(key); @@ -3454,7 +3538,6 @@ public class SettingsProvider extends ContentProvider { } } - @GuardedBy("mLock") private void migrateLegacySettingsForUserIfNeededLocked(int userId) { // Every user has secure settings and if no file we need to migrate. final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId); @@ -3469,7 +3552,6 @@ public class SettingsProvider extends ContentProvider { migrateLegacySettingsForUserLocked(dbHelper, database, userId); } - @GuardedBy("mLock") private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper, SQLiteDatabase database, int userId) { // Move over the system settings. @@ -3514,7 +3596,6 @@ public class SettingsProvider extends ContentProvider { } } - @GuardedBy("mLock") private void migrateLegacySettingsLocked(SettingsState settingsState, SQLiteDatabase database, String table) { SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); @@ -3549,7 +3630,7 @@ public class SettingsProvider extends ContentProvider { } } - @GuardedBy("mLock") + @GuardedBy("secureSettings.mLock") private void ensureSecureSettingAndroidIdSetLocked(SettingsState secureSettings) { Setting value = secureSettings.getSettingLocked(Settings.Secure.ANDROID_ID); @@ -3625,7 +3706,6 @@ public class SettingsProvider extends ContentProvider { name, type, changeType); } - @GuardedBy("mLock") private void notifyForConfigSettingsChangeLocked(int key, String prefix, List<String> changedSettings) { @@ -3707,18 +3787,30 @@ public class SettingsProvider extends ContentProvider { } } - private static File getSettingsFile(int key) { - final int userId = getUserIdFromKey(key); - final int type = getTypeFromKey(key); - final File userSystemDirectory = Environment.getUserSystemDirectory(userId); - return switch (type) { - case SETTINGS_TYPE_CONFIG -> new File(userSystemDirectory, SETTINGS_FILE_CONFIG); - case SETTINGS_TYPE_GLOBAL -> new File(userSystemDirectory, SETTINGS_FILE_GLOBAL); - case SETTINGS_TYPE_SYSTEM -> new File(userSystemDirectory, SETTINGS_FILE_SYSTEM); - case SETTINGS_TYPE_SECURE -> new File(userSystemDirectory, SETTINGS_FILE_SECURE); - case SETTINGS_TYPE_SSAID -> new File(userSystemDirectory, SETTINGS_FILE_SSAID); - default -> throw new IllegalArgumentException("Invalid settings key:" + key); - }; + private File getSettingsFile(int key) { + if (isConfigSettingsKey(key)) { + final int userId = getUserIdFromKey(key); + return new File(Environment.getUserSystemDirectory(userId), + SETTINGS_FILE_CONFIG); + } else if (isGlobalSettingsKey(key)) { + final int userId = getUserIdFromKey(key); + return new File(Environment.getUserSystemDirectory(userId), + SETTINGS_FILE_GLOBAL); + } else if (isSystemSettingsKey(key)) { + final int userId = getUserIdFromKey(key); + return new File(Environment.getUserSystemDirectory(userId), + SETTINGS_FILE_SYSTEM); + } else if (isSecureSettingsKey(key)) { + final int userId = getUserIdFromKey(key); + return new File(Environment.getUserSystemDirectory(userId), + SETTINGS_FILE_SECURE); + } else if (isSsaidSettingsKey(key)) { + final int userId = getUserIdFromKey(key); + return new File(Environment.getUserSystemDirectory(userId), + SETTINGS_FILE_SSAID); + } else { + throw new IllegalArgumentException("Invalid settings key:" + key); + } } private Uri getNotificationUriFor(int key, String name) { @@ -3741,11 +3833,14 @@ public class SettingsProvider extends ContentProvider { private int getMaxBytesPerPackageForType(int type) { switch (type) { - case SETTINGS_TYPE_CONFIG, SETTINGS_TYPE_GLOBAL, SETTINGS_TYPE_SECURE, - SETTINGS_TYPE_SSAID -> { + case SETTINGS_TYPE_CONFIG: + case SETTINGS_TYPE_GLOBAL: + case SETTINGS_TYPE_SECURE: + case SETTINGS_TYPE_SSAID: { return SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED; } - default -> { + + default: { return SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED; } } @@ -3762,7 +3857,7 @@ public class SettingsProvider extends ContentProvider { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_NOTIFY_URI_CHANGED -> { + case MSG_NOTIFY_URI_CHANGED: { final int userId = msg.arg1; Uri uri = (Uri) msg.obj; try { @@ -3773,11 +3868,12 @@ public class SettingsProvider extends ContentProvider { if (DEBUG) { Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri); } - } - case MSG_NOTIFY_DATA_CHANGED -> { + } break; + + case MSG_NOTIFY_DATA_CHANGED: { mBackupManager.dataChanged(); scheduleWriteFallbackFilesJob(); - } + } break; } } } @@ -3791,7 +3887,6 @@ public class SettingsProvider extends ContentProvider { mUserId = userId; } - @GuardedBy("mLock") public void upgradeIfNeededLocked() { // The version of all settings for a user is the same (all users have secure). SettingsState secureSettings = getSettingsLocked( @@ -3849,22 +3944,18 @@ public class SettingsProvider extends ContentProvider { systemSettings.setVersionLocked(newVersion); } - @GuardedBy("mLock") private SettingsState getGlobalSettingsLocked() { return getSettingsLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM); } - @GuardedBy("mLock") private SettingsState getSecureSettingsLocked(int userId) { return getSettingsLocked(SETTINGS_TYPE_SECURE, userId); } - @GuardedBy("mLock") private SettingsState getSsaidSettingsLocked(int userId) { return getSettingsLocked(SETTINGS_TYPE_SSAID, userId); } - @GuardedBy("mLock") private SettingsState getSystemSettingsLocked(int userId) { return getSettingsLocked(SETTINGS_TYPE_SYSTEM, userId); } @@ -5308,7 +5399,7 @@ public class SettingsProvider extends ContentProvider { // next version step. // If this is a new profile, check if a secure setting exists for the // owner of the profile and use that value for the work profile. - int owningId = resolveOwningUserIdForSecureSetting(userId, + int owningId = resolveOwningUserIdForSecureSettingLocked(userId, NOTIFICATION_BUBBLES); Setting previous = getGlobalSettingsLocked() .getSettingLocked("notification_bubbles"); @@ -5977,22 +6068,18 @@ public class SettingsProvider extends ContentProvider { return currentVersion; } - @GuardedBy("mLock") private void initGlobalSettingsDefaultValLocked(String key, boolean val) { initGlobalSettingsDefaultValLocked(key, val ? "1" : "0"); } - @GuardedBy("mLock") private void initGlobalSettingsDefaultValLocked(String key, int val) { initGlobalSettingsDefaultValLocked(key, String.valueOf(val)); } - @GuardedBy("mLock") private void initGlobalSettingsDefaultValLocked(String key, long val) { initGlobalSettingsDefaultValLocked(key, String.valueOf(val)); } - @GuardedBy("mLock") private void initGlobalSettingsDefaultValLocked(String key, String val) { final SettingsState globalSettings = getGlobalSettingsLocked(); Setting currentSetting = globalSettings.getSettingLocked(key); @@ -6111,7 +6198,6 @@ public class SettingsProvider extends ContentProvider { } } - @GuardedBy("mLock") private void ensureLegacyDefaultValueAndSystemSetUpdatedLocked(SettingsState settings, int userId) { List<String> names = settings.getSettingNamesLocked(); diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 9d3200dc340daa9ad987db3dacc04658520ed37d..e6a82e83433ba37505083035e7a4875a44469a62 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -214,6 +214,7 @@ android_library { lint: { extra_check_modules: ["SystemUILintChecker"], + warning_checks: ["MissingApacheLicenseDetector"], }, } @@ -254,7 +255,6 @@ filegroup { srcs: [ /* Keyguard converted tests */ // data - "tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt", "tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt", "tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt", "tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt", @@ -402,10 +402,19 @@ filegroup { "tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt", "tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt", "tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt", + ], path: "tests/src", } +filegroup { + name: "SystemUI-tests-multivalent", + srcs: [ + "multivalentTests/src/**/*.kt", + ], + path: "multivalentTests/src", +} + java_library { name: "SystemUI-tests-concurrency", srcs: [ @@ -494,6 +503,7 @@ android_library { "src/**/*.java", "src/**/I*.aidl", ":ReleaseJavaFiles", + ":SystemUI-tests-multivalent", ":SystemUI-tests-utils", ], static_libs: [ @@ -572,6 +582,7 @@ android_robolectric_test { ":SystemUI-tests-utils", ":SystemUI-test-fakes", ":SystemUI-tests-robolectric-pilots", + ":SystemUI-tests-multivalent", ], static_libs: [ "androidx.test.uiautomator_uiautomator", diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 4ea57a8cc0071aa6aeb82830e2a14907cc48ee59..ab4db451406d8a558f69bef02464b853618b2f0c 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -290,9 +290,10 @@ class ActivityLaunchAnimator( controller: Controller?, animate: Boolean = true, packageName: String? = null, + showOverLockscreen: Boolean = false, intentStarter: PendingIntentStarter ) { - startIntentWithAnimation(controller, animate, packageName) { + startIntentWithAnimation(controller, animate, packageName, showOverLockscreen) { intentStarter.startPendingIntent(it) } } diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/MissingApacheLicenseDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/MissingApacheLicenseDetector.kt new file mode 100644 index 0000000000000000000000000000000000000000..46125bee1523238f3f04130f772743c8ff6a5e11 --- /dev/null +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/MissingApacheLicenseDetector.kt @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2023 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.internal.systemui.lint + +import com.android.ide.common.blame.SourcePosition +import com.android.tools.lint.client.api.UElementHandler +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Location +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import java.time.Year +import org.jetbrains.uast.UComment +import org.jetbrains.uast.UFile + +/** + * Checks if every AOSP Java/Kotlin source code file is starting with Apache license information. + */ +class MissingApacheLicenseDetector : Detector(), SourceCodeScanner { + + override fun getApplicableUastTypes() = listOf(UFile::class.java) + + override fun createUastHandler(context: JavaContext): UElementHandler? { + return object : UElementHandler() { + override fun visitFile(node: UFile) { + val firstComment = node.allCommentsInFile.firstOrNull() + // Normally we don't need to explicitly handle suppressing case and just return + // error as usual with indicating node and lint will ignore it for us. But here + // suppressing will be applied on top of comment that doesn't exist so we don't have + // node to return - it's a bit of corner case + if (firstComment != null && firstComment.isSuppressingComment()) { + return + } + if (firstComment == null || !firstComment.isLicenseComment()) { + val firstLineOfFile = + Location.create( + context.file, + SourcePosition(/* lineNumber= */ 1, /* column= */ 1, /* offset= */ 0) + ) + context.report( + issue = ISSUE, + location = firstLineOfFile, + message = + "License header is missing\n" + + "Please add the following copyright and license header to the" + + " beginning of the file:\n\n" + + copyrightHeader + ) + } + } + } + } + + private fun UComment.isSuppressingComment(): Boolean { + val suppressingComment = + "//noinspection ${MissingApacheLicenseDetector::class.java.simpleName}" + return text.contains(suppressingComment) + } + + private fun UComment.isLicenseComment(): Boolean { + // We probably don't want to compare full copyright header in case there are some small + // discrepancies in already existing files, e.g. year. We could do regexp but it should be + // good enough if this detector deals with missing + // license header instead of incorrect license header + return text.contains("Apache License") + } + + private val copyrightHeader: String + get() = + """ + /* + * Copyright (C) ${Year.now().value} 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. + */ + """ + .trimIndent() + .trim() + + companion object { + @JvmField + val ISSUE: Issue = + Issue.create( + id = "MissingApacheLicenseDetector", + briefDescription = "File is missing Apache license information", + explanation = + """ + Every source code file should have copyright and license information \ + attached at the beginning.""", + category = Category.COMPLIANCE, + priority = 8, + // ignored by default and then explicitly overridden in SysUI's soong configuration + severity = Severity.IGNORE, + implementation = + Implementation(MissingApacheLicenseDetector::class.java, Scope.JAVA_FILE_SCOPE), + ) + } +} diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt index 520c8882b4280bd0649baed8b6f6bebd6dbab2f2..e09aa421c68af6558fe1dc6d4c24bf30674a0899 100644 --- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt @@ -42,6 +42,7 @@ class SystemUIIssueRegistry : IssueRegistry() { StaticSettingsProviderDetector.ISSUE, DemotingTestWithoutBugDetector.ISSUE, TestFunctionNameViolationDetector.ISSUE, + MissingApacheLicenseDetector.ISSUE, ) override val api: Int diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/MissingApacheLicenseDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/MissingApacheLicenseDetectorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..78e133fda3d6daf3de020b2f989330e834e340bb --- /dev/null +++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/MissingApacheLicenseDetectorTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2023 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.internal.systemui.lint + +import com.android.tools.lint.checks.infrastructure.TestMode +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue +import java.time.Year +import org.junit.Test + +class MissingApacheLicenseDetectorTest : SystemUILintDetectorTest() { + override fun getDetector(): Detector { + return MissingApacheLicenseDetector() + } + + override fun getIssues(): List<Issue> { + return listOf( + MissingApacheLicenseDetector.ISSUE, + ) + } + + @Test + fun testHasCopyright() { + lint() + .files( + kotlin( + """ + /* + * Copyright (C) ${Year.now().value} 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 test.pkg.name + + class MyTest + """ + .trimIndent() + ) + ) + .issues(MissingApacheLicenseDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun testDoesntHaveCopyright() { + lint() + .files( + kotlin( + """ + package test.pkg.name + + class MyTest + """ + .trimIndent() + ) + ) + // skipping mode SUPPRESSIBLE because lint tries to add @Suppress to class which + // probably doesn't make much sense for license header (which is far above it) and for + // kotlin files that can have several classes. If someone really wants to omit header + // they can do it with //noinspection + .skipTestModes(TestMode.SUPPRESSIBLE) + .issues(MissingApacheLicenseDetector.ISSUE) + .run() + .expectContains("License header is missing") + } +} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index 6496507218a5493387de6b1e3d4ad073b5793325..2dc53ab8bf76e4ae91fa9a6892f20f508de42b6e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -16,6 +16,7 @@ package com.android.compose.animation.scene +import android.util.Log import androidx.annotation.VisibleForTesting import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.Spring @@ -36,7 +37,7 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp -import com.android.compose.nestedscroll.PriorityPostNestedScrollConnection +import com.android.compose.nestedscroll.PriorityNestedScrollConnection import kotlin.math.absoluteValue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -130,10 +131,12 @@ class SceneGestureHandler( internal val currentScene: Scene get() = layoutImpl.scene(transitionState.currentScene) - internal val isDrivingTransition + @VisibleForTesting + val isDrivingTransition get() = transitionState == swipeTransition - internal var isAnimatingOffset + @VisibleForTesting + var isAnimatingOffset get() = swipeTransition.isAnimatingOffset private set(value) { swipeTransition.isAnimatingOffset = value @@ -154,20 +157,26 @@ class SceneGestureHandler( */ private val positionalThreshold = with(layoutImpl.density) { 56.dp.toPx() } + internal var gestureWithPriority: Any? = null + internal fun onDragStarted() { if (isDrivingTransition) { // This [transition] was already driving the animation: simply take over it. - if (isAnimatingOffset) { - // Stop animating and start from where the current offset. Setting the animation job - // to `null` will effectively cancel the animation. - swipeTransition.stopOffsetAnimation() - swipeTransition.dragOffset = swipeTransition.offsetAnimatable.value - } - + // Stop animating and start from where the current offset. + swipeTransition.stopOffsetAnimation() return } - // TODO(b/290184746): Better handle interruptions here if state != idle. + val transition = transitionState + if (transition is TransitionState.Transition) { + // TODO(b/290184746): Better handle interruptions here if state != idle. + Log.w( + TAG, + "start from TransitionState.Transition is not fully supported: from" + + " ${transition.fromScene} to ${transition.toScene} " + + "(progress ${transition.progress})" + ) + } val fromScene = currentScene @@ -196,6 +205,8 @@ class SceneGestureHandler( } internal fun onDrag(delta: Float) { + if (delta == 0f) return + swipeTransition.dragOffset += delta // First check transition.fromScene should be changed for the case where the user quickly @@ -293,48 +304,77 @@ class SceneGestureHandler( return } - // We were not animating. - if (swipeTransition._fromScene == swipeTransition._toScene) { - transitionState = TransitionState.Idle(swipeTransition._fromScene.key) - return + fun animateTo(targetScene: Scene, targetOffset: Float) { + // If the effective current scene changed, it should be reflected right now in the + // current scene state, even before the settle animation is ongoing. That way all the + // swipeables and back handlers will be refreshed and the user can for instance quickly + // swipe vertically from A => B then horizontally from B => C, or swipe from A => B then + // immediately go back B => A. + if (targetScene != swipeTransition._currentScene) { + swipeTransition._currentScene = targetScene + layoutImpl.onChangeScene(targetScene.key) + } + + animateOffset( + initialVelocity = velocity, + targetOffset = targetOffset, + targetScene = targetScene.key + ) } - // Compute the destination scene (and therefore offset) to settle in. - val targetOffset: Float - val targetScene: Scene - val offset = swipeTransition.dragOffset - val distance = swipeTransition.distance - if ( - canChangeScene && + val fromScene = swipeTransition._fromScene + if (canChangeScene) { + // If we are halfway between two scenes, we check what the target will be based on the + // velocity and offset of the transition, then we launch the animation. + + val toScene = swipeTransition._toScene + if (fromScene == toScene) { + // We were not animating. + transitionState = TransitionState.Idle(fromScene.key) + return + } + + // Compute the destination scene (and therefore offset) to settle in. + val offset = swipeTransition.dragOffset + val distance = swipeTransition.distance + if ( shouldCommitSwipe( offset, distance, velocity, - wasCommitted = swipeTransition._currentScene == swipeTransition._toScene, + wasCommitted = swipeTransition._currentScene == toScene, ) - ) { - targetOffset = distance - targetScene = swipeTransition._toScene + ) { + // Animate to the next scene + animateTo(targetScene = toScene, targetOffset = distance) + } else { + // Animate to the initial scene + animateTo(targetScene = fromScene, targetOffset = 0f) + } } else { - targetOffset = 0f - targetScene = swipeTransition._fromScene - } - - // If the effective current scene changed, it should be reflected right now in the current - // scene state, even before the settle animation is ongoing. That way all the swipeables and - // back handlers will be refreshed and the user can for instance quickly swipe vertically - // from A => B then horizontally from B => C, or swipe from A => B then immediately go back - // B => A. - if (targetScene != swipeTransition._currentScene) { - swipeTransition._currentScene = targetScene - layoutImpl.onChangeScene(targetScene.key) + // We are doing an overscroll animation between scenes. In this case, we can also start + // from the idle position. + + val startFromIdlePosition = swipeTransition.dragOffset == 0f + + if (startFromIdlePosition) { + // If there is a next scene, we start the overscroll animation. + val target = fromScene.findTargetSceneAndDistance(velocity) + val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key + if (isValidTarget) { + swipeTransition._toScene = layoutImpl.scene(target.sceneKey) + swipeTransition._distance = target.distance + + animateTo(targetScene = fromScene, targetOffset = 0f) + } else { + // We will not animate + transitionState = TransitionState.Idle(fromScene.key) + } + } else { + // We were between two scenes: animate to the initial scene. + animateTo(targetScene = fromScene, targetOffset = 0f) + } } - - animateOffset( - initialVelocity = velocity, - targetOffset = targetOffset, - targetScene = targetScene.key - ) } /** @@ -378,74 +418,33 @@ class SceneGestureHandler( targetScene: SceneKey, ) { swipeTransition.startOffsetAnimation { - coroutineScope - .launch { - if (!isAnimatingOffset) { - swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset) - } - isAnimatingOffset = true - - swipeTransition.offsetAnimatable.animateTo( - targetOffset, - // TODO(b/290184746): Make this spring spec configurable. - spring( - stiffness = Spring.StiffnessMediumLow, - visibilityThreshold = OffsetVisibilityThreshold - ), - initialVelocity = initialVelocity, - ) - - // Now that the animation is done, the state should be idle. Note that if the - // state was changed since this animation started, some external code changed it - // and we shouldn't do anything here. Note also that this job will be cancelled - // in the case where the user intercepts this swipe. - if (isDrivingTransition) { - transitionState = TransitionState.Idle(targetScene) - } + coroutineScope.launch { + if (!isAnimatingOffset) { + swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset) } - .also { it.invokeOnCompletion { isAnimatingOffset = false } } - } - } - - internal fun animateOverscroll(velocity: Velocity): Velocity { - val velocityAmount = - when (orientation) { - Orientation.Vertical -> velocity.y - Orientation.Horizontal -> velocity.x - } - - if (velocityAmount == 0f) { - // There is no remaining velocity - return Velocity.Zero - } + isAnimatingOffset = true + + swipeTransition.offsetAnimatable.animateTo( + targetOffset, + // TODO(b/290184746): Make this spring spec configurable. + spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = OffsetVisibilityThreshold + ), + initialVelocity = initialVelocity, + ) - val fromScene = currentScene - val target = fromScene.findTargetSceneAndDistance(velocityAmount) - val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key + isAnimatingOffset = false - if (!isValidTarget || isDrivingTransition) { - // We have not found a valid target or we are already in a transition - return Velocity.Zero + // Now that the animation is done, the state should be idle. Note that if the state + // was changed since this animation started, some external code changed it and we + // shouldn't do anything here. Note also that this job will be cancelled in the case + // where the user intercepts this swipe. + if (isDrivingTransition) { + transitionState = TransitionState.Idle(targetScene) + } + } } - - swipeTransition._currentScene = fromScene - swipeTransition._fromScene = fromScene - swipeTransition._toScene = layoutImpl.scene(target.sceneKey) - swipeTransition._distance = target.distance - swipeTransition.absoluteDistance = target.distance.absoluteValue - swipeTransition.stopOffsetAnimation() - swipeTransition.dragOffset = 0f - - transitionState = swipeTransition - - animateOffset( - initialVelocity = velocityAmount, - targetOffset = 0f, - targetScene = fromScene.key - ) - - // The animateOffset animation consumes any remaining velocity. - return velocity } private class SwipeTransition(initialScene: Scene) : TransitionState.Transition { @@ -500,6 +499,11 @@ class SceneGestureHandler( /** Stops any ongoing offset animation. */ fun stopOffsetAnimation() { offsetAnimationJob?.cancel() + + if (isAnimatingOffset) { + isAnimatingOffset = false + dragOffset = offsetAnimatable.value + } } /** The absolute distance between [fromScene] and [toScene]. */ @@ -513,21 +517,31 @@ class SceneGestureHandler( val distance: Float get() = _distance } + + companion object { + private const val TAG = "SceneGestureHandler" + } } private class SceneDraggableHandler( private val gestureHandler: SceneGestureHandler, ) : DraggableHandler { override suspend fun onDragStarted(coroutineScope: CoroutineScope, startedPosition: Offset) { + gestureHandler.gestureWithPriority = this gestureHandler.onDragStarted() } override fun onDelta(pixels: Float) { - gestureHandler.onDrag(delta = pixels) + if (gestureHandler.gestureWithPriority == this) { + gestureHandler.onDrag(delta = pixels) + } } override suspend fun onDragStopped(coroutineScope: CoroutineScope, velocity: Float) { - gestureHandler.onDragStopped(velocity = velocity, canChangeScene = true) + if (gestureHandler.gestureWithPriority == this) { + gestureHandler.gestureWithPriority = null + gestureHandler.onDragStopped(velocity = velocity, canChangeScene = true) + } } } @@ -535,7 +549,7 @@ private class SceneDraggableHandler( class SceneNestedScrollHandler( private val gestureHandler: SceneGestureHandler, ) : NestedScrollHandler { - override val connection: PriorityPostNestedScrollConnection = nestedScrollConnection() + override val connection: PriorityNestedScrollConnection = nestedScrollConnection() private fun Offset.toAmount() = when (gestureHandler.orientation) { @@ -555,7 +569,7 @@ class SceneNestedScrollHandler( Orientation.Vertical -> Offset(x = 0f, y = this) } - private fun nestedScrollConnection(): PriorityPostNestedScrollConnection { + private fun nestedScrollConnection(): PriorityNestedScrollConnection { // The next potential scene is calculated during the canStart var nextScene: SceneKey? = null @@ -566,29 +580,58 @@ class SceneNestedScrollHandler( // moving on to the next scene. var gestureStartedOnNestedChild = false - return PriorityPostNestedScrollConnection( - canStart = { offsetAvailable, offsetBeforeStart -> - val amount = offsetAvailable.toAmount() - if (amount == 0f) return@PriorityPostNestedScrollConnection false + fun findNextScene(amount: Float): SceneKey? { + val fromScene = gestureHandler.currentScene + return when { + amount < 0f -> fromScene.upOrLeft(gestureHandler.orientation) + amount > 0f -> fromScene.downOrRight(gestureHandler.orientation) + else -> null + } + } + return PriorityNestedScrollConnection( + canStartPreScroll = { offsetAvailable, offsetBeforeStart -> gestureStartedOnNestedChild = offsetBeforeStart != Offset.Zero - val fromScene = gestureHandler.currentScene - nextScene = - when { - amount < 0f -> fromScene.upOrLeft(gestureHandler.orientation) - amount > 0f -> fromScene.downOrRight(gestureHandler.orientation) - else -> null - } + val canInterceptPreScroll = + gestureHandler.isDrivingTransition && + !gestureStartedOnNestedChild && + offsetAvailable.toAmount() != 0f + + if (!canInterceptPreScroll) return@PriorityNestedScrollConnection false + + nextScene = gestureHandler.swipeTransitionToScene.key + true + }, + canStartPostScroll = { offsetAvailable, offsetBeforeStart -> + val amount = offsetAvailable.toAmount() + if (amount == 0f) return@PriorityNestedScrollConnection false + + gestureStartedOnNestedChild = offsetBeforeStart != Offset.Zero + nextScene = findNextScene(amount) + nextScene != null + }, + canStartPostFling = { velocityAvailable -> + val amount = velocityAvailable.toAmount() + if (amount == 0f) return@PriorityNestedScrollConnection false + + // We could start an overscroll animation + gestureStartedOnNestedChild = true + nextScene = findNextScene(amount) nextScene != null }, canContinueScroll = { priorityScene == gestureHandler.swipeTransitionToScene.key }, onStart = { + gestureHandler.gestureWithPriority = this priorityScene = nextScene gestureHandler.onDragStarted() }, onScroll = { offsetAvailable -> + if (gestureHandler.gestureWithPriority != this) { + return@PriorityNestedScrollConnection Offset.Zero + } + val amount = offsetAvailable.toAmount() // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is @@ -598,6 +641,10 @@ class SceneNestedScrollHandler( amount.toOffset() }, onStop = { velocityAvailable -> + if (gestureHandler.gestureWithPriority != this) { + return@PriorityNestedScrollConnection Velocity.Zero + } + priorityScene = null gestureHandler.onDragStopped( @@ -608,11 +655,6 @@ class SceneNestedScrollHandler( // The onDragStopped animation consumes any remaining velocity. velocityAvailable }, - onPostFling = { velocityAvailable -> - // If there is any velocity left, we can try running an overscroll animation between - // scenes. - gestureHandler.animateOverscroll(velocity = velocityAvailable) - }, ) } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt similarity index 74% rename from packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnection.kt rename to packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt index 793a9a59405a26aaf238ac1422f096d3895b447b..824c10b88a9bd4d0c7d53e4216bbe4346dfbd779 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnection.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt @@ -22,22 +22,23 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.unit.Velocity /** - * This [NestedScrollConnection] waits for a child to scroll ([onPostScroll]), and then decides (via - * [canStart]) if it should take over scrolling. If it does, it will scroll before its children, - * until [canContinueScroll] allows it. + * This [NestedScrollConnection] waits for a child to scroll ([onPreScroll] or [onPostScroll]), and + * then decides (via [canStartPreScroll] or [canStartPostScroll]) if it should take over scrolling. + * If it does, it will scroll before its children, until [canContinueScroll] allows it. * * Note: Call [reset] before destroying this object to make sure you always get a call to [onStop] * after [onStart]. * * @sample com.android.compose.animation.scene.rememberSwipeToSceneNestedScrollConnection */ -class PriorityPostNestedScrollConnection( - private val canStart: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean, +class PriorityNestedScrollConnection( + private val canStartPreScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean, + private val canStartPostScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean, + private val canStartPostFling: (velocityAvailable: Velocity) -> Boolean, private val canContinueScroll: () -> Boolean, private val onStart: () -> Unit, private val onScroll: (offsetAvailable: Offset) -> Offset, private val onStop: (velocityAvailable: Velocity) -> Velocity, - private val onPostFling: suspend (velocityAvailable: Velocity) -> Velocity, ) : NestedScrollConnection { /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */ @@ -57,26 +58,21 @@ class PriorityPostNestedScrollConnection( if ( isPriorityMode || source == NestedScrollSource.Fling || - !canStart(available, offsetBeforeStart) + !canStartPostScroll(available, offsetBeforeStart) ) { // The priority mode cannot start so we won't consume the available offset. return Offset.Zero } - // Step 1: It's our turn! We start capturing scroll events when one of our children has an - // available offset following a scroll event. - isPriorityMode = true - - // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is - // lifted (step 3b), or this object has been destroyed (step 3c). - onStart() - - return onScroll(available) + return onPriorityStart(available) } override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { if (!isPriorityMode) { if (source != NestedScrollSource.Fling) { + if (canStartPreScroll(available, offsetScrolledBeforePriorityMode)) { + return onPriorityStart(available) + } // We want to track the amount of offset consumed before entering priority mode offsetScrolledBeforePriorityMode += available } @@ -87,6 +83,11 @@ class PriorityPostNestedScrollConnection( if (!canContinueScroll()) { // Step 3a: We have lost priority and we no longer need to intercept scroll events. onPriorityStop(velocity = Velocity.Zero) + + // We've just reset offsetScrolledBeforePriorityMode to Offset.Zero + // We want to track the amount of offset consumed before entering priority mode + offsetScrolledBeforePriorityMode += available + return Offset.Zero } @@ -101,7 +102,14 @@ class PriorityPostNestedScrollConnection( } override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { - return onPostFling(available) + if (!canStartPostFling(available)) { + return Velocity.Zero + } + + onPriorityStart(available = Offset.Zero) + + // This is the last event of a scroll gesture. + return onPriorityStop(available) } /** Method to call before destroying the object or to reset the initial state. */ @@ -110,8 +118,23 @@ class PriorityPostNestedScrollConnection( onPriorityStop(velocity = Velocity.Zero) } - private fun onPriorityStop(velocity: Velocity): Velocity { + private fun onPriorityStart(available: Offset): Offset { + if (isPriorityMode) { + error("This should never happen, onPriorityStart() was called when isPriorityMode") + } + + // Step 1: It's our turn! We start capturing scroll events when one of our children has an + // available offset following a scroll event. + isPriorityMode = true + + // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is + // lifted (step 3b), or this object has been destroyed (step 3c). + onStart() + return onScroll(available) + } + + private fun onPriorityStop(velocity: Velocity): Velocity { // We can restart tracking the consumed offsets from scratch. offsetScrolledBeforePriorityMode = Offset.Zero diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index 3e0f7ba1bf789bb89c797948b610865d9552b0cc..6791a85ff21c2e41dc348ff3311171af5f70bb6b 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -47,7 +47,7 @@ class SceneGestureHandlerTest { scene(SceneC) { Text("SceneC") } } - private val sceneGestureHandler = + val sceneGestureHandler = SceneGestureHandler( layoutImpl = SceneTransitionLayoutImpl( @@ -81,6 +81,10 @@ class SceneGestureHandlerTest { coroutineScope.testScheduler.advanceUntilIdle() } + fun runCurrent() { + coroutineScope.testScheduler.runCurrent() + } + fun assertScene(currentScene: SceneKey, isIdle: Boolean) { val idleMsg = if (isIdle) "MUST" else "MUST NOT" assertWithMessage("transitionState $idleMsg be Idle") @@ -164,6 +168,33 @@ class SceneGestureHandlerTest { assertScene(currentScene = SceneA, isIdle = true) } + @Test + fun startGestureDuringAnimatingOffset_shouldImmediatelyStopTheAnimation() = runGestureTest { + draggable.onDragStarted(coroutineScope = coroutineScope, startedPosition = Offset.Zero) + assertScene(currentScene = SceneA, isIdle = false) + + draggable.onDelta(pixels = deltaInPixels10) + assertScene(currentScene = SceneA, isIdle = false) + + draggable.onDragStopped( + coroutineScope = coroutineScope, + velocity = velocityThreshold, + ) + + // The stop animation is not started yet + assertThat(sceneGestureHandler.isAnimatingOffset).isFalse() + + runCurrent() + + assertThat(sceneGestureHandler.isAnimatingOffset).isTrue() + assertThat(sceneGestureHandler.isDrivingTransition).isTrue() + assertScene(currentScene = SceneC, isIdle = false) + + // Start a new gesture while the offset is animating + draggable.onDragStarted(coroutineScope = coroutineScope, startedPosition = Offset.Zero) + assertThat(sceneGestureHandler.isAnimatingOffset).isFalse() + } + @Test fun onInitialPreScroll_doNotChangeState() = runGestureTest { nestedScroll.onPreScroll(available = offsetY10, source = NestedScrollSource.Drag) @@ -281,4 +312,52 @@ class SceneGestureHandlerTest { advanceUntilIdle() assertScene(currentScene = SceneA, isIdle = true) } + + @Test + fun beforeDraggableStart_drag_shouldBeIgnored() = runGestureTest { + draggable.onDelta(deltaInPixels10) + assertScene(currentScene = SceneA, isIdle = true) + } + @Test + fun beforeDraggableStart_stop_shouldBeIgnored() = runGestureTest { + draggable.onDragStopped(coroutineScope, velocityThreshold) + assertScene(currentScene = SceneA, isIdle = true) + } + + @Test + fun beforeNestedScrollStart_stop_shouldBeIgnored() = runGestureTest { + nestedScroll.onPreFling(Velocity(0f, velocityThreshold)) + assertScene(currentScene = SceneA, isIdle = true) + } + + @Test + fun startNestedScrollWhileDragging() = runGestureTest { + draggable.onDragStarted(coroutineScope, Offset.Zero) + assertScene(currentScene = SceneA, isIdle = false) + val transition = transitionState as Transition + + draggable.onDelta(deltaInPixels10) + assertThat(transition.progress).isEqualTo(0.1f) + + // now we can intercept the scroll events + nestedScrollEvents(available = offsetY10) + assertThat(transition.progress).isEqualTo(0.2f) + + // this should be ignored, we are scrolling now! + draggable.onDragStopped(coroutineScope, velocityThreshold) + assertScene(currentScene = SceneA, isIdle = false) + + nestedScrollEvents(available = offsetY10) + assertThat(transition.progress).isEqualTo(0.3f) + + nestedScrollEvents(available = offsetY10) + assertThat(transition.progress).isEqualTo(0.4f) + + nestedScroll.onPreFling(available = Velocity(0f, velocityThreshold)) + assertScene(currentScene = SceneC, isIdle = false) + + // wait for the stop animation + advanceUntilIdle() + assertScene(currentScene = SceneC, isIdle = true) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt similarity index 66% rename from packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnectionTest.kt rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt index 8e2b77a2f2a0d45d55b1e868ad20fefd9dec16d8..122774bb28fe139e0950f113fb9a9ec2d7c214ff 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnectionTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt @@ -29,20 +29,22 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class PriorityPostNestedScrollConnectionTest { - private var canStart = false +class PriorityNestedScrollConnectionTest { + private var canStartPreScroll = false + private var canStartPostScroll = false + private var canStartPostFling = false private var canContinueScroll = false private var isStarted = false private var lastScroll: Offset? = null private var returnOnScroll = Offset.Zero private var lastStop: Velocity? = null private var returnOnStop = Velocity.Zero - private var lastOnPostFling: Velocity? = null - private var returnOnPostFling = Velocity.Zero private val scrollConnection = - PriorityPostNestedScrollConnection( - canStart = { _, _ -> canStart }, + PriorityNestedScrollConnection( + canStartPreScroll = { _, _ -> canStartPreScroll }, + canStartPostScroll = { _, _ -> canStartPostScroll }, + canStartPostFling = { canStartPostFling }, canContinueScroll = { canContinueScroll }, onStart = { isStarted = true }, onScroll = { @@ -53,10 +55,6 @@ class PriorityPostNestedScrollConnectionTest { lastStop = it returnOnStop }, - onPostFling = { - lastOnPostFling = it - returnOnPostFling - }, ) private val offset1 = Offset(1f, 1f) @@ -64,8 +62,29 @@ class PriorityPostNestedScrollConnectionTest { private val velocity1 = Velocity(1f, 1f) private val velocity2 = Velocity(2f, 2f) - private fun startPriorityMode() { - canStart = true + @Test + fun step1_priorityModeShouldStartOnlyOnPreScroll() = runTest { + canStartPreScroll = true + + scrollConnection.onPostScroll( + consumed = Offset.Zero, + available = Offset.Zero, + source = NestedScrollSource.Drag + ) + assertThat(isStarted).isEqualTo(false) + + scrollConnection.onPreFling(available = Velocity.Zero) + assertThat(isStarted).isEqualTo(false) + + scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero) + assertThat(isStarted).isEqualTo(false) + + scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag) + assertThat(isStarted).isEqualTo(true) + } + + private fun startPriorityModePostScroll() { + canStartPostScroll = true scrollConnection.onPostScroll( consumed = Offset.Zero, available = Offset.Zero, @@ -75,7 +94,7 @@ class PriorityPostNestedScrollConnectionTest { @Test fun step1_priorityModeShouldStartOnlyOnPostScroll() = runTest { - canStart = true + canStartPostScroll = true scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag) assertThat(isStarted).isEqualTo(false) @@ -86,7 +105,7 @@ class PriorityPostNestedScrollConnectionTest { scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero) assertThat(isStarted).isEqualTo(false) - startPriorityMode() + startPriorityModePostScroll() assertThat(isStarted).isEqualTo(true) } @@ -99,13 +118,13 @@ class PriorityPostNestedScrollConnectionTest { ) assertThat(isStarted).isEqualTo(false) - startPriorityMode() + startPriorityModePostScroll() assertThat(isStarted).isEqualTo(true) } @Test fun step1_onPriorityModeStarted_receiveAvailableOffset() { - canStart = true + canStartPostScroll = true scrollConnection.onPostScroll( consumed = offset1, @@ -118,7 +137,7 @@ class PriorityPostNestedScrollConnectionTest { @Test fun step2_onPriorityMode_shouldContinueIfAllowed() { - startPriorityMode() + startPriorityModePostScroll() canContinueScroll = true scrollConnection.onPreScroll(available = offset1, source = NestedScrollSource.Drag) @@ -132,7 +151,7 @@ class PriorityPostNestedScrollConnectionTest { @Test fun step3a_onPriorityMode_shouldStopIfCannotContinue() { - startPriorityMode() + startPriorityModePostScroll() canContinueScroll = false scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag) @@ -142,7 +161,7 @@ class PriorityPostNestedScrollConnectionTest { @Test fun step3b_onPriorityMode_shouldStopOnFling() = runTest { - startPriorityMode() + startPriorityModePostScroll() canContinueScroll = true scrollConnection.onPreFling(available = Velocity.Zero) @@ -152,7 +171,7 @@ class PriorityPostNestedScrollConnectionTest { @Test fun step3c_onPriorityMode_shouldStopOnReset() { - startPriorityMode() + startPriorityModePostScroll() canContinueScroll = true scrollConnection.reset() @@ -162,11 +181,34 @@ class PriorityPostNestedScrollConnectionTest { @Test fun receive_onPostFling() = runTest { + canStartPostFling = true + scrollConnection.onPostFling( consumed = velocity1, available = velocity2, ) - assertThat(lastOnPostFling).isEqualTo(velocity2) + assertThat(lastStop).isEqualTo(velocity2) + } + + @Test + fun step1_priorityModeShouldStartOnlyOnPostFling() = runTest { + canStartPostFling = true + + scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag) + assertThat(isStarted).isEqualTo(false) + + scrollConnection.onPostScroll( + consumed = Offset.Zero, + available = Offset.Zero, + source = NestedScrollSource.Drag + ) + assertThat(isStarted).isEqualTo(false) + + scrollConnection.onPreFling(available = Velocity.Zero) + assertThat(isStarted).isEqualTo(false) + + scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero) + assertThat(isStarted).isEqualTo(true) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt similarity index 100% rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt diff --git a/packages/SystemUI/multivalentTestsForDevice b/packages/SystemUI/multivalentTestsForDevice new file mode 120000 index 0000000000000000000000000000000000000000..20ee34ada103e06db73c8bee179207299f17e032 --- /dev/null +++ b/packages/SystemUI/multivalentTestsForDevice @@ -0,0 +1 @@ +multivalentTests \ No newline at end of file diff --git a/packages/SystemUI/multivalentTestsForDeviceless b/packages/SystemUI/multivalentTestsForDeviceless new file mode 120000 index 0000000000000000000000000000000000000000..20ee34ada103e06db73c8bee179207299f17e032 --- /dev/null +++ b/packages/SystemUI/multivalentTestsForDeviceless @@ -0,0 +1 @@ +multivalentTests \ No newline at end of file diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index 9cc87fde122f82345a69179c790f7fd2083cfee7..f0e3c99b007d68f4424fd78efff3c5360f57328f 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -56,6 +56,16 @@ public interface ActivityStarter { Runnable intentSentUiThreadCallback, @Nullable ActivityLaunchAnimator.Controller animationController); + /** + * Similar to {@link #startPendingIntentDismissingKeyguard}, except that it supports launching + * activities on top of the keyguard. If the activity supports {@code showOverLockscreen}, it + * will show over keyguard without first dimissing it. If it doesn't support it, calling this + * method is exactly the same as calling {@link #startPendingIntentDismissingKeyguard}. + */ + void startPendingIntentMaybeDismissingKeyguard(PendingIntent intent, + @Nullable Runnable intentSentUiThreadCallback, + @Nullable ActivityLaunchAnimator.Controller animationController); + /** * The intent flag can be specified in startActivity(). */ diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml index 2eb1bb5dd941b8be597b89215fec971b3ab1f7b6..a6a81222a8cbb63248320126ec5476a830dafb60 100644 --- a/packages/SystemUI/res-keyguard/values-af/strings.xml +++ b/packages/SystemUI/res-keyguard/values-af/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans stadig"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laaiproses word geoptimeer om battery te beskerm"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kwessie met laaibykomstigheid"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Druk Kieslys om te ontsluit."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netwerk is gesluit"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Geen SIM nie"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Voeg ’n SIM by."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Die SIM is weg of nie leesbaar nie. Voeg ’n SIM by."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Onbruikbare SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Jou SIM is permanent gedeaktiveer.\n Kontak jou draadlose diensverskaffer vir ’n ander SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is gesluit."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-gesluit."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Ontsluit tans SIM …"</string> diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml index 5fd946b1f188f98e9c4010c0e0d69857cde98b19..fb84414c19df332b4d7ad3c6f506ca15fc9d28c2 100644 --- a/packages/SystemUI/res-keyguard/values-am/strings.xml +++ b/packages/SystemUI/res-keyguard/values-am/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በá‹áŒá‰³ ኃá‹áˆáŠ• በመሙላት ላá‹"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ባትሪን ለመጠበቅ ኃá‹áˆ መሙላት ተብቷáˆ"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ተለዋዋáŒáŠ• ኃá‹áˆ በመሙላት ላዠችáŒáˆ"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ለመáŠáˆá‰µ áˆáŠ“ሌ ተጫንá¢"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"አá‹á‰³áˆ¨ መረብ ተቆáˆááˆ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"áˆáŠ•áˆ SIM የለáˆ"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"ሲሠያáŠáˆ‰á¢"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ሲሙ ጠáቷሠወá‹áˆ አá‹áŠá‰ ብáˆá¢ ሲሠያáŠáˆ‰á¢"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ጥቅሠላዠየማá‹á‹áˆ ሲáˆá¢"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ሲáˆá‹Ž በቋሚáŠá‰µ ቦá‹áŠ—áˆá¢\n ለሌላ ሲሠየእáˆáˆµá‹ŽáŠ• አገáˆáŒáˆŽá‰µ ሰጪ á‹«áŒáŠ™á¢"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ሲሠተቆáˆááˆá¢"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ሲሠበPUK የተቆለሠáŠá‹á¢"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"ሲáˆáŠ• በመáŠáˆá‰µ ላá‹â€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml index b6479f456a2fb65013a46db57eb169091314ec59..fb330929f4d37b6ea533a5d0a076267639d1cf89 100644 --- a/packages/SystemUI/res-keyguard/values-ar/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جار٠الشØÙ† ببطء"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • تم تØسين الشØÙ† Ù„Øماية البطارية"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"‫<xliff:g id="PERCENTAGE">%s</xliff:g> • مشكلة متعلّقة بجهاز الشØÙ† الملØÙ‚"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"اضغط على \"القائمة\" لإلغاء التأمين."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"الشبكة مؤمّنة"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"â€Ù„ا تتوÙر شريØØ© SIM."</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"â€ÙŠØ¬Ø¨ إضاÙØ© شريØØ© SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"â€Ø´Ø±ÙŠØØ© SIM Ù…Ùقودة أو غير قابلة للقراءة. يجب إضاÙØ© شريØØ© SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"â€Ø´Ø±ÙŠØØ© SIM غير قابلة للاستخدام."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"â€ØªÙ… إيقا٠شريØØ© SIM نهائيًا.\n عليك التواصل مع مقدم خدمة اللاسلكي للØصول على شريØØ© SIM أخرى."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"â€Ø´Ø±ÙŠØØ© SIM Ù…ÙÙ‚Ùَلة."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"â€Ø´Ø±ÙŠØØ© SIM Ù…ÙÙ‚Ùَلة برمز PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"â€Ø¬Ø§Ø±Ù إلغاء Ù‚ÙÙ„ شريØØ© SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml index a41a704f345ac853d1c3fc90c16dbae294f686fa..a123bb7957088db0c01302f23f6e90f0a30dba9a 100644 --- a/packages/SystemUI/res-keyguard/values-as/strings.xml +++ b/packages/SystemUI/res-keyguard/values-as/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • লাহে লাহে চাৰà§à¦œ কৰি থকা হৈছে"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • বেটাৰী সà§à§°à¦•à§à¦·à¦¿à¦¤ কৰিবলৈ চাৰà§à¦œà¦¿à¦‚ অপà§à¦Ÿà¦¿à¦®à¦¾à¦‡à¦œ কৰা হৈছে"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চাৰà§à¦œà¦¿à¦™à§° আনà§à¦·à¦‚গিক সামগà§à§°à§€à¦¤ সমসà§à¦¯à¦¾ হৈছে"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"আনলক কৰিবলৈ মেনৠটিপক।"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটৱরà§à¦• লক কৰা অৱসà§à¦¥à¦¾à¦¤ আছে"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"কোনো ছিম নাই"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"à¦à¦–ন ছিম যোগ দিয়ক।"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ছিম নাই অথবা সেইখন পঢ়িব নোৱাৰি। à¦à¦–ন ছিম যোগ দিয়ক।"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"বà§à¦¯à§±à¦¹à¦¾à§° কৰিব নোৱৰা ছিম।"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"আপোনাৰ ছিমখন সà§à¦¥à¦¾à¦¯à¦¼à§€à¦à¦¾à§±à§‡ নিষà§à¦•à§à§°à¦¿à¦¯à¦¼ কৰা হৈছে।\n অনà§à¦¯ à¦à¦–ন ছিমৰ বাবে আপোনাৰ ৱায়াৰলেছ সেৱা পà§à§°à¦¦à¦¾à¦¨à¦•à¦¾à§°à§€à§° সৈতে যোগাযোগ কৰক।"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ছিমখন লক হৈ আছে।"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ছিমখন PUKৰ দà§à¦¬à¦¾à§°à¦¾ লক হৈ আছে।"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"ছিম আনলক কৰি থকা হৈছে…"</string> diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml index ed969c7c8e292288e1ad73be3ded2e2a7703fd9d..b133b30a657c3e5ba209ea0ebe3ef09d283f18ae 100644 --- a/packages/SystemUI/res-keyguard/values-az/strings.xml +++ b/packages/SystemUI/res-keyguard/values-az/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • YavaÅŸ enerji yığır"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batareyanı qorumaq üçün ÅŸarj optimallaÅŸdırılıb"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Åžarj aksesuarı ilÉ™ baÄŸlı problem"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Kilidi açmaq üçün Menyu düymÉ™sinÉ™ basın."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ŞəbÉ™kÉ™ kilidlidir"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM yoxdur"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM É™lavÉ™ edin."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM kart yoxdur vÉ™ ya oxuna bilinmir. SIM É™lavÉ™ edin."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ä°stifadÉ™yÉ™ yararsız SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM kartınız hÉ™miÅŸÉ™lik deaktiv edilib.\n BaÅŸqa SIM kart üçün simsiz xidmÉ™t provayderinizÉ™ müraciÉ™t edin."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM kilidlÉ™nib."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM kart PUK ilÉ™ kilidlÉ™nib."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM kiliddÉ™n çıxarılır…"</string> diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml index b0a6471994160a4c21bec182a18dfe135a51c757..9a919624c4fac147110570ecb13125bda0fb903e 100644 --- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo se puni"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje je optimizovano da bi se zaÅ¡titila baterija"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem sa dodatnim priborom za punjenje"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Meni da biste otkljuÄali."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zakljuÄana"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nema SIM-a"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Dodajte SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM nedostaje ili ne može da se proÄita. Dodajte SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Neupotrebljiv SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM je trajno deaktiviran.\n Obratite se dobavljaÄu usluge bežiÄne telefonije da biste dobili drugi SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM je zakljuÄan."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM je zakljuÄan PUK-om."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"OtkljuÄava se SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml index 11cc77dca5daaf314357c28b847127c2156867c9..5e46b715c0e3f67402212db37340bec5cdcfeb43 100644 --- a/packages/SystemUI/res-keyguard/values-be/strings.xml +++ b/packages/SystemUI/res-keyguard/values-be/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе Ð¿Ð°Ð²Ð¾Ð»ÑŒÐ½Ð°Ñ Ð·Ð°Ñ€Ð°Ð´ÐºÐ°"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • У мÑтах зберажÑÐ½Ð½Ñ Ð°ÐºÑƒÐ¼ÑƒÐ»Ñтара зарадка аптымізавана"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Праблема з зараднай прыладай"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ÐаціÑніце кнопку \"Меню\", каб разблакіраваць."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сетка заблакіравана"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ÐÑма SIM-карты"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Дадайце SIM-карту."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-карта адÑутнічае ці не чытаецца. Дадайце SIM-карту."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ÐÐµÐ¿Ñ€Ñ‹Ð´Ð°Ñ‚Ð½Ð°Ñ Ð´Ð»Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹ÑÑ‚Ð°Ð½Ð½Ñ SIM-карта."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Ваша SIM-карта адключана назаўÑёды.\n ЗвÑжыцеÑÑ Ð· аператарам беÑправадной ÑувÑзі, каб атрымаць іншую SIM-карту."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-карта заблакіравана."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-карта заблакіравана PUK-кодам."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Разблакіраванне SIM-карты…"</string> diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml index c554a274f4665e570ca9195edd4ab11ffe92f5cc..ab931ede75b04e2ffe635730e7a0fad7939d9da9 100644 --- a/packages/SystemUI/res-keyguard/values-bg/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда Ñе бавно"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зареждането е оптимизирано Ñ Ñ†ÐµÐ» запазване на батериÑта"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проблем ÑÑŠÑ Ð·Ð°Ñ€Ñдното уÑтройÑтво"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ÐатиÑнете „Меню“, за да отключите."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заключена"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ÐÑма SIM карта"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Добавете SIM карта."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM картата липÑва или е нечетлива. Добавете SIM карта."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ðеизползваема SIM карта."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM картата ви е деактивирана за поÑтоÑнно.\nЗа да получите друга, Ñе Ñвържете Ñ Ð´Ð¾Ñтавчика Ñи на безжична уÑлуга."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM картата е заключена."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM картата е заключена Ñ PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM картата Ñе отключва…"</string> diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml index 67b4e4bc322b875fb3f46d8af821654f9d304698..e25de9318f5dd69ad192a52ae5499db836136e90 100644 --- a/packages/SystemUI/res-keyguard/values-bn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ধীরে চারà§à¦œ হচà§à¦›à§‡"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • বà§à¦¯à¦¾à¦Ÿà¦¾à¦°à¦¿ à¦à¦¾à¦² রাখতে চারà§à¦œà¦¿à¦‚ অপà§à¦Ÿà¦¿à¦®à¦¾à¦‡à¦œ করা হয়েছে"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চারà§à¦œà¦¿à¦‚ অà§à¦¯à¦¾à¦•à§à¦¸à§‡à¦¸à¦°à¦¿à¦¤à§‡ সমসà§à¦¯à¦¾ রয়েছে"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"আনলক করতে মেনà§à¦¤à§‡ টিপà§à¦¨à¥¤"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটওয়ারà§à¦• লক করা আছে"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"কোনও সিম নেই"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"সিম যোগ করà§à¦¨à¥¤"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"সিম নেই অথবা সেটি রিড করা যাচà§à¦›à§‡ না। সিম যোগ করà§à¦¨à¥¤"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦¯à§‹à¦—à§à¦¯ নয় à¦à¦®à¦¨ সিম।"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"আপনার সিম সà§à¦¥à¦¾à¦¯à¦¼à§€à¦à¦¾à¦¬à§‡ বনà§à¦§ করে দেওয়া হয়েছে।\n অনà§à¦¯ à¦à¦•à¦Ÿà¦¿ সিমের জনà§à¦¯ আপনার ওয়à§à¦¯à¦¾à¦°à¦²à§‡à¦¸ পরিষেবা পà§à¦°à¦¦à¦¾à¦¨à¦•à¦¾à¦°à§€à¦° সাথে যোগাযোগ করà§à¦¨à¥¤"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"সিম লক করা হয়েছে।"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"সিম PUK লক করা হয়েছে।"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"সিম আনলক করা হচà§à¦›à§‡â€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml index 4c519c8d53628f20b1e194e233bc6531c04c793a..cd7aaeb8117a4b59981907d19d03b854c09b81b9 100644 --- a/packages/SystemUI/res-keyguard/values-bs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo punjenje"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje je optimizirano radi zaÅ¡tite baterije"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem s opremom za punjenje"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite meni da otkljuÄate."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zakljuÄana"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nema SIM-a"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Dodajte SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM nedostaje ili se ne može Äitati. Dodajte SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Neupotrebljiv SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM je trajno deaktiviran.\n Kontaktirajte pružaoca bežiÄnih usluga za drugi SIM"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM je zakljuÄan."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM je zakljuÄan PUK-om."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"OtkljuÄavanje SIM-a…"</string> diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml index 3bd65083d810cd7e7017df652815b71aa0d6e47c..bf8a592c27b9855e3b99bdce4e37f22032847188 100644 --- a/packages/SystemUI/res-keyguard/values-ca/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant lentament"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cà rrega optimitzada per protegir la bateria"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema relacionat amb l\'accessori de cà rrega"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Prem Menú per desbloquejar."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"La xarxa està bloquejada"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No hi ha cap SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Afegeix una SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Falta la SIM o no es pot llegir. Afegeix una SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"La SIM no es pot utilitzar."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"La SIM s\'ha desactivat permanentment.\n Contacta amb el proveïdor de serveis sense fil per obtenir-ne una altra."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"La SIM està bloquejada."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"La SIM està bloquejada pel PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"S\'està desbloquejant la targeta SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml index 573638bcc135242d35e5124c2a6c2595d22ba25a..bedafd8dd3b8fd07f72dd35e44ccd3f34c534b1c 100644 --- a/packages/SystemUI/res-keyguard/values-cs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pomalé nabÃjenÃ"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimalizované nabÃjenà za úÄelem ochrany baterie"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problém s nabÃjecÃm pÅ™ÃsluÅ¡enstvÃm"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Klávesy odemknete stisknutÃm tlaÄÃtka nabÃdky."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"SÃÅ¥ je blokována"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Žádná SIM karta"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"PÅ™idejte SIM kartu."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM karta chybà nebo je neÄitelná. PÅ™idejte SIM kartu."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM kartu nelze použÃt."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM karta byla natrvalo deaktivována.\n Požádejte svého poskytovatele bezdrátových služeb o dalÅ¡Ã SIM kartu."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM karta je zablokována."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM karta je blokována pomocà kódu PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Odblokovánà SIM karty…"</string> diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml index c7c863b70336cb92e38f45084511cc6296cf90fa..93f505e8bbf295af08dc6da2281e61418c6b4dc2 100644 --- a/packages/SystemUI/res-keyguard/values-da/strings.xml +++ b/packages/SystemUI/res-keyguard/values-da/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader langsomt"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladning er optimeret for at beskytte batteriet"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem med opladertilbehør"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tryk pÃ¥ menuen for at lÃ¥se op."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netværket er lÃ¥st"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Intet SIM-kort"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Tilføj et SIM-kort."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-kortet mangler eller kan ikke læses. Tilføj et SIM-kort."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Deaktiveret SIM-kort."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Dit SIM-kort er permanent deaktiveret.\n Kontakt din tjenesteudbyder for at fÃ¥ et nyt SIM-kort."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-kortet er lÃ¥st."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-kortet er lÃ¥st med PUK-koden."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-kortet lÃ¥ses op…"</string> diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml index 5c5f264fe508f5132b936281c725217802fffb70..01e166e36559600eaaab7479ad82ce7a06dc08c1 100644 --- a/packages/SystemUI/res-keyguard/values-de/strings.xml +++ b/packages/SystemUI/res-keyguard/values-de/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird langsam geladen"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimiertes Laden zur Akkuschonung"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem mit dem Ladezubehör"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Zum Entsperren die Menütaste drücken."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netzwerk gesperrt"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Keine SIM-Karte"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Lege eine SIM-Karte ein."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-Karte fehlt oder ist nicht lesbar. Lege eine SIM-Karte ein."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-Karte ist nicht nutzbar."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Deine SIM-Karte wurde dauerhaft deaktiviert.\n Wende dich an deinen Mobilfunkanbieter, um eine andere SIM-Karte zu erhalten."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-Karte ist gesperrt."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-Karte ist mit einem PUK gesperrt."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-Karte wird entsperrt…"</string> diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml index 3a01da53dfce03e55b92f95c29da0251a2739d1d..97692424cf2cc4b68132c21317d4b36b3bc7a11b 100644 --- a/packages/SystemUI/res-keyguard/values-el/strings.xml +++ b/packages/SystemUI/res-keyguard/values-el/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ΑÏγή φόÏτιση"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Η φόÏτιση βελτιστοποιήθηκε για την Ï€Ïοστασία της μπαταÏίας"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Î Ïόβλημα Î±Î¾ÎµÏƒÎ¿Ï…Î¬Ï Ï†ÏŒÏτισης"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Πατήστε \"ΜενοÏ\" για ξεκλείδωμα."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ΚλειδωμÎνο δίκτυο"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Δεν υπάÏχει SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Î ÏοσθÎστε μια SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Η SIM λείπει ή δεν είναι δυνατή η ανάγνωσή της. Î ÏοσθÎστε μια SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Η SIM δεν μποÏεί να χÏησιμοποιηθεί."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Η SIM απενεÏγοποιήθηκε οÏιστικά.\n Επικοινωνήστε με τον πάÏοχο υπηÏεσιών ασÏÏματου δικτÏου για μια νÎα SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Η SIM είναι κλειδωμÎνη."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Η SIM Îχει κλειδωθεί με κωδικό PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Ξεκλείδωμα SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml index a4b13487b3e1d85bc2e2591d0009e531f8f58fc7..087ab3a793584fd83a77d85f2dcba7d6c3cb9e7d 100644 --- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging optimised to protect battery"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Issue with charging accessory"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml index 480bcbb61954044d8c186a6dd7dae530565da746..7297cf929e2fd49fc05f78198e8557069ab28d67 100644 --- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging optimized to protect battery"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Issue with charging accessory"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml index a4b13487b3e1d85bc2e2591d0009e531f8f58fc7..087ab3a793584fd83a77d85f2dcba7d6c3cb9e7d 100644 --- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging optimised to protect battery"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Issue with charging accessory"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml index a4b13487b3e1d85bc2e2591d0009e531f8f58fc7..087ab3a793584fd83a77d85f2dcba7d6c3cb9e7d 100644 --- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging optimised to protect battery"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Issue with charging accessory"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Add a SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"The SIM is missing or not readable. Add a SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Unusable SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Your SIM has been permanently deactivated.\n Contact your wireless service provider for another SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM is locked."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM is PUK-locked."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Unlocking SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml index b8e89f4e334f72f91c2da2c0808aa43f1535880d..ead8bce626b82db0fa79bd1bbce8928f2e79fe80 100644 --- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€â€â€Žâ€Žâ€Žâ€â€Žâ€â€â€â€â€â€Žâ€â€Žâ€Žâ€â€â€Žâ€â€Žâ€â€â€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€â€â€â€â€Žâ€â€â€â€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€â€Ž<xliff:g id="PERCENTAGE">%s</xliff:g>‎â€â€Žâ€Žâ€â€â€â€Ž • Charging slowly‎â€â€Žâ€Žâ€â€Ž"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€Žâ€Žâ€â€â€â€Žâ€â€Žâ€Žâ€â€â€â€Žâ€â€Žâ€â€â€â€â€Žâ€Žâ€Žâ€â€Žâ€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€â€Žâ€Žâ€Žâ€â€Žâ€Žâ€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€â€Ž<xliff:g id="PERCENTAGE">%s</xliff:g>‎â€â€Žâ€Žâ€â€â€â€Ž • Charging optimized to protect battery‎â€â€Žâ€Žâ€â€Ž"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€â€Žâ€â€â€Žâ€Žâ€â€â€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€â€Žâ€â€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€â€â€â€Žâ€Žâ€â€Žâ€Žâ€â€â€â€â€Žâ€Žâ€â€Žâ€â€â€Žâ€Žâ€Žâ€â€Žâ€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€â€Ž<xliff:g id="PERCENTAGE">%s</xliff:g>‎â€â€Žâ€Žâ€â€â€â€Ž • Issue with charging accessory‎â€â€Žâ€Žâ€â€Ž"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€â€â€â€â€â€Žâ€â€Žâ€â€Žâ€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€â€Žâ€â€Žâ€Žâ€Žâ€â€â€Žâ€â€Žâ€â€â€Žâ€â€â€Žâ€Žâ€Žâ€â€â€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€â€â€â€Žâ€â€Žâ€ŽPress Menu to unlock.‎â€â€Žâ€Žâ€â€Ž"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€Žâ€â€â€â€Žâ€â€â€Žâ€â€Žâ€Žâ€â€â€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€â€â€â€â€â€Žâ€â€Žâ€â€â€â€â€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€â€â€Žâ€Žâ€â€Žâ€â€â€Žâ€Žâ€â€Žâ€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€ŽNetwork locked‎â€â€Žâ€Žâ€â€Ž"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€Žâ€Žâ€â€Žâ€Žâ€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€â€Žâ€â€â€Žâ€â€â€Žâ€â€â€Žâ€Žâ€Žâ€â€â€â€Žâ€â€â€â€â€Žâ€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€â€â€â€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€â€Žâ€Žâ€Žâ€ŽNo SIM‎â€â€Žâ€Žâ€â€Ž"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€â€â€â€â€Žâ€â€Žâ€â€â€Žâ€â€Žâ€â€â€Žâ€Žâ€â€â€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€â€Žâ€â€Žâ€â€â€â€â€â€â€â€Žâ€Žâ€â€â€â€Žâ€Žâ€â€Žâ€â€Žâ€â€â€â€â€â€Žâ€ŽAdd a SIM.‎â€â€Žâ€Žâ€â€Ž"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€â€Žâ€â€Žâ€â€â€â€â€â€â€â€Žâ€Žâ€â€â€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€â€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€â€â€â€Žâ€â€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€â€â€Žâ€â€Žâ€Žâ€â€â€â€Žâ€Žâ€ŽThe SIM is missing or not readable. Add a SIM.‎â€â€Žâ€Žâ€â€Ž"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€â€Žâ€â€â€Žâ€â€â€Žâ€â€â€â€Žâ€Žâ€Žâ€â€â€Žâ€Žâ€â€Žâ€â€Žâ€â€â€Žâ€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€â€Žâ€Žâ€â€â€Žâ€Žâ€â€Žâ€â€Žâ€â€â€Žâ€Žâ€â€Žâ€â€â€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€Žâ€Žâ€ŽUnusable SIM.‎â€â€Žâ€Žâ€â€Ž"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€â€â€â€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€â€â€â€â€Žâ€â€Žâ€Žâ€â€â€â€Žâ€â€â€â€â€Žâ€Žâ€â€Žâ€Žâ€â€â€â€Žâ€â€â€â€Žâ€Žâ€Žâ€â€Žâ€â€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€â€â€Žâ€â€Žâ€Žâ€ŽYour SIM has been permanently deactivated.‎â€â€Žâ€Žâ€â€â€Ž\n‎â€â€Žâ€Žâ€â€â€â€Ž Contact your wireless service provider for another SIM.‎â€â€Žâ€Žâ€â€Ž"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€â€â€â€â€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€â€â€Žâ€â€â€â€â€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€â€â€â€â€â€Žâ€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€â€â€Žâ€â€Žâ€â€Žâ€â€â€â€Žâ€â€Žâ€â€â€â€â€â€Žâ€Žâ€â€â€Žâ€ŽSIM is locked.‎â€â€Žâ€Žâ€â€Ž"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€â€Žâ€â€Žâ€Žâ€Žâ€â€Žâ€â€Žâ€â€â€â€â€Žâ€â€â€â€â€â€Žâ€â€â€Žâ€â€â€Žâ€Žâ€â€Žâ€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€â€â€â€â€â€Žâ€Žâ€â€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€Žâ€ŽSIM is PUK-locked.‎â€â€Žâ€Žâ€â€Ž"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"‎â€â€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€â€â€â€Žâ€Žâ€Žâ€Žâ€Žâ€Žâ€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€â€â€â€â€â€â€â€â€Žâ€â€Žâ€â€â€â€Žâ€Žâ€â€â€â€â€Žâ€â€Žâ€â€Žâ€Žâ€Žâ€Žâ€â€â€Žâ€Žâ€â€Žâ€Žâ€Žâ€â€â€Žâ€Žâ€â€â€â€â€Žâ€Žâ€Žâ€â€Žâ€â€Žâ€â€Žâ€Žâ€â€â€â€â€â€Žâ€â€â€â€Žâ€â€Žâ€â€â€ŽUnlocking SIM…‎â€â€Žâ€Žâ€â€Ž"</string> diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml index debbeb127c2f2916b5ca18820b7ba304b566bc51..5b82c443ff6250d54621c61927807b8463a40aec 100644 --- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carga optimizada para proteger la baterÃa"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema con el accesorio de carga"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Presiona Menú para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada para la red"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No hay ninguna tarjeta SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Introduce una tarjeta SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Falta la tarjeta SIM o no se puede leer. Introduce una tarjeta SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Tarjeta SIM inutilizable."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Tu tarjeta SIM se desactivó permanentemente.\n Ponte en contacto con tu proveedor de servicios inalámbricos para obtener otra tarjeta SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"La tarjeta SIM está bloqueada."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"La tarjeta SIM está bloqueada con el código PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Desbloqueando tarjeta SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml index 0ea98a8be752f01d66d21de3b51f90e25559a9cc..cf7f3d220443ec63bb814f141c368fc8640c89b1 100644 --- a/packages/SystemUI/res-keyguard/values-es/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carga optimizada para proteger la baterÃa"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema con el accesorio de carga"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pulsa el menú para desbloquear la pantalla."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada para la red"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"No hay ninguna SIM."</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Añade una SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Falta la SIM o no se puede leer. Añade una SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"No se puede usar la SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Tu SIM se ha desactivado de forma permanente.\n Para obtener otra SIM, ponte en contacto con tu proveedor de servicios inalámbricos."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"La SIM está bloqueada."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"La SIM está bloqueada con el código PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Desbloqueando SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml index 722a02237b3d1e17a2d0cbbc051462e5dc8c16c3..6335ca84c75dfbba5cde3b461f113445f340dcc3 100644 --- a/packages/SystemUI/res-keyguard/values-et/strings.xml +++ b/packages/SystemUI/res-keyguard/values-et/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Aeglane laadimine"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laadimine on aku kaitsmiseks optimeeritud"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • probleem laadimistarvikuga"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Vajutage avamiseks menüüklahvi."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Võrk on lukus"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM-i pole"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Lisage SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM puudub või pole loetav. Lisage SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-i ei saa kasutada."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Teie SIM on jäädavalt inaktiveeritud.\n Teise SIM-i saamiseks võtke ühendust oma traadita side teenusepakkujaga."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM on lukustatud."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM on PUK-koodiga lukustatud."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-i avamine …"</string> diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml index d3293696ec58efc021c6fb86a68de22825bfad64..b47c58a770aa45c72ae714e52b946dd839aa15f4 100644 --- a/packages/SystemUI/res-keyguard/values-eu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mantso kargatzen"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bateria ez kaltetzeko, kargatzeko modua optimizatu da"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Arazo bat dago kargatzeko osagarriarekin"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Desblokeatzeko, sakatu Menua."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sarea blokeatuta dago"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ez dago SIMik"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Gehitu SIM bat."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIMa falta da, edo ezin da irakurri. Gehitu SIM bat."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ezin da erabili SIMa."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Betiko desaktibatu da SIMa.\n Jarri operadorearekin harremanetan beste SIM bat eskuratzeko."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIMa blokeatuta dago."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIMa PUKaren bidez desblokeatu behar da."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIMa desblokeatzen…"</string> diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml index ae3f04a290e4348bdcaa8fbbb5353645af5bd4ae..f274f5fd7a5755047453097edc047f1640010ee7 100644 --- a/packages/SystemUI/res-keyguard/values-fa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آهسته‌آهسته شارژ می‌شود"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • برای Ù…ØاÙظت از باتری، شارژ بهینه می‌شود"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • در شارژ وسیله جانبی مشکلی وجود دارد"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"برای باز کردن Ù‚ÙÙ„ روی «منو» Ùشار دهید."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"شبکه Ù‚ÙÙ„ شد"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"سیم‌کارتی وجود ندارد"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"سیم‌کارت اضاÙÙ‡ کنید."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"سیم‌کارت موجود نیست یا قابل‌خواندن نیست. سیم‌کارت اضاÙÙ‡ کنید."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"سیم‌کارت قابل‌استÙاده نیست."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"سیم‌کارت شما برای همیشه غیرÙعال شده است.\n برای دریاÙت سیم‌کارتی دیگر، با رساننده خدمات بی‌سیم خود تماس بگیرید."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"سیم‌کارت Ù‚ÙÙ„ است."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"â€Ø³ÛŒÙ…‌کارت با کد PUK Ù‚ÙÙ„ شده است."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"درØال باز کردن Ù‚ÙÙ„ سیم‌کارت…"</string> diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml index 050df9983725d890997b1fdeabacb98c50dd2f19..dd9ce2e4453f39e1b76ac9a59236c01b49241606 100644 --- a/packages/SystemUI/res-keyguard/values-fi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan hitaasti"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lataus optimoitu akun suojaamiseksi"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ongelma laturin kanssa"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Poista lukitus painamalla Valikkoa."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Verkko lukittu"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ei SIM-korttia"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Lisää SIM-kortti."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-korttia ei löydy tai ei voi lukea. Lisää SIM-kortti."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-korttia ei voi käyttää."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Sim-kortti on poistettu käytöstä pysyvästi.\n Ota yhteyttä langattoman palvelun tarjoajaan ja pyydä uusi SIM-kortti."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-kortti on lukittu."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-kortti on lukittu PUK-koodilla."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-kortin lukitusta avataan…"</string> diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml index fa1a191b8944c4226903592e07114cd1026347fc..742f56e7f214855cb0606df61183669e69366f8e 100644 --- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"En recharge lente : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge optimisée pour protéger la pile"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problème concernant l\'accessoire de recharge"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur la touche Menu pour déverrouiller l\'appareil."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Aucune carte SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Ajouter une carte SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"La carte SIM est manquante ou illisible. Ajouter une carte SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"La carte SIM est inutilisable."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Votre carte SIM a été désactivée de manière permanente.\n Communiquez avec votre fournisseur de services sans fil pour obtenir une autre carte SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"La carte SIM est verrouillée."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"La carte SIM est verrouillée par clé PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Déverrouillage de la carte SIM en cours…"</string> diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml index d687a1d7e9a24ff54c0cc5b6eae609851a29afe6..92d24c4bbc2819e4d266768fc6de9ca0d516b74b 100644 --- a/packages/SystemUI/res-keyguard/values-fr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge lente"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge optimisée pour protéger la batterie"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problème de recharge de l\'accessoire"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur \"Menu\" pour déverrouiller le clavier."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Aucune SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Ajoutez une SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"La SIM est absente ou illisible. Ajoutez une SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM inutilisable."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Votre SIM a été désactivée définitivement.\n Contactez votre opérateur de téléphonie mobile pour en obtenir une autre."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM verrouillée."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM verrouillée par clé PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Déblocage de la SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml index 68f22cb262f7cd82fdc1e7fc04e6d5414540a08a..4837de2139ac598610570b795e43843e8d2fa462 100644 --- a/packages/SystemUI/res-keyguard/values-gl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carga optimizada para protexer a baterÃa"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema co accesorio de carga"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Preme Menú para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada pola rede"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Non hai ningunha SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Engade unha SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"A SIM falta ou non se pode ler. Engade unha."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"A SIM non se pode usar."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"A SIM desactivouse permanentemente.\n Ponte en contacto co teu fornecedor de servizos sen fÃos para conseguir outra."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"A SIM está bloqueada."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"A SIM está bloqueada mediante PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Desbloqueando SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml index 99c98836325660fea7b79075bc15880e8138a7e8..7f8c6d824f934f2058f7ab549286d66d56cbee65 100644 --- a/packages/SystemUI/res-keyguard/values-gu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ધીમેથી ચારà«àªœàª¿àª‚ગ"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • બૅટરીની સà«àª°àª•à«àª·àª¾ કરવા માટે, ચારà«àªœàª¿àª‚ગ ઑપà«àªŸàª¿àª®àª¾àª‡àª કરવામાં આવà«àª¯à«àª‚ છે"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચારà«àªœàª¿àª‚ગ àªàª•à«àª¸à«‡àª¸àª°à«€àª®àª¾àª‚ સમસà«àª¯àª¾"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"અનલૉક કરવા માટે મેનૂ દબાવો."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"નેટવરà«àª• લૉક થયà«àª‚"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"કોઈ સિમ કારà«àª¡ નથી"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"સિમ કારà«àª¡ ઉમેરો."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"સિમ કારà«àª¡ ખૂટે છે અથવા વાંચી શકાય àªàªµà«àª‚ નથી. સિમ કારà«àª¡ ઉમેરો."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ઉપયોગમાં ન લઈ શકાતà«àª‚ સિમ કારà«àª¡."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"તમારà«àª‚ સિમ કારà«àª¡ કાયમ માટે નિષà«àª•à«àª°àª¿àª¯ કરવામાં આવà«àª¯à«àª‚ છે.\n બીજા સિમ કારà«àª¡ માટે તમારા વાયરલેસ સેવા પà«àª°àª¦àª¾àª¤àª¾àª¨à«‹ સંપરà«àª• કરો."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"સિમ કારà«àª¡ લૉક કરેલà«àª‚ છે."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"સિમ કારà«àª¡ PUK-લૉક કરેલà«àª‚ છે."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"સિમ કારà«àª¡ અનલૉક કરી રહà«àª¯àª¾àª‚ છીàªâ€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml index 9d32f04206dc8395e34174052f0d79daf758e74b..18d63ab35e9444a265b0a71618464f104421126c 100644 --- a/packages/SystemUI/res-keyguard/values-hi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • धीरे चारà¥à¤œ हो रहा है"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • बैटरी को नà¥à¤•à¤¸à¤¾à¤¨ से बचाने के लिà¤, चारà¥à¤œà¤¿à¤‚ग को ऑपà¥à¤Ÿà¤¿à¤®à¤¾à¤‡à¤œà¤¼ किया गया"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चारà¥à¤œà¤° à¤à¤•à¥à¤¸à¥‡à¤¸à¤°à¥€ से जà¥à¤¡à¤¼à¥€ समसà¥à¤¯à¤¾"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"लॉक खोलने के लिठमेनà¥à¤¯à¥‚ दबाà¤à¤‚."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवरà¥à¤• लॉक किया हà¥à¤† है"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"कोई सिम नहीं है"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"कोई सिम जोड़ें."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"सिम मौजूद नहीं है या उसे à¤à¤•à¥à¤¸à¥‡à¤¸ नहीं किया जा सकता. कोई सिम जोड़ें."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"सिम को हमेशा के लिठबंद कर दिया गया है."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"आपका सिम हमेशा के लिठबंद कर दिया गया है.\n दूसरा सिम पाने के लिà¤, वायरलेस सेवा देने वाली कंपनी से संपरà¥à¤• करें."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"सिम लॉक है."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"सिम में PUK लॉक लगा है."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"सिम अनलॉक हो रहा है…"</string> diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml index b4224bfbc497768c3b38e0a5ea10450536b2a459..0206faf7f2ad5d0a8b4abe5891a1100ecae1de0d 100644 --- a/packages/SystemUI/res-keyguard/values-hr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • sporo punjenje"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje se optimizira radi zaÅ¡tite baterije"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem s priborom za punjenje"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Izbornik da biste otkljuÄali."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zakljuÄana"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nema SIM-a"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Dodajte SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM nedostaje ili nije Äitljiv. Dodajte SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM je neupotrebljiv."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"VaÅ¡ je SIM trajno deaktiviran.\n Obratite se svom davatelju bežiÄnih usluga da biste dobili drugi SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM je zakljuÄan."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM je zakljuÄan PUK-om."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"OtkljuÄavanje SIM-a…"</string> diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml index bc712c7cc882a5bcdaa31c1e89eb068355ab4e96..8575e10884ceb68178940c969848a871b4568ac2 100644 --- a/packages/SystemUI/res-keyguard/values-hu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lassú töltés"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimalizált töltés az akkumulátor védelme érdekében"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Probléma van a töltÅ‘tartozékkal"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"A feloldáshoz nyomja meg a Menü gombot."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Hálózat zárolva"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nincs SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Adjon hozzá egy SIM-et."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"A SIM hiányzik vagy nem olvasható. Adjon hozzá egy SIM-et."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nem használható SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM véglegesen deaktiválva.\n Forduljon a vezeték nélküli szolgáltatójához másik SIM beszerzése érdekében."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Zárolt SIM."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"A SIM le van zárva PUK-kóddal."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM zárolásának feloldása…"</string> diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml index 4d7bbbef2a084d3aee95cbbf1aab165d62dc42a6..a7c3abab1d842639a878fe192cce340705c6b39d 100644 --- a/packages/SystemUI/res-keyguard/values-hy/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ô´Õ¡Õ¶Õ¤Õ¡Õ² Õ¬Õ«ÖÖ„Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Õ„Õ¡Ö€Õ¿Õ¯Õ¸ÖÕ¨ ÕºÕ¡Õ·Õ¿ÕºÕ¡Õ¶Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ¬Õ«ÖÖ„Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶ Ö…ÕºÕ¿Õ«Õ´Õ¡Õ¬Õ¡ÖÕ¾Õ¥Õ¬ Õ§"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ô¼Õ«ÖÖ„Õ¡Õ¾Õ¸Ö€Õ«Õ¹Õ« Õ°Õ¥Õ¿ Õ¯Õ¡ÕºÕ¾Õ¡Õ® ÕÕ¶Õ¤Õ«Ö€"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ô±ÕºÕ¡Õ¯Õ¸Õ²ÕºÕ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ½Õ¥Õ²Õ´Õ¥Ö„ Ô¸Õ¶Õ¿Ö€Õ¡ÖÕ¡Õ¶Õ¯Õ¨:"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Õ‘Õ¡Õ¶ÖÕ¨ Õ¯Õ¸Õ²ÕºÕ¾Õ¡Õ® Õ§"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM Ö„Õ¡Ö€Õ¿ Õ¹Õ¯Õ¡"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Ô±Õ¾Õ¥Õ¬Õ¡ÖÖ€Õ¥Ö„ SIM Ö„Õ¡Ö€Õ¿Ö‰"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM Ö„Õ¡Ö€Õ¿Õ¨ Õ¢Õ¡ÖÕ¡Õ¯Õ¡ÕµÕ¸Ö‚Õ´ Õ§ Õ¯Õ¡Õ´ Õ¨Õ¶Õ©Õ¥Õ¼Õ¶Õ¥Õ¬Õ« Õ¹Õ§Ö‰ Ô±Õ¾Õ¥Õ¬Õ¡ÖÖ€Õ¥Ö„ SIM Ö„Õ¡Ö€Õ¿Ö‰"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ô±Õ¶Õ¾Õ¡Õ¾Õ¥Ö€ SIM Ö„Õ¡Ö€Õ¿Ö‰"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ÕÕ¥Ö€ SIM Ö„Õ¡Ö€Õ¿Õ¶ Õ¨Õ¶Õ¤Õ´Õ«Õ·Õ¿ Õ¡ÕºÕ¡Õ¯Õ¿Õ«Õ¾Õ¡ÖÕ¾Õ¥Õ¬ Õ§Ö‰\n Õ†Õ¸Ö€ SIM Ö„Õ¡Ö€Õ¿ Õ±Õ¥Õ¼Ö„ Õ¢Õ¥Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€ Õ¯Õ¡ÕºÕ¾Õ¥Ö„ Õ±Õ¥Ö€ Õ¢Õ»Õ»Õ¡ÕµÕ«Õ¶ Ö…ÕºÕ¥Ö€Õ¡Õ¿Õ¸Ö€Õ« Õ°Õ¥Õ¿Ö‰"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM Ö„Õ¡Ö€Õ¿Õ¨ Õ¯Õ¸Õ²ÕºÕ¾Õ¡Õ® Õ§Ö‰"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM Ö„Õ¡Ö€Õ¿Õ¨ Õ¯Õ¸Õ²ÕºÕ¾Õ¡Õ® Õ§ PUK Õ¯Õ¸Õ¤Õ¸Õ¾Ö‰"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM Ö„Õ¡Ö€Õ¿Õ¶ Õ¡ÕºÕ¡Õ¯Õ¸Õ²ÕºÕ¾Õ¸Ö‚Õ´ է…"</string> diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml index aa766e963670d1b0b0171a76d1d3ddb1171f40bd..f9a840fc5aa897d0e515f96657d94c74f422bc3b 100644 --- a/packages/SystemUI/res-keyguard/values-in/strings.xml +++ b/packages/SystemUI/res-keyguard/values-in/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan lambat"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengisian daya dioptimalkan untuk melindungi baterai"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Masalah dengan aksesori pengisian daya"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Jaringan terkunci"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Tidak ada SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Tambahkan SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM tidak ada atau tidak dapat dibaca. Tambahkan SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM tidak dapat digunakan."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM Anda telah dinonaktifkan secara permanen.\n Hubungi penyedia layanan nirkabel Anda untuk mendapatkan SIM lain."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM dikunci."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM dikunci PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Membuka kunci SIM …"</string> diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml index 99f177939378d55de0007b46b7b005ba5e0dadb1..b7147c2019190203c687a2217837a1e583347bd0 100644 --- a/packages/SystemUI/res-keyguard/values-is/strings.xml +++ b/packages/SystemUI/res-keyguard/values-is/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hæg hleðsla"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hleðsla fÃnstillt til að vernda rafhlöðuna"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Vandamál með hleðslubúnað"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ãttu á valmyndarhnappinn til að taka úr lás."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Net læst"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ekkert SIM-kort"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Bæta við SIM-korti."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-kort vantar eða er ekki læsilegt. Bæta við SIM-korti."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ónothæft SIM-kort."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM-kortið þitt var gert varanlega óvirkt.\n Hafðu samband við sÃmafyrirtækið þitt til að fá nýtt SIM-kort."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-kort er læst."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-kort er læst með PUK-númeri."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Opnar SIM-kort…"</string> diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml index cc0a1648054755c3c853ffc3d2d152b8cd7f3dcc..9e1b18740750a61b994f970157db703b0dcb5a6b 100644 --- a/packages/SystemUI/res-keyguard/values-it/strings.xml +++ b/packages/SystemUI/res-keyguard/values-it/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica lenta"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica ottimizzata per proteggere la batteria"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema relativo all\'accessorio di ricarica"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Premi Menu per sbloccare."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rete bloccata"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nessuna SIM presente"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Aggiungi una SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM mancante o non leggibile. Aggiungi una SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM inutilizzabile."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"La SIM è stata disattivata definitivamente.\n Contatta il tuo fornitore di servizi wireless per richiedere un\'altra SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"La SIM è bloccata."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"La SIM è bloccata tramite PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Sblocco della SIM in corso…"</string> diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml index 00c717c3d3c2a9eb5799b3641ef63efbaa919520..16316cebcc7c04e555c6185df5c16d1f30c5de84 100644 --- a/packages/SystemUI/res-keyguard/values-iw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ×‘×˜×¢×™× ×” ×יטית"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ×”×˜×¢×™× ×” עברה ×ופטימיזציה כדי להגן על הסוללה"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • יש בעיה ×¢× ×ביזר ×”×˜×¢×™× ×”"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"יש ללחוץ על \'תפריט\' כדי לבטל ×ת ×”× ×¢×™×œ×”."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"הרשת × ×¢×•×œ×”"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"â€×ין כרטיס SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"â€×”וספת כרטיס SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"â€×›×¨×˜×™×¡ ×”-SIM חסר ×ו ×©×œ× × ×™×ª×Ÿ ×œ×§×¨×•× ×ותו. הוספת כרטיס SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"â€×œ× × ×™×ª×Ÿ להשתמש בכרטיס ×”-SIM ×”×–×”."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"â€×›×¨×˜×™×¡ ×”-SIM שלך הושבת ב×ופן סופי.\n עליך ×œ×¤× ×•×ª לספק השירות ×”×לחוטי שלך לקבלת כרטיס SIM ×חר."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"â€×›×¨×˜×™×¡ ×”-SIM × ×¢×•×œ."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"â€×›×¨×˜×™×¡ ×”-SIM × ×¢×•×œ ב×מצעות PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"â€×ž×ª×‘צע ביטול × ×¢×™×œ×” של כרטיס ×”-SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml index 1d59a630b8c21e42ced073a33d6eb9b769223ebf..6e8f42369e0a856e1be93ad2de02ef1d0ed512dc 100644 --- a/packages/SystemUI/res-keyguard/values-ja/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 低速充電ä¸"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ãƒãƒƒãƒ†ãƒªãƒ¼ã‚’ä¿è·ã™ã‚‹ãŸã‚ã«ã€å……é›»ãŒæœ€é©åŒ–ã•ã‚Œã¦ã„ã¾ã™"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電用アクセサリã«é–¢ã™ã‚‹å•é¡Œ"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"メニューã‹ã‚‰ãƒãƒƒã‚¯ã‚’解除ã§ãã¾ã™ã€‚"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãŒãƒãƒƒã‚¯ã•ã‚Œã¾ã—ãŸ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM ãŒã‚ã‚Šã¾ã›ã‚“"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM ã‚’è¿½åŠ ã—ã¦ãã ã•ã„。"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM ãŒè¦‹ã¤ã‹ã‚‰ãªã„ã‹èªã¿å–ã‚Œã¾ã›ã‚“。SIM ã‚’è¿½åŠ ã—ã¦ãã ã•ã„。"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM ãŒä½¿ç”¨ã§ãã¾ã›ã‚“。"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM ãŒå®Œå…¨ã«ç„¡åŠ¹ã«ãªã£ã¦ã„ã¾ã™ã€‚\n ワイヤレス サービス プãƒãƒã‚¤ãƒ€ã«ãŠå•ã„åˆã‚ã›ã®ã†ãˆã€æ–°ã—ã„ SIM を入手ã—ã¦ãã ã•ã„。"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM ãŒãƒãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM ㌠PUK ã§ãƒãƒƒã‚¯ã•ã‚Œã¾ã—ãŸã€‚"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM ãƒãƒƒã‚¯ã‚’解除ã—ã¦ã„ã¾ã™â€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml index 5bd6b2e3d883b492c6da1151a52730a897fd1abe..a31243d9f5f9a669a99c87856baa407d26f031cb 100644 --- a/packages/SystemUI/res-keyguard/values-ka/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ნელრიტენებáƒ"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • დáƒáƒ¢áƒ”ნვრáƒáƒžáƒ¢áƒ˜áƒ›áƒ˜áƒ–ირებულირბáƒáƒ¢áƒáƒ ეის დáƒáƒ¡áƒáƒªáƒáƒ•áƒáƒ“"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • დáƒáƒ›áƒ¢áƒ”ნი დáƒáƒ›áƒ®áƒ›áƒáƒ ე მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ის პრáƒáƒ‘ლემáƒ"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"გáƒáƒœáƒ¡áƒáƒ‘ლáƒáƒ™áƒáƒ“ დáƒáƒáƒáƒ˜áƒ ეთ მენიუს."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ქსელი ჩáƒáƒ™áƒ”ტილიáƒ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM áƒáƒ áƒáƒ ის"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM-ის დáƒáƒ›áƒáƒ¢áƒ”ბáƒ."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM áƒáƒ™áƒšáƒ˜áƒ áƒáƒœ áƒáƒ იკითხებáƒ. SIM-ის დáƒáƒ›áƒáƒ¢áƒ”ბáƒ."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"გáƒáƒ›áƒáƒ£áƒ§áƒ”ნებელი SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"თქვენი SIM სáƒáƒ›áƒ£áƒ“áƒáƒ›áƒáƒ“ გáƒáƒ›áƒáƒ თულიáƒ.\n დáƒáƒ£áƒ™áƒáƒ•áƒ¨áƒ˜áƒ დით თქვენს უკáƒáƒ‘ელრსერვისის პრáƒáƒ•áƒáƒ˜áƒ“ერს სხვრSIM ბáƒáƒ áƒáƒ—ისთვის."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-ბáƒáƒ áƒáƒ—ი ჩáƒáƒ™áƒ”ტილიáƒ."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM დáƒáƒ‘ლáƒáƒ™áƒ˜áƒšáƒ˜áƒ PUK-ით."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-ის გáƒáƒœáƒ‘ლáƒáƒ™áƒ•áƒâ€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml index 83d270ded60c5d2ed2a4e0b20cd2e224523bd08b..6a777832d07dd708eea9575a4ddd9dd20a55deee 100644 --- a/packages/SystemUI/res-keyguard/values-kk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • БаÑу зарÑдталуда"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • БатареÑны қорғау үшін зарÑдтау оңтайландырылды"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ЗарÑдтау құрылғыÑына қатыÑÑ‚Ñ‹ мәÑеле туындады."</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ðшу үшін \"Мәзір\" пернеÑін баÑыңыз."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Желі құлыптаулы"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM картаÑÑ‹ жоқ."</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM картаÑын қоÑыңыз."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM картаÑÑ‹ жоқ немеÑе оқылмай тұр. SIM картаÑын қоÑыңыз."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM картаÑын пайдалану мүмкін емеÑ."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM картаңыз біржола өшірілді.\n СымÑыз Ð±Ð°Ð¹Ð»Ð°Ð½Ñ‹Ñ Ð¿Ñ€Ð¾Ð²Ð°Ð¹Ð´ÐµÑ€Ñ–Ð½Ðµ хабарлаÑып, баÑқа SIM картаÑын алыңыз."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM картаÑÑ‹ құлыпталған."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM картаÑÑ‹ PUK кодымен құлыпталды."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM картаÑының құлпы ашылып жатыр…"</string> diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml index 5306cb1ff4e6c1bb75986fe360b73cea6e093525..cda9520862aa48d05d5bb55008cae50d320e20c3 100644 --- a/packages/SystemUI/res-keyguard/values-km/strings.xml +++ b/packages/SystemUI/res-keyguard/values-km/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកážáŸ’មយឺáž"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • បានបង្កើនប្រសិទ្ធភាពនៃការសាក ដើម្បីការពារážáŸ’ម"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • បញ្ហាពាក់ពáŸáž“្ធនឹងគ្រឿងសាកážáŸ’ម"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ចុចម៉ឺនុយ ​ដើម្បី​ដោះ​សោ។"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"បណ្ដាញ​ជាប់​សោ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"គ្មានស៊ីមទáŸ"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"បញ្ចូល​ស៊ីម។"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"បាážáŸ‹ážŸáŸŠáž¸áž˜ ឬមិនអាចអានស៊ីមបាន។ បញ្ចូល​ស៊ីម។"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ស៊ីមមិនអាចប្រើបាន។"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ស៊ីមរបស់អ្នកážáŸ’រូវបានបិទដំណើរការជាអចិន្ážáŸ’រៃយáŸáŸ”\n ទាក់ទងទៅក្រុមហ៊ុនផ្ដល់សáŸážœáž¶áž¥ážážáŸ’សែរបស់អ្នក ដើម្បីទទួលបានស៊ីមមួយទៀážáŸ”"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ស៊ីម​ážáŸ’រូវបាន​ចាក់សោ។"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ស៊ីមážáŸ’រូវបានចាក់សោដោយ PUK។"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"កំពុងដោះសោស៊ីម…"</string> diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml index d609a23d2c87416ae22004645bc7cf47a51957e8..e24005a08fc90d696d3a55795cd3745a3091a2bb 100644 --- a/packages/SystemUI/res-keyguard/values-kn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರà³à²œà³ ಮಾಡಲಾಗà³à²¤à³à²¤à²¿à²¦à³†"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಬà³à²¯à²¾à²Ÿà²°à²¿à²¯à²¨à³à²¨à³ ರಕà³à²·à²¿à²¸à²²à³ ಚಾರà³à²œà²¿à²‚ಗೠಅನà³à²¨à³ ಆಪà³à²Ÿà²¿à²®à³ˆà²¸à³ ಮಾಡಲಾಗಿದೆ"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರà³à²œà²¿à²‚ಗೠಪರಿಕರ ಕà³à²°à²¿à²¤à³ ಸಮಸà³à²¯à³† ಇದೆ"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ಅನà³â€Œà²²à²¾à²•à³ ಮಾಡಲೠಮೆನೠಒತà³à²¤à²¿à²°à²¿."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ನೆಟà³â€Œà²µà²°à³à²•à³ ಲಾಕೠಆಗಿದೆ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM ಇಲà³à²²"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM ಅನà³à²¨à³ ಸೇರಿಸಿ."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM ಕಾಣೆಯಾಗಿದೆ ಅಥವಾ ರೀಡೠಆಗà³à²¤à³à²¤à²¿à²²à³à²². SIM ಅನà³à²¨à³ ಸೇರಿಸಿ."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM ನಿಷà³à²ªà³à²°à²¯à³‹à²œà²•à²µà²¾à²—ಿದೆ."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ನಿಮà³à²® SIM ಅನà³à²¨à³ ಶಾಶà³à²µà²¤à²µà²¾à²—ಿ ನಿಷà³à²•à³à²°à²¿à²¯à²—ೊಳಿಸಲಾಗಿದೆ.\n ಬೇರೊಂದೠSIM ಗಾಗಿ ನಿಮà³à²® ವೈರà³â€Œà²²à³†à²¸à³ ಸೇವಾ ಪೂರೈಕೆದಾರರನà³à²¨à³ ಸಂಪರà³à²•à²¿à²¸à²¿."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM ಲಾಕೠಆಗಿದೆ."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM PUK ಲಾಕೠಆಗಿದೆ."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM ಅನà³à²¨à³ ಅನà³â€Œà²²à²¾à²•à³ ಮಾಡಲಾಗà³à²¤à³à²¤à²¿à²¦à³†â€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml index 0e09fade9d57f05de6ad803daa4f4018ddff7374..7378cc78e5e677484d215dff2a82f4c34c69a424 100644 --- a/packages/SystemUI/res-keyguard/values-ko/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ì €ì† ì¶©ì „ 중"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 배터리 보호를 위해 ì¶©ì „ 최ì í™”ë¨"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ì¶©ì „ 액세서리 ë¬¸ì œ"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ìž ê¸ˆ í•´ì œí•˜ë ¤ë©´ 메뉴를 누르세요."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ë„¤íŠ¸ì›Œí¬ ìž ê¹€"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM ì—†ìŒ"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIMì„ ì¶”ê°€í•˜ì„¸ìš”."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIMì´ ì—†ê±°ë‚˜ SIMì„ ì½ì„ 수 없습니다. SIMì„ ì¶”ê°€í•˜ì„¸ìš”."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIMì„ ì‚¬ìš©í• ìˆ˜ 없습니다."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIMì´ ì˜êµ¬ì 으로 비활성화ë˜ì—ˆìŠµë‹ˆë‹¤.\n 다른 SIMì„ ì‚¬ìš©í•˜ë ¤ë©´ ë¬´ì„ ì„œë¹„ìŠ¤ ì œê³µì—…ì²´ì— ë¬¸ì˜í•˜ì‹œê¸° ë°”ëžë‹ˆë‹¤."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIMì´ ìž ê¹€ ìƒíƒœìž…니다."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIMì´ PUK ìž ê¹€ ìƒíƒœìž…니다."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM ìž ê¸ˆ í•´ì œ 중…"</string> diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml index 1e03c03b1ae4f1334597f68877213de180f1c68a..88f0b97b385b80faff8070d347b1ec0443f6eaa8 100644 --- a/packages/SystemUI/res-keyguard/values-ky/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жай кубатталууда"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • БатареÑны коргоо үчүн кубаттоо процеÑÑи оптималдаштырылды"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Кубаттоочу шайманда көйгөй бар"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Кулпуну ачуу үчүн Менюну баÑыңыз."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Тармак кулпуланган"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM карта жок"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM карта кошуңуз."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM карта жок же окулбайт. SIM карта кошуңуз."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ЖаракÑыз SIM карта."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM картаңыз биротоло өчүрүлдү.\n Башка SIM карта алуу үчүн зымÑыз кызмат көрÑөтүүчүгө кайрылыңыз."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM карта кулпуланган."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM карта PUK менен кулпуланган."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM картанын кулпуÑу ачылууда…"</string> diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml index 0059d7fbaceedc8d295d139d67f2d02235407aa7..00a382a7cd02a26e89b10530746e84f07752729c 100644 --- a/packages/SystemUI/res-keyguard/values-lo/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • àºàº³àº¥àº±àº‡àºªàº²àºà»àºšàºšàºŠà»‰àº²"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • àºàº²àº™àºªàº²àºàº–ືàºàº›àº±àºšà»ƒàº«à»‰à»€à»àº²àº°àºªàº»àº¡à»€àºžàº·à»ˆàºàº›àº»àºàº›à»‰àºàº‡à»àºšàº±àº”ເຕີຣີ"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ບັນຫາàºàº±àºšàºàº¸àº›àº°àºàºàº™à»€àºªàºµàº¡à»ƒàº™àºàº²àº™àºªàº²àº"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"àºàº»àº” \"ເມນູ\" ເພື່àºàº›àº»àº”ລັàºàº."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ເຄືàºàº‚່າàºàº–ືàºàº¥àº±àºàº"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ບà»à»ˆàº¡àºµàºŠàº´àº¡"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"ເພີ່ມຊິມ."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ບà»à»ˆàº¡àºµàºŠàº´àº¡ ຫຼື àºà»ˆàº²àº™àºŠàº´àº¡àºšà»à»ˆà»„ດ້. ເພີ່ມຊິມ."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ຊິມໃຊ້ບà»à»ˆà»„ດ້."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ຊິມຂàºàº‡àº—່ານຖືàºàº›àº´àº”ໃຊ້ຢ່າງຖາວàºàº™à»àº¥à»‰àº§.\n ຕິດຕà»à»ˆàºœàº¹à»‰à»ƒàº«à»‰àºšà»àº¥àº´àºàº²àº™à»‚ທລະສັບໄຮ້ສາàºàº‚àºàº‡àº—່ານເພື່àºàº‚à»àºŠàº´àº¡à»ƒà»à»ˆ."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ຊິມຖືàºàº¥àº±àºàºàº¢àº¹à»ˆ."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ຊິມຖືàºàº¥àº±àºàºàº”້ວຠPUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"àºàº³àº¥àº±àº‡àº›àº»àº”ລັàºàºàºŠàº´àº¡â€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml index 01e2f8820700edd35db1aa7c375214abf4702ece..31c4107905568283f5070b3e2f77225ac5a954a3 100644 --- a/packages/SystemUI/res-keyguard/values-lt/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • LÄ—tai įkraunama"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ä®krovimas optimizuotas siekiant apsaugoti akumuliatorių"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Su įkrovimo priedu susijusi problema"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Paspauskite meniu, jei norite atrakinti."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tinklas užrakintas"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"NÄ—ra SIM kortelÄ—s"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Ä®dÄ—kite SIM kortelÄ™."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"TrÅ«ksta SIM kortelÄ—s arba ji neskaitoma. Ä®dÄ—kite SIM kortelÄ™."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nenaudojama SIM kortelÄ—."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"JÅ«sų SIM kortelÄ— visam laikui iÅ¡jungta.\n Susisiekite su belaidžio ryÅ¡io paslaugos teikÄ—ju, kad gautumÄ—te naujÄ… SIM kortelÄ™."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM kortelÄ— užrakinta."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM kortelÄ— užrakinta PUK kodu."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Atrakinama SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml index 2133694f99a855ef4f2f1c6a7339c4cff2bc9a59..ecf223351942c7f30f87fe88ab9f287d82fd92a7 100644 --- a/packages/SystemUI/res-keyguard/values-lv/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek lÄ“nÄ uzlÄde"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • UzlÄde optimizÄ“ta, lai saudzÄ“tu akumulatoru"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ProblÄ“ma ar uzlÄdes ierÄ«ci"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Lai atbloÄ·Ä“tu, nospiediet izvÄ“lnes ikonu."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"TÄ«kls ir bloÄ·Ä“ts."</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nav SIM kartes"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Pievienojiet SIM karti."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Nav SIM kartes, vai arÄ« to nevar nolasÄ«t. Pievienojiet SIM karti."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM karte nav izmantojama."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"JÅ«su SIM karte ir neatgriezeniski deaktivizÄ“ta.\n Sazinieties ar savu bezvadu pakalpojumu sniedzÄ“ju, lai iegÅ«tu citu SIM karti."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM karte ir bloÄ·Ä“ta."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM karte ir bloÄ·Ä“ta ar PUK kodu."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Notiek SIM kartes atbloÄ·Ä“Å¡ana…"</string> diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml index 2771c7f5b5d6c5796ee460cee5d6f6d47f8619e1..3f089b991b92fb1f64c6b67a53c711121fc57aaf 100644 --- a/packages/SystemUI/res-keyguard/values-mk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бавно полнење"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Полнењето е оптимизирано за да Ñе заштити батеријата"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проблем Ñо додатокот за полнење"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ПритиÑнете „Мени“ за отклучување."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заклучена"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ðема SIM-картичка"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Додајте SIM-картичка."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Ðема SIM-картичка или не може да Ñе прочита. Додајте SIM-картичка."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-картичката е неупотреблива."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Вашата SIM-картичка е трајно деактивирана.\n Контактирајте Ñо давателот на уÑлуги за безжична мрежа за друга SIM-картичка."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-картичката е заклучена."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-картичката е заклучена Ñо PUK-код."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Се отклучува SIM-картичката…"</string> diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml index 02ee66fc83b42a232009e4b92376e5162929e366..be1ea89688ff01eb44b6a4652559808be30431c1 100644 --- a/packages/SystemUI/res-keyguard/values-ml/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • പതàµà´•àµà´•àµ† ചാർജൠചെയàµà´¯àµà´¨àµà´¨àµ"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ബാറàµà´±à´±à´¿ പരിരകàµà´·à´¿à´•àµà´•à´¾àµ» ചാർജിംഗൠഒപàµà´±àµà´±à´¿à´®àµˆà´¸àµ ചെയàµà´¤àµ"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ചാർജിംഗൠആകàµà´¸à´¸à´±à´¿à´¯àµà´®à´¾à´¯à´¿ ബനàµà´§à´ªàµà´ªàµ†à´Ÿàµà´Ÿ à´ªàµà´°à´¶àµà´¨à´‚"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"അൺലോകàµà´•àµà´šàµ†à´¯àµà´¯à´¾àµ» മെനൠഅമർതàµà´¤àµà´•."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"നെറàµà´±àµâ€Œà´µàµ¼à´•àµà´•àµ ലോകàµà´•àµà´šàµ†à´¯àµâ€Œà´¤àµ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"സിം ഇലàµà´²"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"സിം ചേർകàµà´•àµà´•."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"സിം കാണàµà´¨àµà´¨à´¿à´²àµà´² à´…à´²àµà´²àµ†à´™àµà´•à´¿àµ½ റീഡൠചെയàµà´¯à´¾à´¨à´¾à´¯à´¿à´²àµà´². സിം ചേർകàµà´•àµà´•."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ഉപയോഗശൂനàµà´¯à´®à´¾à´¯ സിം."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"നിങàµà´™à´³àµà´Ÿàµ† സിം ശാശàµà´µà´¤à´®à´¾à´¯à´¿ നിഷàµà´•àµà´°à´¿à´¯à´®à´¾à´•àµà´•à´¿.\n മറàµà´±àµŠà´°àµ സിമàµà´®à´¿à´¨àµ നിങàµà´™à´³àµà´Ÿàµ† വയർലെസൠസേവന ദാതാവിനെ ബനàµà´§à´ªàµà´ªàµ†à´Ÿàµà´•."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"സിം ലോകàµà´•àµ ചെയàµà´¤àµ."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"സിം PUK ലോകàµà´•àµ ചെയàµà´¤àµ."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"സിം അൺലോകàµà´•àµ ചെയàµà´¯àµà´¨àµà´¨àµâ€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml index 2b9f81e9e3589c8349c80fc21c3a13227a947ca2..54fdecd761ac7081f3b2bc2c64614fcbd54b3191 100644 --- a/packages/SystemUI/res-keyguard/values-mn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Удаан цÑнÑглÑж байна"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батарейг хамгаалахын тулд цÑнÑглÑÑ… Ñвцыг оновчилÑон"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ЦÑнÑглÑÑ… Ñ…ÑÑ€ÑгÑÑлд аÑуудал гарлаа"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ТүгжÑÑг тайлах бол цÑÑийг дарна уу."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"СүлжÑÑ Ñ‚Ò¯Ð³Ð¶Ð¸Ð³Ð´ÑÑн"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM байхгүй"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM нÑÐ¼Ð½Ñ Ò¯Ò¯."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM дутуу ÑÑвÑл үүнийг унших боломжгүй байна. SIM нÑÐ¼Ð½Ñ Ò¯Ò¯."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ðшиглах боломжгүй SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Таны SIM-г бүрмөÑөн идÑвхгүй болгоÑон байна.\n Ó¨Ó©Ñ€ SIM авах бол утаÑгүй үйлчилгÑÑ Ò¯Ð·Ò¯Ò¯Ð»ÑгчтÑйгÑÑ Ñ…Ð¾Ð»Ð±Ð¾Ð³Ð´Ð¾Ð½Ð¾ уу."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-г түгжÑÑн байна."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-г PUK-Ñ€ түгжÑÑн байна."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-н түгжÑÑг тайлж байна…"</string> diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml index 7aa7bdd26c081d0026b3603dd7ade5b09cc260bb..eff4c7a9b2d3a9e4f515bb51af9bb4aef5d5dd95 100644 --- a/packages/SystemUI/res-keyguard/values-mr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • सावकाश चारà¥à¤œ होत आहे"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • बॅटरीचे संरकà¥à¤·à¤£ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ ी चारà¥à¤œà¤¿à¤‚ग ऑपà¥à¤Ÿà¤¿à¤®à¤¾à¤‡à¤ केले आहे"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चारà¥à¤œà¤¿à¤‚गचà¥à¤¯à¤¾ ॲकà¥à¤¸à¥‡à¤¸à¤°à¥€à¤¸à¤‚बंधित समसà¥à¤¯à¤¾"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"अनलॉक करणà¥à¤¯à¤¾à¤¸à¤¾à¤ ी मेनू पà¥à¤°à¥‡à¤¸ करा."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवरà¥à¤• लॉक केले"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"सिम नाही"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"सिम जोडा."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"सिम गहाळ à¤à¤¾à¤²à¥‡ आहे किंवा ते रीड करू शकत नाही. सिम जोडा."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"वापरणà¥à¤¯à¤¾à¤¯à¥‹à¤—à¥à¤¯ नसलेले सिम."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"तà¥à¤®à¤šà¥‡ सिम कायमचे डीॲकà¥à¤Ÿà¤¿à¤µà¥à¤¹à¥‡à¤Ÿ केले गेले आहे.\n दà¥à¤¸à¤±à¥à¤¯à¤¾ सिमसाठी तà¥à¤®à¤šà¥à¤¯à¤¾ वायरलेस सेवा पà¥à¤°à¤µà¤ ादाराशी संपरà¥à¤• साधा."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"सिम लॉक केलेले आहे."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"सिम PUK लॉक केलेले आहे."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"सिम अनलॉक करत आहे…"</string> diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml index bdfa4a77eb8acce1ec181b2949bbfe0fb4875b35..d9eb4ca07c6df2fee195bb15be07214cfa4301f8 100644 --- a/packages/SystemUI/res-keyguard/values-ms/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan perlahan"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengecasan dioptimumkan untuk melindungi bateri"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Isu berkaitan aksesori pengecasan"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rangkaian dikunci"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Tiada SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Tambah SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM tiada atau tidak boleh dibaca. Tambah SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM tidak boleh digunakan."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM anda telah dinyahaktifkan secara kekal.\n Hubungi penyedia perkhidmatan wayarles anda untuk mendapatkan SIM lain."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM dikunci."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM dikunci PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Membuka kunci SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml index 576250b4f157798ef30a9a851083f7777484eaf0..afbce26a945fa7260588435bd3137a0df6e0b840 100644 --- a/packages/SystemUI/res-keyguard/values-my/strings.xml +++ b/packages/SystemUI/res-keyguard/values-my/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • နှေးကွေးစွာ အားသွင်းနေသည်"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ဘက်ထရီကာကွယ်ရန် အားသွင်းá€á€¼á€„်းကá€á€¯ အကောင်းဆုံးပြင်ဆင်ထားသည်"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အားသွင်းပစ္စည်းá€á€½á€„် ပြဿနာရှá€á€žá€Šá€º"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"မီနူးကá€á€¯ နှá€á€•á€ºá လော့á€á€ºá€–ွင့်ပါá‹"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ကွန်ရက်ကá€á€¯ လော့á€á€ºá€á€»á€‘ားသည်"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ဆင်းမ်ကá€á€º မရှá€á€•á€«"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"ဆင်းမ်ကá€á€ºá€‘ည့်ပါá‹"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ဆင်းမ်မရှá€á€•á€« (သá€á€¯á€·) သုံးáမရပါዠဆင်းမ်ကá€á€ºá€‘ည့်ပါá‹"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ဆင်းမ်ကá€á€ºá€€á€á€¯ သုံးáမရပါá‹"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"သင်áဆင်းမ်ကá€á€ºá€€á€á€¯ အပြီးပá€á€á€ºá€œá€á€¯á€€á€ºá€žá€Šá€ºá‹\n ဆင်းမ်ကá€á€ºá€”ောက်á€á€…်á€á€¯ ရယူရန် သင်á ကြá€á€¯á€¸á€™á€²á€·á€á€”်ဆောင်မှုပေးသူထံ ဆက်သွယ်ပါá‹"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ဆင်းမ်ကá€á€ºá€€á€á€¯ လော့á€á€ºá€á€»á€‘ားသည်á‹"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ဆင်းမ်ကá€á€ºá ပင်နံပါá€á€ºá€•á€¼á€”်ဖွင့်သည့် ကုဒ်ကá€á€¯ လော့á€á€ºá€á€»á€‘ားသည်á‹"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"ဆင်းမ်ကá€á€º ဖွင့်နေသည်…"</string> diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml index 455d08620fbba5f0359b5686e53c2360469125a1..3098e878f5b6a172e417e914124fea0b119a6f8d 100644 --- a/packages/SystemUI/res-keyguard/values-nb/strings.xml +++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader sakte"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladingen er optimalisert for Ã¥ beskytte batteriet"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem med ladetilbehøret"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Trykk pÃ¥ menyknappen for Ã¥ lÃ¥se opp."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Nettverket er lÃ¥st"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ingen SIM-kort"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Legg til et SIM-kort."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-kortet mangler eller kan ikke leses. Legg til et SIM-kort."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-kortet kan ikke brukes."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM-kortet er deaktivert permanent.\n Kontakt leverandøren av trÃ¥dløstjenesten for Ã¥ fÃ¥ et nytt SIM-kort."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-kortet er lÃ¥st."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-kortet er lÃ¥st med PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"LÃ¥ser opp SIM-kortet …"</string> diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml index f0094a3d427d4d9f108a9c258232e6734620cc0d..45b88197a52d4a8acb2f35d20bc43960009f6ce0 100644 --- a/packages/SystemUI/res-keyguard/values-ne/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • मनà¥à¤¦ गतिमा चारà¥à¤œ गरिà¤à¤¦à¥ˆ"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • बà¥à¤¯à¤¾à¤Ÿà¥à¤°à¥€ जोगाउन चारà¥à¤œ गरà¥à¤¨à¥‡ पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ अपà¥à¤Ÿà¤¿à¤®à¤¾à¤‡à¤œ गरिà¤à¤•à¥‹ छ"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चारà¥à¤œ गरà¥à¤¨à¥‡ à¤à¤•à¥à¤¸à¥‡à¤¸à¤°à¥€à¤®à¤¾ कà¥à¤¨à¥ˆ समसà¥à¤¯à¤¾ आयो"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"अनलक गरà¥à¤¨ मेनॠथिचà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवरà¥à¤• लक à¤à¤à¤•à¥‹ छ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM कारà¥à¤¡ हालिà¤à¤•à¥‹ छैन"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM कारà¥à¤¡ हालà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM कारà¥à¤¡ हालिà¤à¤•à¥‹ छैन वा रिड गरà¥à¤¨ मिलà¥à¤¦à¥ˆà¤¨à¥¤ SIM कारà¥à¤¡ हालà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"यो SIM कारà¥à¤¡ पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨ मिलà¥à¤¦à¥ˆà¤¨à¥¤"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"तपाईंको SIM कारà¥à¤¡ सदाका लागि डिà¤à¤•à¥à¤Ÿà¤¿à¤à¥‡à¤Ÿ गरिà¤à¤•à¥‹ छ।\n आफà¥à¤¨à¥‹ वायरलेस सेवा पà¥à¤°à¤¦à¤¾à¤¯à¤•à¤²à¤¾à¤ˆ समà¥à¤ªà¤°à¥à¤• गरी अरà¥à¤•à¥‹ SIM कारà¥à¤¡ पà¥à¤°à¤¾à¤ªà¥à¤¤ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥à¥¤"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM कारà¥à¤¡ लक गरिà¤à¤•à¥‹ छ।"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM कारà¥à¤¡ PUK पà¥à¤°à¤¯à¥‹à¤— गरी लक गरिà¤à¤•à¥‹ छ।"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM कारà¥à¤¡ अनलक गरिà¤à¤¦à¥ˆ छ…"</string> diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml index 1ba4a814ebde84a92c8826928e298ec8e7ac4fa7..af24d4002e0ef517ae593b1f023da984362bed20 100644 --- a/packages/SystemUI/res-keyguard/values-nl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Langzaam opladen"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladen geoptimaliseerd om de batterij te beschermen"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Probleem met oplaadaccessoire"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Druk op Menu om te ontgrendelen."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netwerk vergrendeld"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Geen simkaart"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Voeg een simkaart toe."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"De simkaart ontbreekt of kan niet worden gelezen. Voeg een simkaart toe."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Onbruikbare simkaart."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Je simkaart is permanent gedeactiveerd.\n Neem contact op met je mobiele serviceprovider voor een nieuwe simkaart."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Simkaart is vergrendeld."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Simkaart is vergrendeld met pukcode."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Simkaart ontgrendelen…"</string> diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml index b31c9c0c2207973b62a2452675f7146eb7fd8c7b..8cae9874bbf3c12e9d6d4261622e54e88746cea4 100644 --- a/packages/SystemUI/res-keyguard/values-or/strings.xml +++ b/packages/SystemUI/res-keyguard/values-or/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଧà€à¬°à‡ ଚାରàଜ ହà‡à¬‰à¬›à¬¿"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ବà‡à¬Ÿà‡à¬°à€à¬•à ସàରକàଷିତ ରଖିବା ପାଇଠଚାରàଜିଂକà ଅପàଟିମାଇଜ କରାଯାଇଛି"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଚାରàଜିଂ ଆକସà‡à¬¸à‹à¬°à€ ସହ ସମସààŸà¬¾"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ଅନଲକà‌ କରିବା ପାଇଠମà‡à¬¨àକà ଦବାନàତà।"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ନà‡à¬Ÿà±à¬°àକକà ଲକà‌ କରାଯାଇଛି"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"କàŒà¬£à¬¸à¬¿ SIM ନାହିà¬"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"à¬à¬• SIM ଯà‹à¬— କରନàତà।"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM ଉପଲବàଧ ନାହିଠକିମàବା ପଢ଼ିପାରିବା ଯà‹à¬—ààŸ à¬¨àହà‡à¬à¥¤ à¬à¬• SIM ଯà‹à¬— କରନàତà।"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ବààŸà¬¬à¬¹à¬¾à¬° ଅଯà‹à¬—ààŸ à¬¥à¬¿à¬¬à¬¾ SIM।"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ଆପଣଙàକ SIMକà ସàଥାàŸà€ à¬à¬¾à¬¬à¬°à‡ ନିଷàକàà¬°à¬¿àŸ à¬•à¬°à¬¾à¬¯à¬¾à¬‡à¬›à¬¿à¥¤\n ଅନààŸ à¬à¬• SIM ପାଇଠଆପଣଙàକ à±à‡àŸà¬¾à¬°à¬²à‡à¬¸ ସà‡à¬¬à¬¾ ପàରଦାନକାରà€à¬™àକ ସହ କଣàଟାକàଟ କରନàତà।"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIMକà ଲକ କରାଯାଇଛି।"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIMକà PUK-ଲକ କରାଯାଇଛି।"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIMକà ଅନଲକ କରାଯାଉଛି…"</string> diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml index 209b63fbd8f817cdf69e0afb594c78aa7da44073..18959c8fb9e8e54610ac10a2b2147bb76433b939 100644 --- a/packages/SystemUI/res-keyguard/values-pa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬੈਟਰੀ ਦੀ ਸà©à¨°à©±à¨–ਿਆ ਲਈ ਚਾਰਜਿੰਗ ਨੂੰ ਸà©à¨¯à©‹à¨— ਬਣਾਇਆ ਗਿਆ"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਕਰਨ ਵਾਲੀ à¨à¨•à¨¸à©ˆà¨¸à¨°à©€ ਸੰਬੰਧੀ ਸਮੱਸਿਆ"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ਅਣਲਾਕ ਕਰਨ ਲਈ \"ਮੀਨੂ\" ਦਬਾਓ।"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ਨੈੱਟਵਰਕ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ਕੋਈ ਸਿਮ ਨਹੀਂ ਹੈ"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"ਸਿਮ ਸ਼ਾਮਲ ਕਰੋ।"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ਸਿਮ ਮੌਜੂਦ ਨਹੀਂ ਹੈ ਜਾਂ ਪੜà©à¨¹à¨¨à¨¯à©‹à¨— ਨਹੀਂ ਹੈ। ਸਿਮ ਸ਼ਾਮਲ ਕਰੋ।"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"ਬੇਕਾਰ ਸਿਮ।"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ਤà©à¨¹à¨¾à¨¡à©‡ ਸਿਮ ਨੂੰ ਪੱਕੇ ਤੌਰ \'ਤੇ ਅਕਿਰਿਆਸ਼ੀਲ ਕੀਤਾ ਗਿਆ ਹੈ।\n ਦੂਜੇ ਸਿਮ ਲਈ ਆਪਣੇ ਵਾਇਰਲੈੱਸ ਸੇਵਾ ਪà©à¨°à¨¦à¨¾à¨¨à¨• ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"ਸਿਮ ਲਾਕ ਹੈ।"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"ਸਿਮ PUK-ਲਾਕ ਹੈ।"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"ਸਿਮ ਨੂੰ ਅਣਲਾਕ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string> diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml index 7ec988ecf777219092018cb38f4b8001caf69b12..bd00ba9bb5f6fe2760c8a71dc3860da6384a4693 100644 --- a/packages/SystemUI/res-keyguard/values-pl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wolne Å‚adowanie"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Åadowanie zoptymalizowane w celu ochrony baterii"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem z akcesoriami do Å‚adowania"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"NaciÅ›nij Menu, aby odblokować."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieć zablokowana"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Brak karty SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Dodaj kartÄ™ SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Brak karty SIM lub nie można jej odczytać. Dodaj kartÄ™ SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nie można użyć karty SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Karta SIM zostaÅ‚a trwale wyÅ‚Ä…czona.\n Skontaktuj siÄ™ z dostawcÄ… usÅ‚ug bezprzewodowych, aby uzyskać innÄ… kartÄ™ SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Karta SIM jest zablokowana."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Karta SIM zostaÅ‚a zablokowana kodem PUK"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"OdblokowujÄ™ kartÄ™ SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml index 78a80912e99e68a9939241ae7f2901973b25992f..54e270f8eb2c85ef1fbefe11de5bbc304935a8d7 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregamento otimizado para proteger a bateria"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema com o acessório de carregamento"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pressione Menu para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Sem chip"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Adicione um chip."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"O chip não foi inserido ou não pode ser lido. Adicione um chip."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Chip inutilizável."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Seu chip foi desativado permanentemente.\n Entre em contato com seu provedor de serviços sem fio para receber outro chip."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"O chip está bloqueado."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"O chip está bloqueado pela PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Desbloqueando chip…"</string> diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml index 5549b36e504c618780ba8d2ee2bf4720a21a7f21..2e37bde883de160bd5d42f9675bf8e4c2e868db1 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar lentamente…"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregamento otimizado para proteger a bateria"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema com o acessório de carregamento"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Prima Menu para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Sem SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Adicione um SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"O SIM está em falta ou não é legÃvel. Adicione um SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM inutilizável."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"O SIM foi desativado permanentemente.\n Contacte o seu fornecedor de serviços de rede sem fios para obter outro SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"O SIM está bloqueado."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"O SIM está bloqueado com o PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"A desbloquear SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml index 78a80912e99e68a9939241ae7f2901973b25992f..54e270f8eb2c85ef1fbefe11de5bbc304935a8d7 100644 --- a/packages/SystemUI/res-keyguard/values-pt/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregamento otimizado para proteger a bateria"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problema com o acessório de carregamento"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pressione Menu para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Sem chip"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Adicione um chip."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"O chip não foi inserido ou não pode ser lido. Adicione um chip."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Chip inutilizável."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Seu chip foi desativado permanentemente.\n Entre em contato com seu provedor de serviços sem fio para receber outro chip."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"O chip está bloqueado."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"O chip está bloqueado pela PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Desbloqueando chip…"</string> diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml index df28b8d10101f22ad5aa92f04f7ef0d33d29a2ae..ead092099326198200b82902b0f9f3b13feb9db8 100644 --- a/packages/SystemUI/res-keyguard/values-ro/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă lent"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ÃŽncărcarea este optimizată pentru a proteja bateria"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problemă legată de accesoriul de încărcare"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Apasă pe Meniu pentru a debloca."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ReÈ›ea blocată"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Niciun card SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Adaugă un card SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Cardul SIM lipseÈ™te sau nu poate fi citit. Adaugă un card SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Cardul SIM nu se poate folosi."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Cardul tău SIM a fost dezactivat definitiv.\n Contactează furnizorul de servicii wireless pentru a obÈ›ine un alt card SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Cardul SIM este blocat."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Cardul SIM este blocat prin cod PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Se deblochează cardul SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml index 62249a1a68c734c41830f3febdb8d04935172312..595fba5dfcb23b38f278017dd1b2fad7aad82dfe 100644 --- a/packages/SystemUI/res-keyguard/values-ru/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"Идет Ð¼ÐµÐ´Ð»ÐµÐ½Ð½Ð°Ñ Ð·Ð°Ñ€Ñдка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ЗарÑдка оптимизирована Ð´Ð»Ñ Ð·Ð°Ñ‰Ð¸Ñ‚Ñ‹ батареи"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проблема Ñ Ð·Ð°Ñ€Ñдным уÑтройÑтвом"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ð”Ð»Ñ Ñ€Ð°Ð·Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ¸ нажмите \"Меню\"."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сеть заблокирована"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM-карта отÑутÑтвует"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Добавьте SIM-карту."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-карта отÑутÑтвует или не раÑпознана. Добавьте SIM-карту."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-карту невозможно иÑпользовать."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM-карта была окончательно деактивирована.\n Чтобы получить новую, обратитеÑÑŒ к Ñвоему оператору мобильной ÑвÑзи."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-карта заблокирована."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-карта заблокирована Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ PUK-кода."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Разблокировка SIM-карты…"</string> diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml index 17ced75aaf1be22355fe284ada54a86dc0e67ebd..b6a742234fe0dd8cd171d94865c73fdbf1f76cc4 100644 --- a/packages/SystemUI/res-keyguard/values-si/strings.xml +++ b/packages/SystemUI/res-keyguard/values-si/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • සෙමින් ආරà·à¶´à¶«à¶º වෙමින්"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • බà·à¶§à¶»à·’ය ආරක්ෂ෠කිරීම සඳහ෠ආරà·à¶´à¶«à¶º ප්â€à¶»à·à·ƒà·Šà¶ කර ඇà¶"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරà·à¶´à¶« උපà·à¶‚ගයේ ගà·à¶§à¶½à·”à·€"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"අගුලු à·„à·à¶»à·“මට මෙනුව ඔබන්න."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ජà·à¶½à¶º අගුළු දම෠ඇà¶"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM නà·à¶"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM එකක් එක් කරන්න."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM අස්ථà·à¶±à¶œà¶à¶ºà·’ හ෠කියවිය නොහà·à¶š. SIM එකක් එක් කරන්න."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"භà·à·€à·’චකළ නොහà·à¶šà·’ SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ඔබේ SIM ස්ථිරවම අක්â€à¶»à·’ය කර ඇà¶.\n වෙනà¶à·Š SIM පà¶à¶šà·Š සඳහ෠ඔබේ රà·à·„à·à¶±à·Š රහිචසේව෠සපයන්න෠අමà¶à¶±à·Šà¶±."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM අගුළු දම෠ඇà¶."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM PUK-අගුළු දම෠ඇà¶."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM අගුළු අරිමින්…"</string> diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml index ef08a6cd5258470331143f097fb59fcbfeb02e7b..5e34a94ffccd4173d3cd4e7c6e28aa21aaf883ea 100644 --- a/packages/SystemUI/res-keyguard/values-sk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • NabÃja sa pomaly"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • NabÃjanie je optimalizované, aby sa chránila batéria"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problém s nabÃjacÃm prÃsluÅ¡enstvom"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Odomknete stlaÄenÃm tlaÄidla ponuky."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"SieÅ¥ je zablokovaná"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Žiadna SIM karta"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Pridajte SIM kartu."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM karta chýba alebo sa nedá ÄÃtaÅ¥. Pridajte SIM kartu."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Nepoužiteľná SIM karta."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"VaÅ¡a SIM karta bola natrvalo deaktivovaná.\n Požiadajte svojho poskytovateľa bezdrôtových služieb o ÄalÅ¡iu SIM kartu."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM karta je uzamknutá."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM karta je uzamknutá kódom PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM karta sa odomyká…"</string> diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml index a42989c18ea9ea288092584ada9972e7fd981625..3508f3b3af6e8d9781188be96da4792b5e11f132 100644 --- a/packages/SystemUI/res-keyguard/values-sl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • poÄasno polnjenje"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Polnjenje je optimizirano zaradi zaÅ¡Äite baterije"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • težava s pripomoÄkom za polnjenje"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ÄŒe želite odkleniti, pritisnite meni."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Omrežje je zaklenjeno"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ni kartice SIM."</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Dodajte kartico SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Ni kartice SIM ali je ni mogoÄe prebrati. Dodajte kartico SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Kartica SIM je neuporabna."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"VaÅ¡a kartica SIM je bila trajno deaktivirana.\n Za drugo kartico SIM se obrnite na ponudnika brezžiÄnih storitev."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Kartica SIM je zaklenjena."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Kartica SIM je zaklenjena s kodo PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Odklepanje kartice SIM …"</string> diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml index ce53b7ed3a98d97a61ceaf26d9c9ce5e2d601c27..8d71b0f941b78b7e948274f83ff617a53e5af5ba 100644 --- a/packages/SystemUI/res-keyguard/values-sq/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet ngadalë"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Karikimi u optimizua për të mbrojtur baterinë"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Problem me aksesorin e karikimit"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Shtyp \"Meny\" për të shkyçur."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rrjeti është i kyçur"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Nuk ka kartë SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Shto një kartë SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Karta SIM mungon ose është e palexueshme. Shto një kartë SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Kartë SIM e papërdorshme."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Karta jote SIM është çaktivizuar përgjithmonë.\n Kontakto me ofruesin e shërbimit wireless për një tjetër kartë SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Karta SIM është e kyçur."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Karta SIM është e kyçur me PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Karta SIM po shkyçet…"</string> diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml index 437018d938795e5105c09e2dc8ff4dc71b5a1a22..409395285343cc2517f261f2c1b2bbab2b2ced4d 100644 --- a/packages/SystemUI/res-keyguard/values-sr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Споро Ñе пуни"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуњење је оптимизовано да би Ñе заштитила батерија"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проблем Ñа додатним прибором за пуњење"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ПритиÑните Мени да биÑте откључали."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежа је закључана"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ðема SIM-а"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Додајте SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM недоÑтаје или не може да Ñе прочита. Додајте SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ðеупотребљив SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM је трајно деактивиран.\n Обратите Ñе добављачу уÑлуге бежичне телефоније да биÑте добили други SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM је закључан."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM је закључан PUK-ом."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Откључава Ñе SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml index b4b1996094bc14a3cfb49c8a0569bb4991563e53..5b01f39a6a11916053ef9fffeeeb6d3e7b7378f9 100644 --- a/packages/SystemUI/res-keyguard/values-sv/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas lÃ¥ngsamt"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddningen har optimerats för att skydda batteriet"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ett problem uppstod med att ladda tillbehöret"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"LÃ¥s upp genom att trycka pÃ¥ Meny."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Nätverk lÃ¥st"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Inget SIM-kort"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Lägg till ett SIM-kort."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-kort saknas eller gÃ¥r inte att läsa. Lägg till ett SIM-kort."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM-kortet gÃ¥r inte att använda."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Ditt SIM-kort har inaktiverats permanent.\n Kontakta din operatör och be om ett nytt SIM-kort."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-kortet är lÃ¥st."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-kortet har lÃ¥sts med PUK-kod."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM-kortet lÃ¥ses upp …"</string> diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml index 8ca90465ee3a32a9e075535fa3f63500ede99c7e..72f1fc31c3dac645d33fa703ad0b8b1a9b83fbd5 100644 --- a/packages/SystemUI/res-keyguard/values-sw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji pole pole"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hali ya kuchaji imeboreshwa ili kulinda betri"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kifuasi cha kuchaji kina hitilafu"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Bonyeza Menyu ili kufungua."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mtandao umefungwa"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Hakuna SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Weka SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM haipo au haiwezi kusomwa. Weka SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM haiwezi kutumika."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM kadi yako imefungwa kabisa.\n wasiliana na mtoa huduma wako wa pasi waya ili upate SIM nyingine."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM imefungwa."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM imefungwa kwa PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Inafungua SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml index 7671194c3b142871cbdef5549bded19187dcc8d5..20eb8ef3e43e342d3c1164749e582168530933d3 100644 --- a/packages/SystemUI/res-keyguard/values-ta/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • மெதà¯à®µà®¾à®•à®šà¯ சாரà¯à®œà®¾à®•à®¿à®±à®¤à¯"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • பேடà¯à®Ÿà®°à®¿à®¯à¯ˆà®ªà¯ பாதà¯à®•à®¾à®•à¯à®• சாரà¯à®œà®¿à®™à¯ மேமà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சாரà¯à®œà®°à®¿à®²à¯ சிகà¯à®•à®²à¯ உளà¯à®³à®¤à¯"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"அனà¯à®²à®¾à®•à¯ செயà¯à®¯ மெனà¯à®µà¯ˆ à®…à®´à¯à®¤à¯à®¤à®µà¯à®®à¯."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"நெடà¯à®µà¯Šà®°à¯à®•à¯ பூடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"சிம௠இலà¯à®²à¯ˆ"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"சிமà¯à®®à¯ˆà®šà¯ சேரà¯à®™à¯à®•à®³à¯."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"சிம௠இலà¯à®²à¯ˆ அலà¯à®²à®¤à¯ படிகà¯à®•à®•à¯à®•à¯‚டியதாக இலà¯à®²à¯ˆ. சிமà¯à®®à¯ˆà®šà¯ சேரà¯à®™à¯à®•à®³à¯."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤ à®®à¯à®Ÿà®¿à®¯à®¾à®¤ சிமà¯."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"உஙà¯à®•à®³à¯ சிம௠நிரநà¯à®¤à®°à®®à®¾à®• à®®à¯à®Ÿà®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯.\n மறà¯à®±à¯Šà®°à¯ சிமà¯à®®à®¿à®±à¯à®•à®¾à®© உஙà¯à®•à®³à¯ வயரà¯à®²à¯†à®¸à¯ சேவை வழஙà¯à®•à¯à®¨à®°à¯ˆà®¤à¯ தொடரà¯à®ªà¯à®•à¯Šà®³à¯à®³à¯à®™à¯à®•à®³à¯."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"சிம௠லாக௠செயà¯à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"சிம௠PUK-லாக௠செயà¯à®¯à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³à®¤à¯."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"சிமà¯à®®à¯ˆ அனà¯à®²à®¾à®•à¯ செயà¯à®•à®¿à®±à®¤à¯â€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml index 623b589861a9b35932baf2e691292d9732b65078..d496944798f9e00d4f2adb4a65b134ead7bac56f 100644 --- a/packages/SystemUI/res-keyguard/values-te/strings.xml +++ b/packages/SystemUI/res-keyguard/values-te/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • నెమà±à°®à°¦à°¿à°—à°¾ ఛారà±à°œà± à°…à°µà±à°¤à±‹à°‚ది"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • à°¬à±à°¯à°¾à°Ÿà°°à±€à°¨à°¿ à°°à°•à±à°·à°¿à°‚చడానికి ఛారà±à°œà°¿à°‚గౠఆపà±à°Ÿà°¿à°®à±ˆà°œà± చేయబడింది"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛారà±à°œà°¿à°‚గౠయాకà±à°¸à±†à°¸à°°à±€à°¤à±‹ సమసà±à°¯ ఉంది"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"à°…à°¨à±â€Œà°²à°¾à°•à± చేయడానికి మెనూనౠనొకà±à°•à°‚à°¡à°¿."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"నెటà±â€Œà°µà°°à±à°•à± లాకౠచేయబడింది"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM లేదà±"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIMనౠజోడించండి."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM మిసౠఅయà±à°¯à°¿à°‚ది లేదా ఆమోదయోగà±à°¯à°‚ కాదà±. SIMనౠజోడించండి."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"వినియోగించలేని SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"మీ SIM శాశà±à°µà°¤à°‚à°—à°¾ డీయాకà±à°Ÿà°¿à°µà±‡à°Ÿà± చేయబడింది.\n మరో SIMనౠపొందడం కోసం మీ వైరà±â€Œà°²à±†à°¸à± సరà±à°µà±€à°¸à± à°ªà±à°°à±Šà°µà±ˆà°¡à°°à±â€Œà°¨à± సంపà±à°°à°¦à°¿à°‚à°šà°‚à°¡à°¿."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM లాకౠచేయబడింది."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM PUK లాకౠచేయబడింది."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIMనౠఅనà±â€Œà°²à°¾à°•à± చేసà±à°¤à±‹à°‚ది…"</string> diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml index c2441075d02db0fc9b245b720e6819793f370481..605d0772ac6e7e355fb2c18bb9634e61d085c32a 100644 --- a/packages/SystemUI/res-keyguard/values-th/strings.xml +++ b/packages/SystemUI/res-keyguard/values-th/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • à¸à¸³à¸¥à¸±à¸‡à¸Šà¸²à¸£à¹Œà¸ˆà¸à¸¢à¹ˆà¸²à¸‡à¸Šà¹‰à¸²à¹†"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ปรับà¸à¸²à¸£à¸Šà¸²à¸£à¹Œà¸ˆà¹ƒà¸«à¹‰à¹€à¸«à¸¡à¸²à¸°à¸ªà¸¡à¹€à¸žà¸·à¹ˆà¸à¸–นà¸à¸¡à¹à¸šà¸•à¹€à¸•à¸à¸£à¸µà¹ˆ"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ปัà¸à¸«à¸²à¹€à¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¸à¸¸à¸›à¸à¸£à¸“์เสริมสำหรับà¸à¸²à¸£à¸Šà¸²à¸£à¹Œà¸ˆ"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"à¸à¸” \"เมนู\" เพื่à¸à¸›à¸¥à¸”ล็à¸à¸"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"เครืà¸à¸‚่ายถูà¸à¸¥à¹‡à¸à¸"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"ไม่มี SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"โปรดใส่ SIM"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"ไม่มี SIM หรืà¸à¸à¹ˆà¸²à¸™à¹„ม่ได้ โปรดใส่ SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM ใช้งานไม่ได้"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"ปิดใช้งาน SIM à¸à¸¢à¹ˆà¸²à¸‡à¸–าวรà¹à¸¥à¹‰à¸§\n ติดต่à¸à¸œà¸¹à¹‰à¹ƒà¸«à¹‰à¸šà¸£à¸´à¸à¸²à¸£à¹„ร้สายขà¸à¸‡à¸„ุณเพื่à¸à¸£à¸±à¸š SIM ใหม่"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM ถูà¸à¸¥à¹‡à¸à¸"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM ถูà¸à¸¥à¹‡à¸à¸à¸”้วย PUK"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"à¸à¸³à¸¥à¸±à¸‡à¸›à¸¥à¸”ล็à¸à¸ SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml index cd8f81046f969c81510ff30656d8c943dcfe5cde..040ec9e9b703aa5758bc3799d827ce28aafdebc0 100644 --- a/packages/SystemUI/res-keyguard/values-tl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabagal na nagcha-charge"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Naka-optimize ang pag-charge para protektahan ang baterya"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Isyu sa pag-charge ng accessory"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pindutin ang Menu upang i-unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Naka-lock ang network"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Walang SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Magdagdag ng SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Wala o hindi nababasa ang SIM. Magdagdag ng SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Hindi magagamit na SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"Permanenteng na-deactivate ang iyong SIM.\n Makipag-ugnayan sa iyong service provider ng wireless para sa isa pang SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"Naka-lock ang SIM."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"Naka-PUK lock ang SIM."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Ina-unlock ang SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml index ddeba67719d7acbfa84ccad70a6c9988d7bba33d..750ba1174fe8b3be2b244d903d5337c806796873 100644 --- a/packages/SystemUI/res-keyguard/values-tr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • YavaÅŸ ÅŸarj oluyor"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Åžarj iÅŸlemi pili korumak üzere optimize edildi"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Åžarj aksesuarı ile ilgili sorun"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Kilidi açmak için Menü\'ye basın."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"AÄŸ kilitli"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM yok"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM ekleyin."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM yok veya okunamıyor. SIM ekleyin."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Kullanılamayan SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM\'iniz kalıcı olarak devre dışı bırakıldı.\n BaÅŸka bir SIM için kablosuz servis saÄŸlayıcınızla iletiÅŸime geçin."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM kilitli."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM\'in PUK kilidi devrede."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM\'in kilidi açılıyor…"</string> diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml index f06d17df9146731ae3eed515c718795b6bcca3bc..169ea1f16eec5754847b73daa20895035be2fd12 100644 --- a/packages/SystemUI/res-keyguard/values-uk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Повільне зарÑджаннÑ"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ЗарÑÐ´Ð¶Ð°Ð½Ð½Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ñ–Ð·Ð¾Ð²Ð°Ð½Ð¾, щоб захиÑтити акумулÑтор"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Проблема із зарÑдним приÑтроєм"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ÐатиÑніть меню, щоб розблокувати."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мережу заблоковано"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ðемає SIM-карти"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Додайте SIM-карту."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM-карта відÑÑƒÑ‚Ð½Ñ Ð°Ð±Ð¾ недоÑтупна Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ. Додайте SIM-карту."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ðепридатна SIM-карта."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM-карту деактивовано назавжди.\n Щоб отримати іншу, звернітьÑÑ Ð´Ð¾ Ñвого поÑтачальника поÑлуг бездротового зв’Ñзку."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM-карту заблоковано."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM-карту заблоковано PUK-кодом."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Ð Ð¾Ð·Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ SIM-карти…"</string> diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml index 8adbaca955f7dea1ca58e48eb64de938b8887762..d7f7b653e92eadabd7931a9d3f4951d8458fd014 100644 --- a/packages/SystemUI/res-keyguard/values-ur/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ø¢ÛØ³ØªÛ Ú†Ø§Ø±Ø¬ ÛÙˆ رÛا ÛÛ’"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • بیٹری Ú©ÛŒ ØÙاظت Ú©Û’ لیے چارجنگ Ú©Ùˆ بÛتر بنایا گیا"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • چارجنگ ایکسیسری Ú©Û’ ساتھ مسئلÛ"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"غیر مقÙÙ„ کرنے کیلئے مینیو دبائیں۔"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"نیٹ ورک مقÙÙ„ ÛÙˆ گیا"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"â€Ú©ÙˆØ¦ÛŒ SIM Ù†Ûیں ÛÛ’"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"â€Ø§ÛŒÚ© SIM شامل کریں۔"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"â€SIM غائب ÛÛ’ یا Ù¾Ú‘Ú¾Ù†Û’ لائق Ù†Ûیں ÛÛ’Û” ایک SIM شامل کریں۔"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"â€Ù†Ø§Ù‚ابل استعمال SIMÛ”"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"â€Ø¢Ù¾ Ú©Û’ SIM Ú©Ùˆ مستقل طور پر غیر Ùعال کر دیا گیا ÛÛ’Û”\n کسی دوسرے SIM کیلئے اپنے وائرلیس سروس ÙراÛÙ… Ú©Ù†Ù†Ø¯Û Ø³Û’ Ø±Ø§Ø¨Ø·Û Ú©Ø±ÛŒÚºÛ”"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"â€SIM مقÙÙ„ ÛÛ’Û”"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"â€Ø¢Ù¾ کا SIM â€PUK مقÙÙ„ ÛÛ’Û”"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"â€SIM Ú©Ùˆ غیر مقÙÙ„ کیا جا رÛا Ûے…"</string> diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml index 96dfa05a74aaf1e98de22d2807726b333f49f938..40dbaf3142fa2822e91ec39c611d96e4586880c5 100644 --- a/packages/SystemUI/res-keyguard/values-uz/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sekin quvvat olmoqda"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batareyani himoyalash uchun quvvatlash optimallashtirildi"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quvvatlash aksessuari bilan muammo"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Qulfdan chiqarish uchun Menyu tugmasini bosing."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tarmoq qulflangan"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"SIM kartasiz"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"SIM karta qoÊ»shish."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM karta topilmadi yoki oÊ»qilmadi. SIM karta qoÊ»shish."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"Ishlamaydigan SIM."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM karta butunlay faolsizlantirildi.\n Boshqa SIM karta olish uchun simsiz aloqa operatoriga murojaat qiling."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM karta qulflandi."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM karta PUK kod bilan qulflangan."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM karta qulfdan chiqarilmoqda…"</string> diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml index 41b5a33ba967693af2cb9fa8c8ca66e425f12bb8..d5a33d37bc660b7424ddfea392edf9519aef1557 100644 --- a/packages/SystemUI/res-keyguard/values-vi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Äang sạc cháºm"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quá trình sạc được tối Æ°u hoá để bảo vệ pin"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Có vấn Ä‘á» vá»›i phụ kiện sạc"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Nhấn và o Menu để mở khóa."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mạng đã bị khóa"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Không có SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"Hãy thêm SIM."</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"Không tìm thấy hoặc không Ä‘á»c được SIM. Hãy thêm SIM."</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM không sá» dụng được."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM của bạn đã bị vô hiệu hoá vÄ©nh viá»…n.\n Hãy liên hệ vá»›i nhà cung cấp dịch vụ viá»…n thông không dây của bạn để yêu cầu cấp SIM khác."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM nà y Ä‘ang bị khoá."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM nà y Ä‘ang bị khoá PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Äang mở khoá SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml index 4c65832162ba89409fd689039f52bb4be6789267..6de9ff9448820cecccac6c6d3af0fd9b9dfe8158 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • æ£åœ¨æ…¢é€Ÿå……电"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 为ä¿æŠ¤ç”µæ± ,充电方å¼å·²ä¼˜åŒ–"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充电é…件有问题"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按“èœå•â€å³å¯è§£é”。"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"网络已é”定"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"没有 SIM å¡"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"请æ’å…¥ SIM å¡ã€‚"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"SIM å¡ç¼ºå¤±æˆ–æ— æ³•è¯»å–。请æ’å…¥ SIM å¡ã€‚"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM å¡æ— 法使用。"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"您的 SIM å¡å·²è¢«æ°¸ä¹…åœç”¨ã€‚\nè¯·ä¸Žæ‚¨çš„æ— çº¿æœåŠ¡æ供商è”系,以便é‡æ–°èŽ·å–ä¸€å¼ SIM å¡ã€‚"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM å¡å·²è¢«é”定。"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM å¡å·²ç”¨ PUK ç é”定。"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"æ£åœ¨è§£é” SIM å¡â€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml index dad6f31ac5531983d95bb98f9ed3a9ac09f4db3a..11966ca08dcefca26c46fa47d6a7dda4006b3ea9 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電ä¸"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 為ä¿è·é›»æ± ,系統已優化充電"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • å……é›»é…件發生å•é¡Œ"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按下 [é¸å–®] å³å¯è§£éŽ–。"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"網絡已鎖定"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"沒有 SIM å¡"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"請新增 SIM å¡ã€‚"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"找ä¸åˆ° SIM å¡æˆ– SIM å¡ç„¡æ³•è®€å–,請新增 SIM å¡ã€‚"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM å¡ç„¡æ³•ä½¿ç”¨ã€‚"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM å¡å·²æ°¸ä¹…åœç”¨ã€‚\nè«‹å‘ç„¡ç·šæœå‹™ä¾›æ‡‰å•†ç´¢å–其他 SIM å¡ã€‚"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM å¡å·²éŽ–定。"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM å¡å·²ä½¿ç”¨ PUK 鎖定。"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"æ£åœ¨è§£éŽ– SIM å¡â€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml index 88b7e4374ff9228761a2fc7b83d1f15b2bd20b80..e4f868a093d376968d32ccf2298d936e92157065 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電ä¸"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 為ä¿è·é›»æ± ,充電效能已最佳化"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • å……é›»é…件有å•é¡Œ"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按é¸å–®éµè§£éŽ–。"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"網路已鎖定"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"沒有 SIM å¡"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"請新增 SIM å¡ã€‚"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"找ä¸åˆ° SIM å¡æˆ– SIM å¡ç„¡æ³•è®€å–,請新增 SIM å¡ã€‚"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"SIM å¡ç„¡æ³•ä½¿ç”¨ã€‚"</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"SIM å¡å·²æ°¸ä¹…åœç”¨ã€‚\n è«‹å‘ç„¡ç·šæœå‹™ä¾›æ‡‰å•†ç´¢å–其他 SIM å¡ã€‚"</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM å¡å·²éŽ–定。"</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM å¡å·²ä½¿ç”¨ PUK 碼鎖定。"</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"æ£åœ¨è§£éŽ– SIM å¡â€¦"</string> diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml index c5e99abbda54d60c121a2d108711012c625cf713..4fadc2e55ee920ffa1f8236be38be9521264ae91 100644 --- a/packages/SystemUI/res-keyguard/values-zu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml @@ -35,13 +35,9 @@ <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kancane"</string> <string name="keyguard_plugged_in_charging_limited" msgid="1053130519456324630">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ukushaja kuthuthukisiwe ukuze kuvikelwe ibhethri"</string> <string name="keyguard_plugged_in_incompatible_charger" msgid="3687961801947819076">"<xliff:g id="PERCENTAGE">%s</xliff:g> • • Inkinga ngesisekeli sokushaja"</string> - <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Chofoza Menyu ukuvula."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Inethiwekhi ivaliwe"</string> <string name="keyguard_missing_sim_message_short" msgid="685029586173458728">"Ayikho i-SIM"</string> - <string name="keyguard_missing_sim_instructions" msgid="7735360104844653246">"engeza i-SIM"</string> - <string name="keyguard_missing_sim_instructions_long" msgid="3451467338947610268">"I-SIM ayitholakali noma ayifundeki. engeza i-SIM"</string> <string name="keyguard_permanent_disabled_sim_message_short" msgid="3955052454216046100">"I-SIM engasebenziseki."</string> - <string name="keyguard_permanent_disabled_sim_instructions" msgid="5034635040020685428">"I-SIM yakho iyekiswe ukusebenza unomphela.\n Xhumana nomhlinzeki wakho wesevisi ngokungenazintambo ukuze uthole enye i-SIM."</string> <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"I-SIM ikhiyiwe."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"I-SIM ikhiyiwe nge-PUK."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"Ivula i-SIM…"</string> diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 28b58703b02de7eb05c18c1b16fbae4129d33ce5..565ed1085fb9dc6a58dcf7a01787bfebaca291f8 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -67,23 +67,13 @@ <!-- When the lock screen is showing and the phone plugged in with incompatible charger. --> <string name="keyguard_plugged_in_incompatible_charger"><xliff:g id="percentage">%s</xliff:g> • Issue with charging accessory</string> - <!-- On the keyguard screen, when pattern lock is disabled, only tell them to press menu to unlock. This is shown in small font at the bottom. --> - <string name="keyguard_instructions_when_pattern_disabled">Press Menu to unlock.</string> - <!-- SIM messages --><skip /> <!-- When the user inserts a sim card from an unsupported network, it becomes network locked --> <string name="keyguard_network_locked_message">Network locked</string> <!-- Shown when there is no SIM. --> <string name="keyguard_missing_sim_message_short">No SIM</string> - <!-- Shown to ask the user to add a SIM. --> - <string name="keyguard_missing_sim_instructions">Add a SIM.</string> - <!-- Shown to ask the user to add a SIM when sim is missing or not readable. --> - <string name="keyguard_missing_sim_instructions_long">The SIM is missing or not readable. Add a SIM.</string> <!-- Shown when SIM is permanently disabled. --> <string name="keyguard_permanent_disabled_sim_message_short">Unusable SIM.</string> - <!-- Shown to inform the user to SIM is permanently deactivated. --> - <string name="keyguard_permanent_disabled_sim_instructions">Your SIM has been permanently deactivated.\n - Contact your wireless service provider for another SIM.</string> <!-- Shown to tell the user that their SIM is locked and they must unlock it. --> <string name="keyguard_sim_locked_message">SIM is locked.</string> <!-- When the user enters a wrong sim pin too many times, it becomes PUK locked (Pin Unlock Kode) --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Tracing.kt b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Tracing.kt new file mode 100644 index 0000000000000000000000000000000000000000..9b7cd704aa2ffcd4701170ce03d6e123145fc0d8 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/Tracing.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2023 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.dagger.qualifiers + +import javax.inject.Qualifier + +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Tracing diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt index 7b2e1afda7005ffd394980d8279510fbd4b4266e..e4590342972861c8246f8ffadbb20fb746a863d7 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 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. @@ -18,13 +18,25 @@ package com.android.systemui.util import android.os.Trace import android.os.TraceNameSupplier +import android.util.Log +import com.android.systemui.util.tracing.TraceContextElement +import com.android.systemui.util.tracing.TraceData +import com.android.systemui.util.tracing.TraceData.Companion.FIRST_VALID_SPAN +import com.android.systemui.util.tracing.TraceData.Companion.INVALID_SPAN +import com.android.systemui.util.tracing.threadLocalTrace import java.util.concurrent.atomic.AtomicInteger import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.coroutines.coroutineContext +import kotlin.random.Random import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Deferred +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job import kotlinx.coroutines.async +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext /** * Run a block within a [Trace] section. Calls [Trace.beginSection] before and [Trace.endSection] @@ -44,6 +56,10 @@ inline fun <T> traceSection(tag: String, block: () -> T): T = class TraceUtils { companion object { + const val TAG = "TraceUtils" + private const val DEBUG_COROUTINE_TRACING = false + const val DEFAULT_TRACK_NAME = "AsyncTraces" + inline fun traceRunnable(tag: String, crossinline block: () -> Unit): Runnable { return Runnable { traceSection(tag) { block() } } } @@ -73,7 +89,7 @@ class TraceUtils { * under a single track. */ inline fun <T> traceAsync(method: String, block: () -> T): T = - traceAsync("AsyncTraces", method, block) + traceAsync(DEFAULT_TRACK_NAME, method, block) /** * Creates an async slice in a track with [trackName] while [block] runs. @@ -93,16 +109,313 @@ class TraceUtils { } /** - * Convenience method to avoid one indentation level when we want to add a trace when - * launching a coroutine + * Convenience function for calling [CoroutineScope.launch] with [traceCoroutine] enable + * tracing. + * + * @see traceCoroutine + */ + inline fun CoroutineScope.launch( + crossinline spanName: () -> String, + context: CoroutineContext = EmptyCoroutineContext, + // TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size + crossinline block: suspend CoroutineScope.() -> Unit + ): Job = launch(context) { traceCoroutine(spanName) { block() } } + + /** + * Convenience function for calling [CoroutineScope.launch] with [traceCoroutine] enable + * tracing. + * + * @see traceCoroutine + */ + inline fun CoroutineScope.launch( + spanName: String, + context: CoroutineContext = EmptyCoroutineContext, + // TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size + crossinline block: suspend CoroutineScope.() -> Unit + ): Job = launch(context) { traceCoroutine(spanName) { block() } } + + /** + * Convenience function for calling [CoroutineScope.async] with [traceCoroutine] enable + * tracing + * + * @see traceCoroutine + */ + inline fun <T> CoroutineScope.async( + crossinline spanName: () -> String, + context: CoroutineContext = EmptyCoroutineContext, + // TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size + crossinline block: suspend CoroutineScope.() -> T + ): Deferred<T> = async(context) { traceCoroutine(spanName) { block() } } + + /** + * Convenience function for calling [CoroutineScope.async] with [traceCoroutine] enable + * tracing. + * + * @see traceCoroutine */ - fun <T> CoroutineScope.tracedAsync( - method: String, + inline fun <T> CoroutineScope.async( + spanName: String, context: CoroutineContext = EmptyCoroutineContext, - start: CoroutineStart = CoroutineStart.DEFAULT, - block: suspend () -> T - ): Deferred<T> { - return async(context, start) { traceAsync(method) { block() } } + // TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size + crossinline block: suspend CoroutineScope.() -> T + ): Deferred<T> = async(context) { traceCoroutine(spanName) { block() } } + + /** + * Convenience function for calling [runBlocking] with [traceCoroutine] to enable tracing. + * + * @see traceCoroutine + */ + inline fun <T> runBlocking( + crossinline spanName: () -> String, + context: CoroutineContext, + crossinline block: suspend () -> T + ): T = runBlocking(context) { traceCoroutine(spanName) { block() } } + + /** + * Convenience function for calling [runBlocking] with [traceCoroutine] to enable tracing. + * + * @see traceCoroutine + */ + inline fun <T> runBlocking( + spanName: String, + context: CoroutineContext, + crossinline block: suspend CoroutineScope.() -> T + ): T = runBlocking(context) { traceCoroutine(spanName) { block() } } + + /** + * Convenience function for calling [withContext] with [traceCoroutine] to enable tracing. + * + * @see traceCoroutine + */ + suspend inline fun <T> withContext( + spanName: String, + context: CoroutineContext, + crossinline block: suspend CoroutineScope.() -> T + ): T = withContext(context) { traceCoroutine(spanName) { block() } } + + /** + * Convenience function for calling [withContext] with [traceCoroutine] to enable tracing. + * + * @see traceCoroutine + */ + suspend inline fun <T> withContext( + crossinline spanName: () -> String, + context: CoroutineContext, + crossinline block: suspend CoroutineScope.() -> T + ): T = withContext(context) { traceCoroutine(spanName) { block() } } + + /** + * A hacky way to propagate the value of the COROUTINE_TRACING flag for static usage in this + * file. It should only every be set to true during startup. Once true, it cannot be set to + * false again. + */ + var coroutineTracingIsEnabled = false + set(v) { + if (v) field = true + } + + /** + * Traces a section of work of a `suspend` [block]. The trace sections will appear on the + * thread that is currently executing the [block] of work. If the [block] is suspended, all + * trace sections added using this API will end until the [block] is resumed, which could + * happen either on this thread or on another thread. If a child coroutine is started, it + * will inherit the trace sections of its parent. The child will continue to print these + * trace sections whether or not the parent coroutine is still running them. + * + * The current [CoroutineContext] must have a [TraceContextElement] for this API to work. + * Otherwise, the trace sections will be dropped. + * + * For example, in the following trace, Thread #1 ran some work, suspended, then continued + * working on Thread #2. Meanwhile, Thread #2 created a new child coroutine which inherited + * its trace sections. Then, the original coroutine resumed on Thread #1 before ending. + * Meanwhile Thread #3 is still printing trace sections from its parent because they were + * copied when it was created. There is no way for the parent to communicate to the child + * that it marked these slices as completed. While this might seem counterintuitive, it + * allows us to pinpoint the origin of the child coroutine's work. + * + * ``` + * Thread #1 | [==== Slice A ====] [==== Slice A ====] + * | [==== B ====] [=== B ===] + * -------------------------------------------------------------------------------------- + * Thread #2 | [====== Slice A ======] + * | [========= B =========] + * | [===== C ======] + * -------------------------------------------------------------------------------------- + * Thread #3 | [== Slice A ==] [== Slice A ==] + * | [===== B =====] [===== B =====] + * | [===== C =====] [===== C =====] + * | [=== D ===] + * ``` + * + * @param name The name of the code section to appear in the trace + * @see endSlice + * @see traceCoroutine + */ + @OptIn(ExperimentalCoroutinesApi::class) + suspend inline fun <T> traceCoroutine( + spanName: Lazy<String>, + crossinline block: suspend () -> T + ): T { + // For coroutine tracing to work, trace spans must be added and removed even when + // tracing is not active (i.e. when TRACE_TAG_APP is disabled). Otherwise, when the + // coroutine resumes when tracing is active, we won't know its name. + val tracer = getTraceData(spanName) + val coroutineSpanCookie = tracer?.beginSpan(spanName.value) ?: INVALID_SPAN + + // For now, also trace to "AsyncTraces". This will allow us to verify the correctness + // of the COROUTINE_TRACING feature flag. + val asyncTraceCookie = + if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) + Random.nextInt(FIRST_VALID_SPAN, Int.MAX_VALUE) + else INVALID_SPAN + if (asyncTraceCookie != INVALID_SPAN) { + Trace.asyncTraceForTrackBegin( + Trace.TRACE_TAG_APP, + DEFAULT_TRACK_NAME, + spanName.value, + asyncTraceCookie + ) + } + try { + return block() + } finally { + if (asyncTraceCookie != INVALID_SPAN) { + Trace.asyncTraceForTrackEnd( + Trace.TRACE_TAG_APP, + DEFAULT_TRACK_NAME, + asyncTraceCookie + ) + } + tracer?.endSpan(coroutineSpanCookie) + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + suspend fun getTraceData(spanName: Lazy<String>): TraceData? { + if (!coroutineTracingIsEnabled) { + logVerbose("Experimental flag COROUTINE_TRACING is off", spanName) + } else if (coroutineContext[TraceContextElement] == null) { + logVerbose("Current CoroutineContext is missing TraceContextElement", spanName) + } else { + return threadLocalTrace.get().also { + if (it == null) logVerbose("ThreadLocal TraceData is null", spanName) + } + } + return null + } + + private fun logVerbose(logMessage: String, spanName: Lazy<String>) { + if (DEBUG_COROUTINE_TRACING && Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "$logMessage. Dropping trace section: \"${spanName.value}\"") + } + } + + /** @see traceCoroutine */ + suspend inline fun <T> traceCoroutine( + spanName: String, + crossinline block: suspend () -> T + ): T = traceCoroutine(lazyOf(spanName)) { block() } + + /** @see traceCoroutine */ + suspend inline fun <T> traceCoroutine( + crossinline spanName: () -> String, + crossinline block: suspend () -> T + ): T = traceCoroutine(lazy(LazyThreadSafetyMode.PUBLICATION) { spanName() }) { block() } + + /** + * Writes a trace message to indicate that a given section of code has begun running __on + * the current thread__. This must be followed by a corresponding call to [endSlice] in a + * reasonably short amount of time __on the same thread__ (i.e. _before_ the thread becomes + * idle again and starts running other, unrelated work). + * + * Calls to [beginSlice] and [endSlice] may be nested, and they will render in Perfetto as + * follows: + * ``` + * Thread #1 | [==========================] + * | [==============] + * | [====] + * ``` + * + * This function is provided for convenience to wrap a call to [Trace.traceBegin], which is + * more verbose to call than [Trace.beginSection], but has the added benefit of not throwing + * an [IllegalArgumentException] if the provided string is longer than 127 characters. We + * use the term "slice" instead of "section" to be consistent with Perfetto. + * + * # Avoiding malformed traces + * + * Improper usage of this API will lead to malformed traces with long slices that sometimes + * never end. This will look like the following: + * ``` + * Thread #1 | [===================================================================== ... + * | [==============] [====================================== ... + * | [=======] [======] [===================== ... + * | [=======] + * ``` + * + * To avoid this, [beginSlice] and [endSlice] should never be called from `suspend` blocks + * (instead, use [traceCoroutine] for tracing suspending functions). While it would be + * technically okay to call from a suspending function if that function were to only wrap + * non-suspending blocks with [beginSlice] and [endSlice], doing so is risky because suspend + * calls could be mistakenly added to that block as the code is refactored. + * + * Additionally, it is _not_ okay to call [beginSlice] when registering a callback and match + * it with a call to [endSlice] inside that callback, even if the callback runs on the same + * thread. Doing so would cause malformed traces because the [beginSlice] wasn't closed + * before the thread became idle and started running unrelated work. + * + * @param sliceName The name of the code section to appear in the trace + * @see endSlice + * @see traceCoroutine + */ + fun beginSlice(sliceName: String) { + Trace.traceBegin(Trace.TRACE_TAG_APP, sliceName) + } + + /** + * Writes a trace message to indicate that a given section of code has ended. This call must + * be preceded by a corresponding call to [beginSlice]. See [beginSlice] for important + * information regarding usage. + * + * @see beginSlice + * @see traceCoroutine + */ + fun endSlice() { + Trace.traceEnd(Trace.TRACE_TAG_APP) + } + + /** + * Writes a trace message indicating that an instant event occurred on the current thread. + * Unlike slices, instant events have no duration and do not need to be matched with another + * call. Perfetto will display instant events using an arrow pointing to the timestamp they + * occurred: + * ``` + * Thread #1 | [==============] [======] + * | [====] ^ + * | ^ + * ``` + * + * @param eventName The name of the event to appear in the trace. + */ + fun instant(eventName: String) { + Trace.instant(Trace.TRACE_TAG_APP, eventName) + } + + /** + * Writes a trace message indicating that an instant event occurred on the given track. + * Unlike slices, instant events have no duration and do not need to be matched with another + * call. Perfetto will display instant events using an arrow pointing to the timestamp they + * occurred: + * ``` + * Async | [==============] [======] + * Track | [====] ^ + * Name | ^ + * ``` + * + * @param trackName The track where the event should appear in the trace. + * @param eventName The name of the event to appear in the trace. + */ + fun instantForTrack(trackName: String, eventName: String) { + Trace.instantForTrack(Trace.TRACE_TAG_APP, trackName, eventName) } } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceContextElement.kt b/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceContextElement.kt new file mode 100644 index 0000000000000000000000000000000000000000..4d8c5450d880ec6d3e9850aeb96a03c9859506f0 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceContextElement.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.tracing + +import com.android.systemui.util.TraceUtils.Companion.instant +import com.android.systemui.util.TraceUtils.Companion.traceCoroutine +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CopyableThreadContextElement +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.ExperimentalCoroutinesApi + +/** + * Used for safely persisting [TraceData] state when coroutines are suspended and resumed. + * + * This is internal machinery for [traceCoroutine]. It cannot be made `internal` or `private` + * because [traceCoroutine] is a Public-API inline function. + * + * @see traceCoroutine + */ +@OptIn(DelicateCoroutinesApi::class) +@ExperimentalCoroutinesApi +class TraceContextElement(private val traceData: TraceData = TraceData()) : + CopyableThreadContextElement<TraceData?> { + + companion object Key : CoroutineContext.Key<TraceContextElement> + + override val key: CoroutineContext.Key<TraceContextElement> = Key + + @OptIn(ExperimentalStdlibApi::class) + override fun updateThreadContext(context: CoroutineContext): TraceData? { + val oldState = threadLocalTrace.get() + oldState?.endAllOnThread() + threadLocalTrace.set(traceData) + instant("resuming ${context[CoroutineDispatcher]}") + traceData.beginAllOnThread() + return oldState + } + + @OptIn(ExperimentalStdlibApi::class) + override fun restoreThreadContext(context: CoroutineContext, oldState: TraceData?) { + instant("suspending ${context[CoroutineDispatcher]}") + traceData.endAllOnThread() + threadLocalTrace.set(oldState) + oldState?.beginAllOnThread() + } + + override fun copyForChild(): CopyableThreadContextElement<TraceData?> { + return TraceContextElement(traceData.copy()) + } + + override fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext { + return TraceContextElement(traceData.copy()) + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceData.kt b/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceData.kt new file mode 100644 index 0000000000000000000000000000000000000000..0ae58fc2c45bc107416f175bce4c29c92eeb5cfb --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceData.kt @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.tracing + +import android.os.Build +import android.util.Log +import com.android.systemui.util.TraceUtils.Companion.beginSlice +import com.android.systemui.util.TraceUtils.Companion.endSlice +import com.android.systemui.util.TraceUtils.Companion.traceCoroutine +import kotlin.random.Random + +/** + * Used for giving each thread a unique [TraceData] for thread-local storage. `null` by default. + * [threadLocalTrace] can only be used when it is paired with a [TraceContextElement]. + * + * This ThreadLocal will be `null` if either 1) we aren't in a coroutine, or 2) the coroutine we are + * in does not have a [TraceContextElement]. + * + * This is internal machinery for [traceCoroutine]. It cannot be made `internal` or `private` + * because [traceCoroutine] is a Public-API inline function. + * + * @see traceCoroutine + */ +val threadLocalTrace = ThreadLocal<TraceData?>() + +/** + * Used for storing trace sections so that they can be added and removed from the currently running + * thread when the coroutine is suspended and resumed. + * + * This is internal machinery for [traceCoroutine]. It cannot be made `internal` or `private` + * because [traceCoroutine] is a Public-API inline function. + * + * @see traceCoroutine + */ +class TraceData { + private var slices = mutableListOf<TraceSection>() + + /** Adds current trace slices back to the current thread. Called when coroutine is resumed. */ + fun beginAllOnThread() { + slices.forEach { beginSlice(it.name) } + } + + /** + * Removes all current trace slices from the current thread. Called when coroutine is suspended. + */ + fun endAllOnThread() { + for (i in 0..slices.size) { + endSlice() + } + } + + /** + * Creates a new trace section with a unique ID and adds it to the current trace data. The slice + * will also be added to the current thread immediately. This slice will not propagate to parent + * coroutines, or to child coroutines that have already started. The unique ID is used to verify + * that the [endSpan] is corresponds to a [beginSpan]. + */ + fun beginSpan(name: String): Int { + val newSlice = TraceSection(name, Random.nextInt(FIRST_VALID_SPAN, Int.MAX_VALUE)) + slices.add(newSlice) + beginSlice(name) + return newSlice.id + } + + /** + * Used by [TraceContextElement] when launching a child coroutine so that the child coroutine's + * state is isolated from the parent. + */ + fun copy(): TraceData { + return TraceData().also { it.slices.addAll(slices) } + } + + /** + * Ends the trace section and validates it corresponds with an earlier call to [beginSpan]. The + * trace slice will immediately be removed from the current thread. This information will not + * propagate to parent coroutines, or to child coroutines that have already started. + */ + fun endSpan(id: Int) { + val v = slices.removeLast() + if (v.id != id) { + if (STRICT_MODE) { + throw IllegalArgumentException(errorMsg) + } else if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, errorMsg) + } + } + endSlice() + } + + companion object { + private const val TAG = "TraceData" + const val INVALID_SPAN = -1 + const val FIRST_VALID_SPAN = 1 + + /** + * If true, throw an exception instead of printing a warning when trace sections beginnings + * and ends are mismatched. + */ + private val STRICT_MODE = Build.IS_ENG + + private const val errorMsg = + "Mismatched trace section. This likely means you are accessing the trace local " + + "storage (threadLocalTrace) without a corresponding CopyableThreadContextElement." + + " This could happen if you are using a global dispatcher like Dispatchers.IO." + + " To fix this, use one of the coroutine contexts provided by the dagger scope " + + "(e.g. \"@Main CoroutineContext\")." + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceSection.kt b/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceSection.kt new file mode 100644 index 0000000000000000000000000000000000000000..b70c4977614a9704a5ab21e64f46c419ce315ca9 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceSection.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.tracing + +import com.android.systemui.util.TraceUtils.Companion.traceCoroutine + +/** + * Represents a section of code executing in a coroutine. This can be split up into multiple slices + * on different threads as the coroutine is suspended and resumed. + * + * This is internal machinery for [traceCoroutine]. It cannot be made `internal` or `private` + * because [traceCoroutine] is a Public-API inline function. + * + * @param name the name of the slice to appear on the current thread's track. + * @param id used for matching the beginning and end of trace sections and validating correctness + * @see traceCoroutine + */ +data class TraceSection( + val name: String, + val id: Int, +) diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt index b186018ba78a22d26a67ce9a0e087476178f1144..375727437b8b8bf78b603f3c82c558374b817a66 100644 --- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt +++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt @@ -318,9 +318,8 @@ class ActiveUnlockConfig @Inject constructor( keyguardUpdateMonitor?.let { val anyFaceEnrolled = it.isFaceEnrolled - val anyFingerprintEnrolled = - it.getCachedIsUnlockWithFingerprintPossible( - selectedUserInteractor.getSelectedUserId()) + val anyFingerprintEnrolled = it.isUnlockWithFingerprintPossible( + selectedUserInteractor.getSelectedUserId()) val udfpsEnrolled = it.isUdfpsEnrolled if (!anyFaceEnrolled && !anyFingerprintEnrolled) { @@ -374,9 +373,8 @@ class ActiveUnlockConfig @Inject constructor( pw.println(" shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" + "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}") pw.println(" faceEnrolled=${it.isFaceEnrolled}") - pw.println(" fpEnrolled=${ - it.getCachedIsUnlockWithFingerprintPossible( - selectedUserInteractor.getSelectedUserId())}") + pw.println(" fpUnlockPossible=${ + it.isUnlockWithFingerprintPossible(selectedUserInteractor.getSelectedUserId())}") pw.println(" udfpsEnrolled=${it.isUdfpsEnrolled}") } ?: pw.println(" keyguardUpdateMonitor is uninitialized") } diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java index 873c3d9105db853feebef046110fe72c5fd060c0..1cfa816f46122c412e987f6038222b7bd4e43fe3 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java @@ -39,10 +39,10 @@ import androidx.annotation.VisibleForTesting; import com.android.keyguard.logging.CarrierTextManagerLogger; import com.android.settingslib.WirelessUtils; -import com.android.systemui.res.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.res.R; import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository; import com.android.systemui.telephony.TelephonyListenerManager; @@ -612,36 +612,6 @@ public class CarrierTextManager { return list; } - private CharSequence getCarrierHelpTextForSimState(int simState, - String plmn, String spn) { - int carrierHelpTextId = 0; - CarrierTextManager.StatusMode status = getStatusForIccState(simState); - switch (status) { - case NetworkLocked: - carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled; - break; - - case SimMissing: - carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long; - break; - - case SimPermDisabled: - carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions; - break; - - case SimMissingLocked: - carrierHelpTextId = R.string.keyguard_missing_sim_instructions; - break; - - case Normal: - case SimLocked: - case SimPukLocked: - break; - } - - return mContext.getText(carrierHelpTextId); - } - /** Injectable Buildeer for {@#link CarrierTextManager}. */ public static class Builder { private final Context mContext; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 7101ed599b868482f7c5e7dd060845466b50e35f..1b6112f52082831e4510500e2b6d7f777955d4fa 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -755,7 +755,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } mView.onResume( mSecurityModel.getSecurityMode(mSelectedUserInteractor.getSelectedUserId()), - mKeyguardStateController.isFaceAuthEnabled()); + mKeyguardStateController.isFaceEnrolled()); } /** Sets an initial message that would override the default message */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java index 13f9d3e1038ef8d781e099e469454ad8305cb0f2..05fb5fa75e9ef55610f5cb9af5dceceed3e4288c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java @@ -246,7 +246,7 @@ public class KeyguardSimPukViewController private boolean checkPuk() { // make sure the puk is at least 8 digits long. - if (mPasswordEntry.getText().length() == 8) { + if (mPasswordEntry.getText().length() >= 8) { mPukText = mPasswordEntry.getText(); return true; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 7d6240b0dc2c045852c78b4b59711126da12b4b1..f19a9ed5546fca9831259a1b060fe0b27c6f65b2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -441,7 +441,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private int mFaceRunningState = BIOMETRIC_STATE_STOPPED; private boolean mIsDreaming; private boolean mLogoutEnabled; - private boolean mIsFaceEnrolled; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private int mPostureState = DEVICE_POSTURE_UNKNOWN; private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider; @@ -2083,7 +2082,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private boolean mFingerprintLockedOut; private boolean mFingerprintLockedOutPermanent; private boolean mFaceLockedOutPermanent; - private final HashMap<Integer, Boolean> mIsUnlockWithFingerprintPossible = new HashMap<>(); /** * When we receive a {@link android.content.Intent#ACTION_SIM_STATE_CHANGED} broadcast, @@ -2701,16 +2699,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private void updateFaceEnrolled(int userId) { - final Boolean isFaceEnrolled = isFaceSupported() - && mBiometricEnabledForUser.get(userId) - && mAuthController.isFaceAuthEnrolled(userId); - if (mIsFaceEnrolled != isFaceEnrolled) { - mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled); - } - mIsFaceEnrolled = isFaceEnrolled; - } - private boolean isFaceSupported() { return mFaceManager != null && !mFaceSensorProperties.isEmpty(); } @@ -2749,11 +2737,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mAuthController.isSfpsSupported(); } + /** + * @return true if there's at least one face enrolled for the given user. + */ + public boolean isFaceEnrolled(int userId) { + return mAuthController.isFaceAuthEnrolled(userId); + } + /** * @return true if there's at least one face enrolled */ public boolean isFaceEnrolled() { - return mIsFaceEnrolled; + return isFaceEnrolled(mSelectedUserInteractor.getSelectedUserId()); } private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @@ -3442,49 +3437,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } @SuppressLint("MissingPermission") - @VisibleForTesting - boolean isUnlockWithFingerprintPossible(int userId) { - // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once. - boolean newFpEnrolled = isFingerprintSupported() - && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId); - Boolean oldFpEnrolled = mIsUnlockWithFingerprintPossible.getOrDefault(userId, false); - if (oldFpEnrolled != newFpEnrolled) { - mLogger.logFpEnrolledUpdated(userId, oldFpEnrolled, newFpEnrolled); - } - mIsUnlockWithFingerprintPossible.put(userId, newFpEnrolled); - return mIsUnlockWithFingerprintPossible.get(userId); - } - - /** - * Cached value for whether fingerprint is enrolled and possible to use for authentication. - * Note: checking fingerprint enrollment directly with the AuthController requires an IPC. - */ - public boolean getCachedIsUnlockWithFingerprintPossible(int userId) { - return mIsUnlockWithFingerprintPossible.getOrDefault(userId, false); + public boolean isUnlockWithFingerprintPossible(int userId) { + return isFingerprintSupported() + && !isFingerprintDisabled(userId) && mAuthController.isFingerprintEnrolled(userId); } /** * @deprecated This is being migrated to use modern architecture. */ + @VisibleForTesting @Deprecated - private boolean isUnlockWithFacePossible(int userId) { + public boolean isUnlockWithFacePossible(int userId) { if (isFaceAuthInteractorEnabled()) { return getFaceAuthInteractor() != null && getFaceAuthInteractor().isFaceAuthEnabledAndEnrolled(); } - return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId); - } - - /** - * If face hardware is available, user has enrolled and enabled auth via setting. - * - * @deprecated This is being migrated to use modern architecture. - */ - @Deprecated - public boolean isFaceAuthEnabledForUser(int userId) { - // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once. - updateFaceEnrolled(userId); - return mIsFaceEnrolled; + return isFaceSupported() && isFaceEnrolled(userId) && !isFaceDisabled(userId); } private void notifyAboutEnrollmentChange(@BiometricAuthenticator.Modality int modality) { diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index fa07072b7fe1ccdc8f81a74bb93001bb3154e20a..5bf8d635f8ee862f8973f0de6043b2dec950c71c 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -660,19 +660,6 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { ) } - fun logFpEnrolledUpdated(userId: Int, oldValue: Boolean, newValue: Boolean) { - logBuffer.log( - TAG, - DEBUG, - { - int1 = userId - bool1 = oldValue - bool2 = newValue - }, - { "Fp enrolled state changed for userId: $int1 old: $bool1, new: $bool2" } - ) - } - fun logTrustUsuallyManagedUpdated( userId: Int, oldValue: Boolean, diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index 453a7a6d3536db6ae53a1ab14f485ba4e44f7dde..8ea867bbf3fccef11a3421a01d0bd997716784fe 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -28,6 +28,8 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository import com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.util.TraceUtils.Companion.async +import com.android.systemui.util.TraceUtils.Companion.withContext import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlin.math.max @@ -263,7 +265,7 @@ constructor( * not being throttled. */ private suspend fun refreshThrottling(): Long { - return withContext(backgroundDispatcher) { + return withContext("$TAG#refreshThrottling", backgroundDispatcher) { val failedAttemptCount = async { repository.getFailedAuthenticationAttemptCount() } val deadline = async { repository.getThrottlingEndTimestamp() } val remainingMs = max(0, deadline.await() - clock.elapsedRealtime()) @@ -311,6 +313,10 @@ constructor( DomainLayerAuthenticationMethodModel.Pattern } } + + companion object { + const val TAG = "AuthenticationInteractor" + } } /** Result of a user authentication attempt. */ diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt index 21578f491de7e5c2d9223a21f6917b85a58358b0..56dfa5ed337cf103abf4df112e0e4f2fb5fbbad0 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt @@ -415,7 +415,7 @@ constructor( /** Whether we want to wait to show the bouncer in case passive auth succeeds. */ private fun usePrimaryBouncerPassiveAuthDelay(): Boolean { val canRunFaceAuth = - keyguardStateController.isFaceAuthEnabled && + keyguardStateController.isFaceEnrolled && keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) && keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth() val canRunActiveUnlock = diff --git a/packages/SystemUI/src/com/android/systemui/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/complication/ComplicationLayoutEngine.java index 20b2494a6c295fbfee0cafad04519a06663eb9f4..f7b6b0f06a00648ced396a3c9411dca59808597f 100644 --- a/packages/SystemUI/src/com/android/systemui/complication/ComplicationLayoutEngine.java +++ b/packages/SystemUI/src/com/android/systemui/complication/ComplicationLayoutEngine.java @@ -652,8 +652,7 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll CrossFadeHelper.fadeOut( mLayout, mFadeOutDuration, - /* delay= */ 0, - /* endRunnable= */ null); + /* delay= */ 0); } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt index 93b00eef08b52a5f5dc523c3bad0abc29cfdf49e..dd5860484a556b95e9a57828589ecab4342ac201 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt @@ -17,13 +17,13 @@ package com.android.systemui.flags import android.util.Log -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.flags.ConditionalRestarter.Condition +import com.android.systemui.util.kotlin.UnflaggedApplication +import com.android.systemui.util.kotlin.UnflaggedBackground import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Named -import kotlinx.coroutines.CoroutineDispatcher +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow @@ -39,8 +39,8 @@ constructor( private val systemExitRestarter: SystemExitRestarter, private val conditions: Set<@JvmSuppressWildcards Condition>, @Named(RESTART_DELAY) private val restartDelaySec: Long, - @Application private val applicationScope: CoroutineScope, - @Background private val backgroundDispatcher: CoroutineDispatcher, + @UnflaggedApplication private val applicationScope: CoroutineScope, + @UnflaggedBackground private val backgroundDispatcher: CoroutineContext, ) : Restarter { private var pendingReason = "" diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 10fac4d328e02649cdd20e6fa9110f19f92c691b..8c81fbbaeb9d80162f67ddf3f75ee84504ef5924 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -118,7 +118,7 @@ object Flags { // TODO(b/292213543): Tracking Bug @JvmField val NOTIFICATION_GROUP_EXPANSION_CHANGE = - unreleasedFlag("notification_group_expansion_change", teamfood = true) + releasedFlag("notification_group_expansion_change") // TODO(b/301955929) @JvmField diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt index bc0713952ce1653c9fda73b9a39ab236518778bc..6f491d88dab4fdc3e56e2e4ccb320a78a623965e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt @@ -36,9 +36,9 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract +import com.android.systemui.util.TraceUtils.Companion.runBlocking import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.runBlocking class CustomizationProvider : ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer { @@ -132,7 +132,7 @@ class CustomizationProvider : throw UnsupportedOperationException() } - return runBlocking(mainDispatcher) { insertSelection(values) } + return runBlocking("$TAG#insert", mainDispatcher) { insertSelection(values) } } override fun query( @@ -142,7 +142,7 @@ class CustomizationProvider : selectionArgs: Array<out String>?, sortOrder: String?, ): Cursor? { - return runBlocking(mainDispatcher) { + return runBlocking("$TAG#query", mainDispatcher) { when (uriMatcher.match(uri)) { MATCH_CODE_ALL_AFFORDANCES -> queryAffordances() MATCH_CODE_ALL_SLOTS -> querySlots() @@ -172,7 +172,7 @@ class CustomizationProvider : throw UnsupportedOperationException() } - return runBlocking(mainDispatcher) { deleteSelection(uri, selectionArgs) } + return runBlocking("$TAG#delete", mainDispatcher) { deleteSelection(uri, selectionArgs) } } override fun call(method: String, arg: String?, extras: Bundle?): Bundle? { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index 3cdff76881eefa3576e47ffa1b00693fc90c2d46..6a0d5954fc44c65815f4553446bb2ab277846ea2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -56,6 +56,10 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.UserRepository import com.google.errorprone.annotations.CompileTimeConstant +import java.io.PrintWriter +import java.util.Arrays +import java.util.stream.Collectors +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -78,10 +82,6 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.io.PrintWriter -import java.util.Arrays -import java.util.stream.Collectors -import javax.inject.Inject /** * API to run face authentication and detection for device entry / on keyguard (as opposed to the @@ -368,10 +368,12 @@ constructor( return arrayOf( Pair( and( - displayStateInteractor.isDefaultDisplayOff, - keyguardTransitionInteractor.isFinishedInStateWhere( - KeyguardState::deviceIsAwakeInState), - ).isFalse(), + displayStateInteractor.isDefaultDisplayOff, + keyguardTransitionInteractor.isFinishedInStateWhere( + KeyguardState::deviceIsAwakeInState + ), + ) + .isFalse(), // this can happen if an app is requesting for screen off, the display can // turn off without wakefulness.isStartingToSleepOrAsleep calls "displayIsNotOffWhileFullyTransitionedToAwake", @@ -381,10 +383,7 @@ constructor( "isFaceAuthEnrolledAndEnabled" ), Pair(keyguardRepository.isKeyguardGoingAway.isFalse(), "keyguardNotGoingAway"), - Pair( - powerInteractor.isAsleep.isFalse(), - "deviceNotAsleep" - ), + Pair(powerInteractor.isAsleep.isFalse(), "deviceNotAsleep"), Pair( keyguardInteractor.isSecureCameraActive .isFalse() @@ -430,10 +429,15 @@ constructor( private val faceAuthCallback = object : FaceManager.AuthenticationCallback() { override fun onAuthenticationFailed() { - _authenticationStatus.value = FailedFaceAuthenticationStatus() _isAuthenticated.value = false faceAuthLogger.authenticationFailed() - onFaceAuthRequestCompleted() + if (!_isLockedOut.value) { + // onAuthenticationError gets invoked before onAuthenticationFailed when the + // last auth attempt locks out face authentication. + // Skip updating the authentication status in such a scenario. + _authenticationStatus.value = FailedFaceAuthenticationStatus() + onFaceAuthRequestCompleted() + } } override fun onAuthenticationAcquired(acquireInfo: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index d44a9d86a731762f665b03bc49bdf25a190b6874..95ac0d8a29997e245d279876dc5e3a6c90a251d3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -31,6 +31,7 @@ import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.data.repository.ShadeRepository +import com.android.systemui.util.TraceUtils.Companion.launch import com.android.systemui.util.kotlin.Utils.Companion.toQuad import com.android.systemui.util.kotlin.Utils.Companion.toTriple import com.android.systemui.util.kotlin.sample @@ -43,7 +44,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch @SysUISingleton class FromLockscreenTransitionInteractor @@ -136,7 +136,7 @@ constructor( private fun listenForLockscreenToDreaming() { val invalidFromStates = setOf(KeyguardState.AOD, KeyguardState.DOZING) - scope.launch { + scope.launch("$TAG#listenForLockscreenToDreaming") { keyguardInteractor.isAbleToDream .sample( combine( @@ -169,7 +169,7 @@ constructor( } private fun listenForLockscreenToPrimaryBouncer() { - scope.launch { + scope.launch("$TAG#listenForLockscreenToPrimaryBouncer") { keyguardInteractor.primaryBouncerShowing .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) .collect { pair -> @@ -184,7 +184,7 @@ constructor( } private fun listenForLockscreenToAlternateBouncer() { - scope.launch { + scope.launch("$TAG#listenForLockscreenToAlternateBouncer") { keyguardInteractor.alternateBouncerShowing .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) .collect { pair -> @@ -202,7 +202,7 @@ constructor( /* Starts transitions when manually dragging up the bouncer from the lockscreen. */ private fun listenForLockscreenToPrimaryBouncerDragging() { var transitionId: UUID? = null - scope.launch { + scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") { shadeRepository.shadeModel .sample( combine( @@ -287,7 +287,7 @@ constructor( return } - scope.launch { + scope.launch("$TAG#listenForLockscreenToGone") { keyguardInteractor.isKeyguardGoingAway .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) .collect { pair -> @@ -304,7 +304,7 @@ constructor( return } - scope.launch { + scope.launch("$TAG#listenForLockscreenToGoneDragging") { keyguardInteractor.isKeyguardGoingAway .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) .collect { pair -> @@ -317,7 +317,7 @@ constructor( } private fun listenForLockscreenToOccluded() { - scope.launch { + scope.launch("$TAG#listenForLockscreenToOccluded") { keyguardInteractor.isKeyguardOccluded .sample(transitionInteractor.startedKeyguardState, ::Pair) .collect { (isOccluded, keyguardState) -> @@ -329,7 +329,7 @@ constructor( } private fun listenForLockscreenToAodOrDozing() { - scope.launch { + scope.launch("$TAG#listenForLockscreenToAodOrDozing") { powerInteractor.isAsleep .sample( combine( @@ -375,6 +375,7 @@ constructor( } companion object { + const val TAG = "FromLockscreenTransitionInteractor" private val DEFAULT_DURATION = 400.milliseconds val TO_DREAMING_DURATION = 933.milliseconds val TO_OCCLUDED_DURATION = 450.milliseconds diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index de791aa23e226083f815fe443d517f6facda99e3..fe9370fd8ce9f9f828a4427ba6441e00c85f641f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -23,7 +23,6 @@ import android.content.Context import android.content.Intent import android.util.Log import com.android.internal.widget.LockPatternUtils -import com.android.systemui.res.R import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.animation.Expandable import com.android.systemui.dagger.SysUISingleton @@ -44,11 +43,12 @@ import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentati import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.util.TraceUtils.Companion.traceAsync +import com.android.systemui.util.TraceUtils.Companion.withContext import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -59,7 +59,6 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.withContext @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton @@ -417,10 +416,8 @@ constructor( } private suspend fun isFeatureDisabledByDevicePolicy(): Boolean = - traceAsync(TAG, "isFeatureDisabledByDevicePolicy") { - withContext(backgroundDispatcher) { - devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId) - } + withContext("$TAG#isFeatureDisabledByDevicePolicy", backgroundDispatcher) { + devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt index 565bf241a1946ffad95a3f56a19899911964c0be..baa07c14ae0494d888a5d155a5cf321f6c961899 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt @@ -95,7 +95,7 @@ fun Flow<Boolean>.logDiffsForTable( tableLogBuffer.logChange(columnPrefix, columnName, initialValue, isInitial = true) initialValue } - return this.pairwiseBy(initialValueFun) { prevVal, newVal: Boolean -> + return this.pairwiseBy(initialValueFun) { prevVal: Boolean, newVal: Boolean -> if (prevVal != newVal) { tableLogBuffer.logChange(columnPrefix, columnName, newVal) } @@ -114,7 +114,7 @@ fun Flow<Int>.logDiffsForTable( tableLogBuffer.logChange(columnPrefix, columnName, initialValue, isInitial = true) initialValue } - return this.pairwiseBy(initialValueFun) { prevVal, newVal: Int -> + return this.pairwiseBy(initialValueFun) { prevVal: Int, newVal: Int -> if (prevVal != newVal) { tableLogBuffer.logChange(columnPrefix, columnName, newVal) } @@ -133,7 +133,7 @@ fun Flow<Int?>.logDiffsForTable( tableLogBuffer.logChange(columnPrefix, columnName, initialValue, isInitial = true) initialValue } - return this.pairwiseBy(initialValueFun) { prevVal, newVal: Int? -> + return this.pairwiseBy(initialValueFun) { prevVal: Int?, newVal: Int? -> if (prevVal != newVal) { tableLogBuffer.logChange(columnPrefix, columnName, newVal) } @@ -152,7 +152,7 @@ fun Flow<String?>.logDiffsForTable( tableLogBuffer.logChange(columnPrefix, columnName, initialValue, isInitial = true) initialValue } - return this.pairwiseBy(initialValueFun) { prevVal, newVal: String? -> + return this.pairwiseBy(initialValueFun) { prevVal: String?, newVal: String? -> if (prevVal != newVal) { tableLogBuffer.logChange(columnPrefix, columnName, newVal) } @@ -176,7 +176,7 @@ fun <T> Flow<List<T>>.logDiffsForTable( ) initialValue } - return this.pairwiseBy(initialValueFun) { prevVal, newVal: List<T> -> + return this.pairwiseBy(initialValueFun) { prevVal: List<T>, newVal: List<T> -> if (prevVal != newVal) { // TODO(b/267761156): Can we log list changes without using toString? tableLogBuffer.logChange(columnPrefix, columnName, newVal.toString()) diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 051eeb0cfaf1e229aa4c747c470c453ade111323..bd13d0686462bc256232641c54eaa175c2d2a2a5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -48,6 +48,7 @@ import android.widget.Switch; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.animation.ActivityLaunchAnimator; @@ -538,12 +539,17 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener, Log.i(TAG, "Launching activity before click"); } else { Log.i(TAG, "The activity is starting"); - ActivityLaunchAnimator.Controller controller = mViewClicked == null - ? null - : ActivityLaunchAnimator.Controller.fromView(mViewClicked, 0); - mUiHandler.post(() -> - mActivityStarter.startPendingIntentDismissingKeyguard( - pendingIntent, null, controller) + + ActivityLaunchAnimator.Controller controller = + mViewClicked == null ? null : + ActivityLaunchAnimator.Controller.fromView( + mViewClicked, + InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE + ); + mActivityStarter.startPendingIntentMaybeDismissingKeyguard( + pendingIntent, + /* intentSentUiThreadCallback= */ null, + controller ); } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt index f0650d34fc9db06bd19f49ee6e1b5052700626d4..9ba02b1aa9a86064a46d839ee60dc7d784342781 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt @@ -17,6 +17,8 @@ package com.android.systemui.scene.shared.flag import androidx.annotation.VisibleForTesting +import com.android.systemui.FeatureFlags +import com.android.systemui.Flags as AConfigFlags import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlagsClassic @@ -47,15 +49,15 @@ interface SceneContainerFlags { class SceneContainerFlagsImpl @AssistedInject constructor( - private val featureFlags: FeatureFlagsClassic, + private val featureFlagsClassic: FeatureFlagsClassic, + featureFlags: FeatureFlags, @Assisted private val isComposeAvailable: Boolean, ) : SceneContainerFlags { companion object { @VisibleForTesting - val flags: List<Flag<Boolean>> = + val classicFlagTokens: List<Flag<Boolean>> = listOf( - Flags.SCENE_CONTAINER, Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, Flags.MIGRATE_LOCK_ICON, Flags.MIGRATE_NSSL, @@ -67,7 +69,13 @@ constructor( /** The list of requirements, all must be met for the feature to be enabled. */ private val requirements = - flags.map { FlagMustBeEnabled(it) } + + listOf( + AconfigFlagMustBeEnabled( + flagName = AConfigFlags.FLAG_SCENE_CONTAINER, + flagValue = featureFlags.sceneContainer(), + ), + ) + + classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } + listOf(ComposeMustBeAvailable(), CompileTimeFlagMustBeEnabled()) override fun isEnabled(): Boolean { @@ -115,14 +123,25 @@ constructor( override fun isMet(): Boolean { return when (flag) { - is ResourceBooleanFlag -> featureFlags.isEnabled(flag) - is ReleasedFlag -> featureFlags.isEnabled(flag) - is UnreleasedFlag -> featureFlags.isEnabled(flag) + is ResourceBooleanFlag -> featureFlagsClassic.isEnabled(flag) + is ReleasedFlag -> featureFlagsClassic.isEnabled(flag) + is UnreleasedFlag -> featureFlagsClassic.isEnabled(flag) else -> error("Unsupported flag type ${flag.javaClass}") } } } + private inner class AconfigFlagMustBeEnabled( + flagName: String, + private val flagValue: Boolean, + ) : Requirement { + override val name: String = "Aconfig flag $flagName must be enabled" + + override fun isMet(): Boolean { + return flagValue + } + } + @AssistedFactory interface Factory { fun create(isComposeAvailable: Boolean): SceneContainerFlagsImpl diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt index aa6bfc3473d2d8628652a37ea3ef23d15bd27fe1..10d5f597105a9aee449ee2ddc42b8cb7f8c0e95f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt @@ -34,11 +34,11 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.settings.DisplayTracker +import com.android.systemui.util.TraceUtils.Companion.launch import javax.inject.Inject import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @SysUISingleton @@ -63,7 +63,9 @@ constructor( user: UserHandle, overrideTransition: Boolean, ) { - applicationScope.launch { launchIntent(intent, options, user, overrideTransition) } + applicationScope.launch("$TAG#launchIntentAsync") { + launchIntent(intent, options, user, overrideTransition) + } } suspend fun launchIntent( diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt index c34fd42e2154d1db9f179b69896bbb737934443b..f1c74c1bcff60b0ec5f56e87ac65e9c01cc45e99 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt @@ -20,10 +20,10 @@ import android.util.Log import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.util.TraceUtils.Companion.launch +import kotlinx.coroutines.CoroutineScope import java.util.function.Consumer import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch /** Processes a screenshot request sent from [ScreenshotHelper]. */ interface ScreenshotRequestProcessor { @@ -88,7 +88,7 @@ constructor( * thread */ fun processAsync(screenshot: ScreenshotData, callback: Consumer<ScreenshotData>) { - mainScope.launch { + mainScope.launch({ "$TAG#processAsync" }) { val result = process(screenshot) callback.accept(result) } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt index 14e875d28f8f96f6f23030d13478c828ae19c127..d2e47946441b36e30b9e1f250cb1ef57593badcf 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt @@ -25,7 +25,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.shade.ShadeExpansionStateManager import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.launch +import com.android.systemui.util.TraceUtils.Companion.launch import kotlinx.coroutines.withContext /** Provides state from the main SystemUI process on behalf of the Screenshot process. */ @@ -47,7 +47,9 @@ constructor( } override fun dismissKeyguard(callback: IOnDoneCallback) { - lifecycleScope.launch { executeAfterDismissing(callback) } + lifecycleScope.launch("IScreenshotProxy#dismissKeyguard") { + executeAfterDismissing(callback) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt index cd0cab556c4d090083fda3a6c8f90dbba4aae88a..1eae1918c4c7aab945a4c1457f0ff1e34056ebc5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt @@ -20,7 +20,7 @@ import android.media.MediaPlayer import android.util.Log import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.util.TraceUtils.Companion.tracedAsync +import com.android.systemui.util.TraceUtils.Companion.async import com.google.errorprone.annotations.CanIgnoreReturnValue import javax.inject.Inject import kotlin.time.Duration.Companion.seconds @@ -48,7 +48,7 @@ constructor( ) : ScreenshotSoundController { val player: Deferred<MediaPlayer?> = - coroutineScope.tracedAsync("loadCameraSound", bgDispatcher) { + coroutineScope.async("loadCameraSound", bgDispatcher) { try { soundProvider.getScreenshotSound() } catch (e: IllegalStateException) { @@ -58,12 +58,10 @@ constructor( } override fun playCameraSound(): Deferred<Unit> { - return coroutineScope.tracedAsync("playCameraSound", bgDispatcher) { - player.await()?.start() - } + return coroutineScope.async("playCameraSound", bgDispatcher) { player.await()?.start() } } override fun releaseScreenshotSound(): Deferred<Unit> { - return coroutineScope.tracedAsync("releaseScreenshotSound", bgDispatcher) { + return coroutineScope.async("releaseScreenshotSound", bgDispatcher) { try { withTimeout(1.seconds) { player.await()?.release() } } catch (e: TimeoutCancellationException) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt index 25ee8d80d4f55aea62849424fb201c4fd433b01c..5684605601c9d38daa305239a72c40e413f64d2a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt @@ -13,11 +13,11 @@ import com.android.systemui.display.data.repository.DisplayRepository import com.android.systemui.res.R import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback +import com.android.systemui.util.TraceUtils.Companion.launch import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch /** * Receives the signal to take a screenshot from [TakeScreenshotService], and calls back with the @@ -176,7 +176,7 @@ constructor( onSaved: Consumer<Uri>, requestCallback: RequestCallback ) { - mainScope.launch { + mainScope.launch("TakeScreenshotService#executeScreenshotsAsync") { executeScreenshots(screenshotRequest, { uri -> onSaved.accept(uri) }, requestCallback) } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 3d3447b921fc37a5c2504ad9f368b71336adf9b5..a2627eddf05de1e896df0ed62e0092716f3c1140 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -65,7 +65,7 @@ import com.android.systemui.statusbar.NotificationInsetsController; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository; +import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; @@ -182,7 +182,7 @@ public class NotificationShadeWindowViewController implements Dumpable { PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, CommunalViewModel communalViewModel, CommunalRepository communalRepository, - NotificationExpansionRepository notificationExpansionRepository, + NotificationLaunchAnimationInteractor notificationLaunchAnimationInteractor, FeatureFlagsClassic featureFlagsClassic, SystemClock clock, BouncerMessageInteractor bouncerMessageInteractor, @@ -239,7 +239,7 @@ public class NotificationShadeWindowViewController implements Dumpable { mLockscreenToDreamingTransition); collectFlow( mView, - notificationExpansionRepository.isExpandAnimationRunning(), + notificationLaunchAnimationInteractor.isLaunchAnimationRunning(), this::setExpandAnimationRunning); mClock = clock; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java index 77b095802b00e8d5becda654dd59e3be43f04376..7d81e55d336a2ff06a21e5bd5be210180e264017 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java @@ -16,7 +16,9 @@ package com.android.systemui.statusbar; +import android.animation.Animator; import android.view.View; +import android.view.ViewPropertyAnimator; import androidx.annotation.Nullable; @@ -31,32 +33,58 @@ public class CrossFadeHelper { public static final long ANIMATION_DURATION_LENGTH = 210; public static void fadeOut(final View view) { - fadeOut(view, null); + fadeOut(view, (Runnable) null); } public static void fadeOut(final View view, final Runnable endRunnable) { fadeOut(view, ANIMATION_DURATION_LENGTH, 0, endRunnable); } + public static void fadeOut(final View view, final Animator.AnimatorListener listener) { + fadeOut(view, ANIMATION_DURATION_LENGTH, 0, listener); + } + + public static void fadeOut(final View view, long duration, int delay) { + fadeOut(view, duration, delay, (Runnable) null); + } + public static void fadeOut(final View view, long duration, int delay, - final Runnable endRunnable) { + @Nullable final Runnable endRunnable) { view.animate().cancel(); view.animate() .alpha(0f) .setDuration(duration) .setInterpolator(Interpolators.ALPHA_OUT) .setStartDelay(delay) - .withEndAction(new Runnable() { - @Override - public void run() { - if (endRunnable != null) { - endRunnable.run(); - } - if (view.getVisibility() != View.GONE) { - view.setVisibility(View.INVISIBLE); - } + .withEndAction(() -> { + if (endRunnable != null) { + endRunnable.run(); + } + if (view.getVisibility() != View.GONE) { + view.setVisibility(View.INVISIBLE); + } + }); + if (view.hasOverlappingRendering()) { + view.animate().withLayer(); + } + } + + public static void fadeOut(final View view, long duration, int delay, + @Nullable final Animator.AnimatorListener listener) { + view.animate().cancel(); + ViewPropertyAnimator animator = view.animate() + .alpha(0f) + .setDuration(duration) + .setInterpolator(Interpolators.ALPHA_OUT) + .setStartDelay(delay) + .withEndAction(() -> { + if (view.getVisibility() != View.GONE) { + view.setVisibility(View.INVISIBLE); } }); + if (listener != null) { + animator.setListener(listener); + } if (view.hasOverlappingRendering()) { view.animate().withLayer(); } @@ -119,8 +147,12 @@ public class CrossFadeHelper { fadeIn(view, ANIMATION_DURATION_LENGTH, /* delay= */ 0, endRunnable); } + public static void fadeIn(final View view, Animator.AnimatorListener listener) { + fadeIn(view, ANIMATION_DURATION_LENGTH, /* delay= */ 0, listener); + } + public static void fadeIn(final View view, long duration, int delay) { - fadeIn(view, duration, delay, /* endRunnable= */ null); + fadeIn(view, duration, delay, /* endRunnable= */ (Runnable) null); } public static void fadeIn(final View view, long duration, int delay, @@ -141,6 +173,26 @@ public class CrossFadeHelper { } } + public static void fadeIn(final View view, long duration, int delay, + @Nullable Animator.AnimatorListener listener) { + view.animate().cancel(); + if (view.getVisibility() == View.INVISIBLE) { + view.setAlpha(0.0f); + view.setVisibility(View.VISIBLE); + } + ViewPropertyAnimator animator = view.animate() + .alpha(1f) + .setDuration(duration) + .setStartDelay(delay) + .setInterpolator(Interpolators.ALPHA_IN); + if (listener != null) { + animator.setListener(listener); + } + if (view.hasOverlappingRendering() && view.getLayerType() != View.LAYER_TYPE_HARDWARE) { + view.animate().withLayer(); + } + } + public static void fadeIn(View view, float fadeInAmount) { fadeIn(view, fadeInAmount, false /* remap */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 2f1b589899a0295a9bb0280d968fc29b6c3325f2..dd24ca78005f41f6a5b501d9819b2548173715d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -1457,7 +1457,7 @@ public class KeyguardIndicationController { } private boolean canUnlockWithFingerprint() { - return mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( + return mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible( getCurrentUser()) && mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java index ff4570ea0fc5ead2b39469ddaf3bb1c2a26c00bb..2e1e395518a00be9b9149b7a9b2b988e4cd50e8b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -29,8 +29,11 @@ import android.util.Log; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.flags.FeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.PluginManager; import com.android.systemui.statusbar.dagger.CentralSurfacesModule; +import com.android.systemui.statusbar.domain.interactor.SilentNotificationStatusIconsVisibilityInteractor; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.PipelineDumpable; import com.android.systemui.statusbar.notification.collection.PipelineDumper; @@ -59,7 +62,9 @@ public class NotificationListener extends NotificationListenerWithPlugins implem private static final long MAX_RANKING_DELAY_MILLIS = 500L; private final Context mContext; + private final FeatureFlagsClassic mFeatureFlags; private final NotificationManager mNotificationManager; + private final SilentNotificationStatusIconsVisibilityInteractor mStatusIconInteractor; private final SystemClock mSystemClock; private final Executor mMainExecutor; private final List<NotificationHandler> mNotificationHandlers = new ArrayList<>(); @@ -75,13 +80,17 @@ public class NotificationListener extends NotificationListenerWithPlugins implem @Inject public NotificationListener( Context context, + FeatureFlagsClassic featureFlags, NotificationManager notificationManager, + SilentNotificationStatusIconsVisibilityInteractor statusIconInteractor, SystemClock systemClock, @Main Executor mainExecutor, PluginManager pluginManager) { super(pluginManager); mContext = context; + mFeatureFlags = featureFlags; mNotificationManager = notificationManager; + mStatusIconInteractor = statusIconInteractor; mSystemClock = systemClock; mMainExecutor = mainExecutor; } @@ -95,6 +104,7 @@ public class NotificationListener extends NotificationListenerWithPlugins implem } /** Registers a listener that's notified when any notification-related settings change. */ + @Deprecated public void addNotificationSettingsListener(NotificationSettingsListener listener) { mSettingsListeners.add(listener); } @@ -230,8 +240,12 @@ public class NotificationListener extends NotificationListenerWithPlugins implem @Override public void onSilentStatusBarIconsVisibilityChanged(boolean hideSilentStatusIcons) { - for (NotificationSettingsListener listener : mSettingsListeners) { - listener.onStatusBarIconsBehaviorChanged(hideSilentStatusIcons); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mStatusIconInteractor.setHideSilentStatusIcons(hideSilentStatusIcons); + } else { + for (NotificationSettingsListener listener : mSettingsListeners) { + listener.onStatusBarIconsBehaviorChanged(hideSilentStatusIcons); + } } } @@ -294,6 +308,7 @@ public class NotificationListener extends NotificationListenerWithPlugins implem return ranking; } + @Deprecated public interface NotificationSettingsListener { default void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) { } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..2c706a5327f28c4420dce2fc721cae3eaa7e3f97 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepository.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 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.data.repository + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.NotificationListener +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow + +/** Exposes state pertaining to settings tracked over the [NotificationListener] boundary. */ +@SysUISingleton +class NotificationListenerSettingsRepository @Inject constructor() { + /** Should icons for silent notifications be shown in the status bar? */ + val showSilentStatusIcons = MutableStateFlow(true) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/SilentNotificationStatusIconsVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/SilentNotificationStatusIconsVisibilityInteractor.kt new file mode 100644 index 0000000000000000000000000000000000000000..1248b1c1cbb09073f38014d9e018cd0a8178acce --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/SilentNotificationStatusIconsVisibilityInteractor.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 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.domain.interactor + +import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository +import javax.inject.Inject + +class SilentNotificationStatusIconsVisibilityInteractor +@Inject +constructor(private val repository: NotificationListenerSettingsRepository) { + /** Set whether icons for silent notifications be hidden in the status bar. */ + fun setHideSilentStatusIcons(hideIcons: Boolean) { + repository.showSilentStatusIcons.value = !hideIcons + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt index 756151bd57e0c03ce4b834ce2fe8073e92aa82ac..96279e2d2e449dffb227f4d28022afdd517fae5f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt @@ -21,7 +21,7 @@ import android.view.ViewGroup import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.LaunchAnimator -import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository +import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.policy.HeadsUpManager @@ -33,7 +33,7 @@ private const val TAG = "NotificationLaunchAnimatorController" /** A provider of [NotificationLaunchAnimatorController]. */ class NotificationLaunchAnimatorControllerProvider( - private val notificationExpansionRepository: NotificationExpansionRepository, + private val notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor, private val notificationListContainer: NotificationListContainer, private val headsUpManager: HeadsUpManager, private val jankMonitor: InteractionJankMonitor @@ -44,7 +44,7 @@ class NotificationLaunchAnimatorControllerProvider( onFinishAnimationCallback: Runnable? = null ): NotificationLaunchAnimatorController { return NotificationLaunchAnimatorController( - notificationExpansionRepository, + notificationLaunchAnimationInteractor, notificationListContainer, headsUpManager, notification, @@ -60,7 +60,7 @@ class NotificationLaunchAnimatorControllerProvider( * notification expanding into an opening window. */ class NotificationLaunchAnimatorController( - private val notificationExpansionRepository: NotificationExpansionRepository, + private val notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor, private val notificationListContainer: NotificationListContainer, private val headsUpManager: HeadsUpManager, private val notification: ExpandableNotificationRow, @@ -143,7 +143,7 @@ class NotificationLaunchAnimatorController( if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) { Log.d(TAG, "onIntentStarted(willAnimate=$willAnimate)") } - notificationExpansionRepository.setIsExpandAnimationRunning(willAnimate) + notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(willAnimate) notificationEntry.isExpandAnimationRunning = willAnimate if (!willAnimate) { @@ -180,7 +180,7 @@ class NotificationLaunchAnimatorController( // TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started // here? - notificationExpansionRepository.setIsExpandAnimationRunning(false) + notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(false) notificationEntry.isExpandAnimationRunning = false removeHun(animate = true) onFinishAnimationCallback?.run() @@ -200,7 +200,7 @@ class NotificationLaunchAnimatorController( jankMonitor.end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START) notification.isExpandAnimationRunning = false - notificationExpansionRepository.setIsExpandAnimationRunning(false) + notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(false) notificationEntry.isExpandAnimationRunning = false notificationListContainer.setExpandingNotification(null) applyParams(null) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index e763797d99669ecfe826f20fcd578b742a9a123d..7b3a93a4a094618da86a134a0ba67bc1bd203a26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -225,7 +225,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable { /** @see NotifPipeline#getEntry(String) () */ @Nullable - NotificationEntry getEntry(@NonNull String key) { + public NotificationEntry getEntry(@NonNull String key) { return mNotificationSet.get(key); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt index c2a021d0803f10df14cfd39520f60171bbedc723..07e84bb37fa04032be1ba49bbfb244fd6471921d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt @@ -52,9 +52,10 @@ internal constructor( fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) = traceSection("StackCoordinator.onAfterRenderList") { controller.setNotifStats(calculateNotifStats(entries)) - notificationIconAreaController.updateNotificationIcons(entries) if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { renderListInteractor.setRenderedList(entries) + } else { + notificationIconAreaController.updateNotificationIcons(entries) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 8561869af352f29e015b459f159bd4c7126f14c5..fa366c65b4a3b1942aa7458ce2c4ad3ffbe68fec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -54,7 +54,7 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.data.NotificationDataLayerModule; -import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository; +import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor; import com.android.systemui.statusbar.notification.icon.ConversationIconManager; import com.android.systemui.statusbar.notification.icon.IconManager; import com.android.systemui.statusbar.notification.init.NotificationsController; @@ -204,12 +204,12 @@ public interface NotificationsModule { @Provides @SysUISingleton static NotificationLaunchAnimatorControllerProvider provideNotifLaunchAnimControllerProvider( - NotificationExpansionRepository notificationExpansionRepository, + NotificationLaunchAnimationInteractor notificationLaunchAnimationInteractor, NotificationListContainer notificationListContainer, HeadsUpManager headsUpManager, InteractionJankMonitor jankMonitor) { return new NotificationLaunchAnimatorControllerProvider( - notificationExpansionRepository, + notificationLaunchAnimationInteractor, notificationListContainer, headsUpManager, jankMonitor); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..afed6bef58fc8868876bdc0cff0be8afb36eb7c1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepository.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 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.data.repository + +import android.graphics.Rect +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow + +/** View-states pertaining to heads-up notification icons. */ +@SysUISingleton +class HeadsUpNotificationIconViewStateRepository @Inject constructor() { + /** Notification key for a notification icon to show isolated, or `null` if none. */ + val isolatedNotification = MutableStateFlow<String?>(null) + /** Area to display the isolated notification, or `null` if none. */ + val isolatedIconLocation = MutableStateFlow<Rect?>(null) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationLaunchAnimationRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationLaunchAnimationRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..9b562991f9a333268032283b41184bc3b8658dc0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationLaunchAnimationRepository.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 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.data.repository + +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow + +/** A repository tracking the status of notification launch animations. */ +@SysUISingleton +class NotificationLaunchAnimationRepository @Inject constructor() { + val isLaunchAnimationRunning = MutableStateFlow(false) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractor.kt new file mode 100644 index 0000000000000000000000000000000000000000..17b6e9f572c9e0b193eb132bca35894c3f2c439a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractor.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 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.domain.interactor + +import android.graphics.Rect +import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationIconViewStateRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Domain logic pertaining to heads up notification icons. */ +class HeadsUpNotificationIconInteractor +@Inject +constructor( + private val repository: HeadsUpNotificationIconViewStateRepository, +) { + /** Notification key for a notification icon to show isolated, or `null` if none. */ + val isolatedIconLocation: Flow<Rect?> = repository.isolatedIconLocation + + /** Area to display the isolated notification, or `null` if none. */ + val isolatedNotification: Flow<String?> = repository.isolatedNotification + + /** Updates the location where isolated notification icons are shown. */ + fun setIsolatedIconLocation(rect: Rect?) { + repository.isolatedIconLocation.value = rect + } + + /** Updates which notification will have its icon displayed isolated. */ + fun setIsolatedIconNotificationKey(key: String?) { + repository.isolatedNotification.value = key + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt similarity index 59% rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt index 6f0a97adb3116e40d9b9b9a69065e781a4a2a611..22ce4f11b6615589d9257077fe408a9433f9e733 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt @@ -14,22 +14,20 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification.data.repository +package com.android.systemui.statusbar.notification.domain.interactor import android.util.Log import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow - -private const val TAG = "NotificationExpansionRepository" +import kotlinx.coroutines.flow.StateFlow /** A repository tracking the status of notification expansion animations. */ @SysUISingleton -class NotificationExpansionRepository @Inject constructor() { - private val _isExpandAnimationRunning = MutableStateFlow(false) +class NotificationLaunchAnimationInteractor +@Inject +constructor(private val repository: NotificationLaunchAnimationRepository) { /** * Emits true if an animation that expands a notification object into an opening window is @@ -37,13 +35,18 @@ class NotificationExpansionRepository @Inject constructor() { * * See [com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController]. */ - val isExpandAnimationRunning: Flow<Boolean> = _isExpandAnimationRunning.asStateFlow() + val isLaunchAnimationRunning: StateFlow<Boolean> + get() = repository.isLaunchAnimationRunning - /** Sets whether the notification expansion animation is currently running. */ - fun setIsExpandAnimationRunning(running: Boolean) { + /** Sets whether the notification expansion launch animation is currently running. */ + fun setIsLaunchAnimationRunning(running: Boolean) { if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) { - Log.d(TAG, "setIsExpandAnimationRunning(running=$running)") + Log.d(TAG, "setIsLaunchAnimationRunning(running=$running)") } - _isExpandAnimationRunning.value = running + repository.isLaunchAnimationRunning.value = running + } + + companion object { + private const val TAG = "NotificationLaunchAnimationInteractor" } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt index c5396ddbe565a6c7a334aeac628463ff5f87eed7..604ecbc047ff50cb0609739f1c0b13d2d3e864fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt @@ -15,12 +15,16 @@ */ package com.android.systemui.statusbar.notification.domain.interactor +import android.graphics.drawable.Icon import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import javax.inject.Inject import kotlinx.coroutines.flow.update +private typealias ModelStore = Map<String, ActiveNotificationModel> + /** * Logic for passing information from the * [com.android.systemui.statusbar.notification.collection.NotifPipeline] to the presentation @@ -30,6 +34,7 @@ class RenderNotificationListInteractor @Inject constructor( private val repository: ActiveNotificationListRepository, + private val sectionStyleProvider: SectionStyleProvider, ) { /** * Sets the current list of rendered notification entries as displayed in the notification @@ -38,21 +43,100 @@ constructor( * @see com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository.activeNotifications */ fun setRenderedList(entries: List<ListEntry>) { - repository.activeNotifications.update { modelsByKey -> + repository.activeNotifications.update { existingModels -> entries.associateBy( keySelector = { it.key }, - valueTransform = { it.toModel(modelsByKey[it.key]) } + valueTransform = { it.toModel(existingModels) }, + ) + } + } + + private fun ListEntry.toModel( + existingModels: ModelStore, + ): ActiveNotificationModel = + existingModels.createOrReuse( + key = key, + groupKey = representativeEntry?.sbn?.groupKey, + isAmbient = sectionStyleProvider.isMinimized(this), + isRowDismissed = representativeEntry?.isRowDismissed == true, + isSilent = sectionStyleProvider.isSilent(this), + isLastMessageFromReply = representativeEntry?.isLastMessageFromReply == true, + isSuppressedFromStatusBar = representativeEntry?.shouldSuppressStatusBar() == true, + isPulsing = representativeEntry?.showingPulsing() == true, + aodIcon = representativeEntry?.icons?.aodIcon?.sourceIcon, + shelfIcon = representativeEntry?.icons?.shelfIcon?.sourceIcon, + statusBarIcon = representativeEntry?.icons?.statusBarIcon?.sourceIcon, + ) + + private fun ModelStore.createOrReuse( + key: String, + groupKey: String?, + isAmbient: Boolean, + isRowDismissed: Boolean, + isSilent: Boolean, + isLastMessageFromReply: Boolean, + isSuppressedFromStatusBar: Boolean, + isPulsing: Boolean, + aodIcon: Icon?, + shelfIcon: Icon?, + statusBarIcon: Icon? + ): ActiveNotificationModel { + return this[key]?.takeIf { + it.isCurrent( + key = key, + groupKey = groupKey, + isAmbient = isAmbient, + isRowDismissed = isRowDismissed, + isSilent = isSilent, + isLastMessageFromReply = isLastMessageFromReply, + isSuppressedFromStatusBar = isSuppressedFromStatusBar, + isPulsing = isPulsing, + aodIcon = aodIcon, + shelfIcon = shelfIcon, + statusBarIcon = statusBarIcon ) } + ?: ActiveNotificationModel( + key = key, + groupKey = groupKey, + isAmbient = isAmbient, + isRowDismissed = isRowDismissed, + isSilent = isSilent, + isLastMessageFromReply = isLastMessageFromReply, + isSuppressedFromStatusBar = isSuppressedFromStatusBar, + isPulsing = isPulsing, + aodIcon = aodIcon, + shelfIcon = shelfIcon, + statusBarIcon = statusBarIcon, + ) } - private fun ListEntry.toModel(existing: ActiveNotificationModel?): ActiveNotificationModel { - val isCurrent = - when { - existing == null -> false - key == existing.key -> true - else -> false - } - return if (isCurrent) existing!! else ActiveNotificationModel(key = key) + private fun ActiveNotificationModel.isCurrent( + key: String, + groupKey: String?, + isAmbient: Boolean, + isRowDismissed: Boolean, + isSilent: Boolean, + isLastMessageFromReply: Boolean, + isSuppressedFromStatusBar: Boolean, + isPulsing: Boolean, + aodIcon: Icon?, + shelfIcon: Icon?, + statusBarIcon: Icon? + ): Boolean { + return when { + key != this.key -> false + groupKey != this.groupKey -> false + isAmbient != this.isAmbient -> false + isRowDismissed != this.isRowDismissed -> false + isSilent != this.isSilent -> false + isLastMessageFromReply != this.isLastMessageFromReply -> false + isSuppressedFromStatusBar != this.isSuppressedFromStatusBar -> false + isPulsing != this.isPulsing -> false + aodIcon != this.aodIcon -> false + shelfIcon != this.shelfIcon -> false + statusBarIcon != this.statusBarIcon -> false + else -> true + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt index f3e122ce690b5ccdbcf928d3a02d63976698a192..1f7ab962c3f2d487e30ff362eeccb968ad9a78cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt @@ -32,6 +32,7 @@ constructor( val hasFilteredOutSeenNotifications: StateFlow<Boolean> = notificationListRepository.hasFilteredOutSeenNotifications + /** Set whether already-seen notifications are currently filtered out of the shade. */ fun setHasFilteredOutSeenNotifications(value: Boolean) { notificationListRepository.hasFilteredOutSeenNotifications.value = value } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java index e74b3fcdf050177e0036ac4da925d7c22634170b..ba916542fa67d58e5350cb03224abcb1b7ceaa0a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.notification.footer.ui.view; import static android.graphics.PorterDuff.Mode.SRC_ATOP; import android.annotation.ColorInt; +import android.annotation.DrawableRes; +import android.annotation.StringRes; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; @@ -35,6 +38,7 @@ import androidx.annotation.NonNull; import com.android.settingslib.Utils; import com.android.systemui.res.R; +import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.row.FooterViewButton; import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; @@ -44,6 +48,8 @@ import com.android.systemui.util.DumpUtilsKt; import java.io.PrintWriter; public class FooterView extends StackScrollerDecorView { + private static final String TAG = "FooterView"; + private FooterViewButton mClearAllButton; private FooterViewButton mManageButton; private boolean mShowHistory; @@ -57,6 +63,9 @@ public class FooterView extends StackScrollerDecorView { private String mSeenNotifsFilteredText; private Drawable mSeenNotifsFilteredIcon; + private @StringRes int mMessageStringId; + private @DrawableRes int mMessageIconId; + public FooterView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -84,6 +93,50 @@ public class FooterView extends StackScrollerDecorView { }); } + /** Set the string for a message to be shown instead of the buttons. */ + public void setMessageString(@StringRes int messageId) { + if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return; + if (mMessageStringId == messageId) { + return; // nothing changed + } + mMessageStringId = messageId; + updateMessageString(); + } + + private void updateMessageString() { + if (mMessageStringId == 0) { + return; // not initialized yet + } + String messageString = getContext().getString(mMessageStringId); + mSeenNotifsFooterTextView.setText(messageString); + } + + + /** Set the icon to be shown before the message (see {@link #setMessageString(int)}). */ + public void setMessageIcon(@DrawableRes int iconId) { + if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return; + if (mMessageIconId == iconId) { + return; // nothing changed + } + mMessageIconId = iconId; + updateMessageIcon(); + } + + private void updateMessageIcon() { + if (mMessageIconId == 0) { + return; // not initialized yet + } + int unlockIconSize = getResources() + .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size); + @SuppressLint("UseCompatLoadingForDrawables") + Drawable messageIcon = getContext().getDrawable(mMessageIconId); + if (messageIcon != null) { + messageIcon.setBounds(0, 0, unlockIconSize, unlockIconSize); + mSeenNotifsFooterTextView + .setCompoundDrawablesRelative(messageIcon, null, null, null); + } + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -148,9 +201,11 @@ public class FooterView extends StackScrollerDecorView { mManageButton.setText(mManageNotificationText); mManageButton.setContentDescription(mManageNotificationText); } - mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText); - mSeenNotifsFooterTextView - .setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null); + if (!FooterViewRefactor.isEnabled()) { + mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText); + mSeenNotifsFooterTextView + .setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null); + } } /** Whether the start button shows "History" (true) or "Manage" (false). */ @@ -167,6 +222,11 @@ public class FooterView extends StackScrollerDecorView { mContext.getString(R.string.accessibility_clear_all)); updateResources(); updateContent(); + + if (FooterViewRefactor.isEnabled()) { + updateMessageString(); + updateMessageIcon(); + } } /** @@ -200,11 +260,13 @@ public class FooterView extends StackScrollerDecorView { mManageNotificationText = getContext().getString(R.string.manage_notifications_text); mManageNotificationHistoryText = getContext() .getString(R.string.manage_notifications_history_text); - int unlockIconSize = getResources() - .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size); - mSeenNotifsFilteredText = getContext().getString(R.string.unlock_to_see_notif_text); - mSeenNotifsFilteredIcon = getContext().getDrawable(R.drawable.ic_friction_lock_closed); - mSeenNotifsFilteredIcon.setBounds(0, 0, unlockIconSize, unlockIconSize); + if (!FooterViewRefactor.isEnabled()) { + int unlockIconSize = getResources() + .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size); + mSeenNotifsFilteredText = getContext().getString(R.string.unlock_to_see_notif_text); + mSeenNotifsFilteredIcon = getContext().getDrawable(R.drawable.ic_friction_lock_closed); + mSeenNotifsFilteredIcon.setBounds(0, 0, unlockIconSize, unlockIconSize); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt new file mode 100644 index 0000000000000000000000000000000000000000..6d8234371b65867c0f4588e7e33ab3b72dbaa468 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 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.footer.ui.viewbinder + +import androidx.lifecycle.lifecycleScope +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView +import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel +import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.launch + +/** Binds a [FooterView] to its [view model][FooterViewModel]. */ +object FooterViewBinder { + fun bind( + footer: FooterView, + viewModel: FooterViewModel, + ): DisposableHandle { + return footer.repeatWhenAttached { + // Listen for changes when the view is attached. + lifecycleScope.launch { + viewModel.message.collect { message -> + footer.setFooterLabelVisible(message.visible) + footer.setMessageString(message.messageId) + footer.setMessageIcon(message.iconId) + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..bc912fb106f0e4b7cbd06d8b362d818eb61da651 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 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.footer.ui.viewmodel + +import android.annotation.DrawableRes +import android.annotation.StringRes + +/** A ViewModel for the string message that can be shown in the footer. */ +data class FooterMessageViewModel( + @StringRes val messageId: Int, + @DrawableRes val iconId: Int, + val visible: Boolean, +) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..ffa5ff052454fc7d8a6bc002cbdf119215fbc1ca --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 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.footer.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.res.R +import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor +import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** ViewModel for [FooterView]. */ +@SysUISingleton +class FooterViewModel +@Inject +constructor(seenNotificationsInteractor: SeenNotificationsInteractor) { + init { + /* Check if */ FooterViewRefactor.isUnexpectedlyInLegacyMode() + } + + val message: Flow<FooterMessageViewModel> = + seenNotificationsInteractor.hasFilteredOutSeenNotifications.map { hasFilteredOutNotifs -> + FooterMessageViewModel( + messageId = R.string.unlock_to_see_notif_text, + iconId = R.drawable.ic_friction_lock_closed, + visible = hasFilteredOutNotifs, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt new file mode 100644 index 0000000000000000000000000000000000000000..00d873e074b8d2e84ead2952a307cc14cab56b9a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2023 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. + * + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.icon.domain.interactor + +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor +import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository +import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository +import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel +import com.android.wm.shell.bubbles.Bubbles +import java.util.Optional +import javax.inject.Inject +import kotlin.jvm.optionals.getOrNull +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest + +/** Domain logic related to notification icons. */ +class NotificationIconsInteractor +@Inject +constructor( + private val activeNotificationsInteractor: ActiveNotificationsInteractor, + private val bubbles: Optional<Bubbles>, + private val keyguardViewStateRepository: NotificationsKeyguardViewStateRepository, +) { + /** Returns a subset of all active notifications based on the supplied filtration parameters. */ + fun filteredNotifSet( + showAmbient: Boolean = true, + showLowPriority: Boolean = true, + showDismissed: Boolean = true, + showRepliedMessages: Boolean = true, + showPulsing: Boolean = true, + ): Flow<Set<ActiveNotificationModel>> { + return combine( + activeNotificationsInteractor.notifications, + keyguardViewStateRepository.areNotificationsFullyHidden, + ) { notifications, notifsFullyHidden -> + notifications + .asSequence() + .filter { model: ActiveNotificationModel -> + shouldShowNotificationIcon( + model = model, + showAmbient = showAmbient, + showLowPriority = showLowPriority, + showDismissed = showDismissed, + showRepliedMessages = showRepliedMessages, + showPulsing = showPulsing, + notifsFullyHidden = notifsFullyHidden, + ) + } + .toSet() + } + } + + private fun shouldShowNotificationIcon( + model: ActiveNotificationModel, + showAmbient: Boolean, + showLowPriority: Boolean, + showDismissed: Boolean, + showRepliedMessages: Boolean, + showPulsing: Boolean, + notifsFullyHidden: Boolean, + ): Boolean { + return when { + !showAmbient && model.isAmbient -> false + !showLowPriority && model.isSilent -> false + !showDismissed && model.isRowDismissed -> false + !showRepliedMessages && model.isLastMessageFromReply -> false + !showAmbient && model.isSuppressedFromStatusBar -> false + !showPulsing && model.isPulsing && !notifsFullyHidden -> false + bubbles.getOrNull()?.isBubbleExpanded(model.key) == true -> false + else -> true + } + } +} + +/** Domain logic related to notification icons shown on the always-on display. */ +class AlwaysOnDisplayNotificationIconsInteractor +@Inject +constructor( + deviceEntryInteractor: DeviceEntryInteractor, + iconsInteractor: NotificationIconsInteractor, +) { + val aodNotifs: Flow<Set<ActiveNotificationModel>> = + deviceEntryInteractor.isBypassEnabled.flatMapLatest { isBypassEnabled -> + iconsInteractor.filteredNotifSet( + showAmbient = false, + showDismissed = false, + showRepliedMessages = false, + showPulsing = !isBypassEnabled, + ) + } +} + +/** Domain logic related to notification icons shown in the status bar. */ +class StatusBarNotificationIconsInteractor +@Inject +constructor( + iconsInteractor: NotificationIconsInteractor, + settingsRepository: NotificationListenerSettingsRepository, +) { + val statusBarNotifs: Flow<Set<ActiveNotificationModel>> = + settingsRepository.showSilentStatusIcons.flatMapLatest { showSilentIcons -> + iconsInteractor.filteredNotifSet( + showAmbient = false, + showLowPriority = showSilentIcons, + showDismissed = false, + showRepliedMessages = false, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt index de011dbd1ab5f2985f7d4b14545ffb52342f3642..246933ad69e334f373c56091c4f5b642bcd68476 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt @@ -17,43 +17,23 @@ package com.android.systemui.statusbar.notification.icon.ui.viewbinder import android.content.Context import android.graphics.Rect -import android.os.Bundle -import android.os.Trace -import android.view.LayoutInflater import android.view.View -import android.widget.FrameLayout -import androidx.annotation.VisibleForTesting -import androidx.collection.ArrayMap -import com.android.internal.statusbar.StatusBarIcon import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.demomode.DemoMode -import com.android.systemui.demomode.DemoModeController import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.flags.RefactorFlag -import com.android.systemui.res.R -import com.android.systemui.statusbar.NotificationListener -import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.NotificationShelfController import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.collection.ListEntry -import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel -import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.statusbar.window.StatusBarWindowController -import com.android.wm.shell.bubbles.Bubbles -import java.util.Optional -import java.util.function.Function +import com.android.systemui.statusbar.policy.ConfigurationController import javax.inject.Inject import kotlinx.coroutines.DisposableHandle @@ -68,57 +48,22 @@ import kotlinx.coroutines.DisposableHandle class NotificationIconAreaControllerViewBinderWrapperImpl @Inject constructor( - private val context: Context, private val configuration: ConfigurationState, - private val wakeUpCoordinator: NotificationWakeUpCoordinator, - private val bypassController: KeyguardBypassController, - private val mediaManager: NotificationMediaManager, - notificationListener: NotificationListener, + private val configurationController: ConfigurationController, private val dozeParameters: DozeParameters, - private val sectionStyleProvider: SectionStyleProvider, - private val bubblesOptional: Optional<Bubbles>, - demoModeController: DemoModeController, private val featureFlags: FeatureFlagsClassic, - private val statusBarWindowController: StatusBarWindowController, private val screenOffAnimationController: ScreenOffAnimationController, + private val shelfIconViewStore: ShelfNotificationIconViewStore, private val shelfIconsViewModel: NotificationIconContainerShelfViewModel, - private val statusBarIconsViewModel: NotificationIconContainerStatusBarViewModel, + private val aodIconViewStore: AlwaysOnDisplayNotificationIconViewStore, private val aodIconsViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, -) : NotificationIconAreaController, NotificationWakeUpCoordinator.WakeUpListener, DemoMode { +) : NotificationIconAreaController { - private val updateStatusBarIcons = Runnable { updateStatusBarIcons() } private val shelfRefactor = RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR) - private var iconSize = 0 - private var iconHPadding = 0 - private var notificationEntries = listOf<ListEntry>() - private var notificationIconArea: View? = null - private var notificationIcons: NotificationIconContainer? = null private var shelfIcons: NotificationIconContainer? = null private var aodIcons: NotificationIconContainer? = null private var aodBindJob: DisposableHandle? = null - private var showLowPriority = true - - @VisibleForTesting - val settingsListener: NotificationListener.NotificationSettingsListener = - object : NotificationListener.NotificationSettingsListener { - override fun onStatusBarIconsBehaviorChanged(hideSilentStatusIcons: Boolean) { - showLowPriority = !hideSilentStatusIcons - updateStatusBarIcons() - } - } - - init { - wakeUpCoordinator.addListener(this) - demoModeController.addCallback(this) - notificationListener.addNotificationSettingsListener(settingsListener) - initializeNotificationAreaViews(context) - } - - @VisibleForTesting - fun shouldShowLowPriorityIcons(): Boolean { - return showLowPriority - } /** Called by the Keyguard*ViewController whose view contains the aod icons. */ override fun setupAodIcons(aodIcons: NotificationIconContainer) { @@ -135,14 +80,12 @@ constructor( aodIcons, aodIconsViewModel, configuration, + configurationController, dozeParameters, featureFlags, screenOffAnimationController, + aodIconViewStore, ) - if (changed) { - updateAodNotificationIcons() - } - updateIconLayoutParams(context) } override fun setupShelf(notificationShelfController: NotificationShelfController) = @@ -154,65 +97,30 @@ constructor( icons, shelfIconsViewModel, configuration, + configurationController, dozeParameters, featureFlags, screenOffAnimationController, + shelfIconViewStore, ) shelfIcons = icons } } - override fun onDensityOrFontScaleChanged(context: Context) { - updateIconLayoutParams(context) - } + override fun onDensityOrFontScaleChanged(context: Context) = unsupported /** Returns the view that represents the notification area. */ - override fun getNotificationInnerAreaView(): View? { - return notificationIconArea - } + override fun getNotificationInnerAreaView(): View? = unsupported /** Updates the notifications with the given list of notifications to display. */ - override fun updateNotificationIcons(entries: List<ListEntry>) { - notificationEntries = entries - updateNotificationIcons() - } - - private fun updateStatusBarIcons() { - updateIconsForLayout( - { entry: NotificationEntry -> entry.icons.statusBarIcon }, - notificationIcons, - showAmbient = false /* showAmbient */, - showLowPriority = showLowPriority, - hideDismissed = true /* hideDismissed */, - hideRepliedMessages = true /* hideRepliedMessages */, - hideCurrentMedia = false /* hideCurrentMedia */, - hidePulsing = false /* hidePulsing */ - ) - } + override fun updateNotificationIcons(entries: List<ListEntry>) = unsupported - override fun updateAodNotificationIcons() { - if (aodIcons == null) { - return - } - updateIconsForLayout( - { entry: NotificationEntry -> entry.icons.aodIcon }, - aodIcons, - showAmbient = false /* showAmbient */, - showLowPriority = true /* showLowPriority */, - hideDismissed = true /* hideDismissed */, - hideRepliedMessages = true /* hideRepliedMessages */, - hideCurrentMedia = true /* hideCurrentMedia */, - hidePulsing = bypassController.bypassEnabled /* hidePulsing */ - ) - } + override fun updateAodNotificationIcons() = unsupported - override fun showIconIsolated(icon: StatusBarIconView?, animated: Boolean) { - notificationIcons!!.showIconIsolated(icon, animated) - } + override fun showIconIsolated(icon: StatusBarIconView?, animated: Boolean) = unsupported - override fun setIsolatedIconLocation(iconDrawingRect: Rect, requireStateUpdate: Boolean) { - notificationIcons!!.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate) - } + override fun setIsolatedIconLocation(iconDrawingRect: Rect, requireStateUpdate: Boolean) = + unsupported override fun setAnimationsEnabled(enabled: Boolean) = unsupported @@ -222,271 +130,6 @@ constructor( return if (aodIcons == null) 0 else aodIcons!!.height } - override fun onFullyHiddenChanged(isFullyHidden: Boolean) { - updateAodNotificationIcons() - } - - override fun demoCommands(): List<String> { - val commands = ArrayList<String>() - commands.add(DemoMode.COMMAND_NOTIFICATIONS) - return commands - } - - override fun dispatchDemoCommand(command: String, args: Bundle) { - if (notificationIconArea != null) { - val visible = args.getString("visible") - val vis = if ("false" == visible) View.INVISIBLE else View.VISIBLE - notificationIconArea?.visibility = vis - } - } - - override fun onDemoModeFinished() { - if (notificationIconArea != null) { - notificationIconArea?.visibility = View.VISIBLE - } - } - - private fun inflateIconArea(inflater: LayoutInflater): View { - return inflater.inflate(R.layout.notification_icon_area, null) - } - - /** Initializes the views that will represent the notification area. */ - private fun initializeNotificationAreaViews(context: Context) { - reloadDimens(context) - val layoutInflater = LayoutInflater.from(context) - notificationIconArea = inflateIconArea(layoutInflater) - notificationIcons = notificationIconArea?.findViewById(R.id.notificationIcons) - NotificationIconContainerViewBinder.bind( - notificationIcons!!, - statusBarIconsViewModel, - configuration, - dozeParameters, - featureFlags, - screenOffAnimationController, - ) - } - - private fun updateIconLayoutParams(context: Context) { - reloadDimens(context) - val params = generateIconLayoutParams() - for (i in 0 until notificationIcons!!.childCount) { - val child = notificationIcons!!.getChildAt(i) - child.layoutParams = params - } - if (shelfIcons != null) { - for (i in 0 until shelfIcons!!.childCount) { - val child = shelfIcons!!.getChildAt(i) - child.layoutParams = params - } - } - if (aodIcons != null) { - for (i in 0 until aodIcons!!.childCount) { - val child = aodIcons!!.getChildAt(i) - child.layoutParams = params - } - } - } - - private fun generateIconLayoutParams(): FrameLayout.LayoutParams { - return FrameLayout.LayoutParams( - iconSize + 2 * iconHPadding, - statusBarWindowController.statusBarHeight - ) - } - - private fun reloadDimens(context: Context) { - val res = context.resources - iconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size_sp) - iconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin) - } - - private fun shouldShowNotificationIcon( - entry: NotificationEntry, - showAmbient: Boolean, - showLowPriority: Boolean, - hideDismissed: Boolean, - hideRepliedMessages: Boolean, - hideCurrentMedia: Boolean, - hidePulsing: Boolean - ): Boolean { - if (!showAmbient && sectionStyleProvider.isMinimized(entry)) { - return false - } - if (hideCurrentMedia && entry.key == mediaManager.mediaNotificationKey) { - return false - } - if (!showLowPriority && sectionStyleProvider.isSilent(entry)) { - return false - } - if (entry.isRowDismissed && hideDismissed) { - return false - } - if (hideRepliedMessages && entry.isLastMessageFromReply) { - return false - } - // showAmbient == show in shade but not shelf - if (!showAmbient && entry.shouldSuppressStatusBar()) { - return false - } - if ( - hidePulsing && - entry.showingPulsing() && - (!wakeUpCoordinator.notificationsFullyHidden || !entry.isPulseSuppressed) - ) { - return false - } - return if (bubblesOptional.isPresent && bubblesOptional.get().isBubbleExpanded(entry.key)) { - false - } else true - } - - private fun updateNotificationIcons() { - Trace.beginSection("NotificationIconAreaController.updateNotificationIcons") - updateStatusBarIcons() - updateShelfIcons() - updateAodNotificationIcons() - Trace.endSection() - } - - private fun updateShelfIcons() { - if (shelfIcons == null) { - return - } - updateIconsForLayout( - { entry: NotificationEntry -> entry.icons.shelfIcon }, - shelfIcons, - showAmbient = true, - showLowPriority = true, - hideDismissed = false, - hideRepliedMessages = false, - hideCurrentMedia = false, - hidePulsing = false - ) - } - - /** - * Updates the notification icons for a host layout. This will ensure that the notification host - * layout will have the same icons like the ones in here. - * - * @param function A function to look up an icon view based on an entry - * @param hostLayout which layout should be updated - * @param showAmbient should ambient notification icons be shown - * @param showLowPriority should icons from silent notifications be shown - * @param hideDismissed should dismissed icons be hidden - * @param hideRepliedMessages should messages that have been replied to be hidden - * @param hidePulsing should pulsing notifications be hidden - */ - private fun updateIconsForLayout( - function: Function<NotificationEntry, StatusBarIconView?>, - hostLayout: NotificationIconContainer?, - showAmbient: Boolean, - showLowPriority: Boolean, - hideDismissed: Boolean, - hideRepliedMessages: Boolean, - hideCurrentMedia: Boolean, - hidePulsing: Boolean, - ) { - val toShow = ArrayList<StatusBarIconView>(notificationEntries.size) - // Filter out ambient notifications and notification children. - for (i in notificationEntries.indices) { - val entry = notificationEntries[i].representativeEntry - if (entry != null && entry.row != null) { - if ( - shouldShowNotificationIcon( - entry, - showAmbient, - showLowPriority, - hideDismissed, - hideRepliedMessages, - hideCurrentMedia, - hidePulsing - ) - ) { - val iconView = function.apply(entry) - if (iconView != null) { - toShow.add(iconView) - } - } - } - } - - // In case we are changing the suppression of a group, the replacement shouldn't flicker - // and it should just be replaced instead. We therefore look for notifications that were - // just replaced by the child or vice-versa to suppress this. - val replacingIcons = ArrayMap<String, ArrayList<StatusBarIcon>>() - val toRemove = ArrayList<View>() - for (i in 0 until hostLayout!!.childCount) { - val child = hostLayout.getChildAt(i) as? StatusBarIconView ?: continue - if (!toShow.contains(child)) { - var iconWasReplaced = false - val removedGroupKey = child.notification.groupKey - for (j in toShow.indices) { - val candidate = toShow[j] - if ( - candidate.sourceIcon.sameAs(child.sourceIcon) && - candidate.notification.groupKey == removedGroupKey - ) { - if (!iconWasReplaced) { - iconWasReplaced = true - } else { - iconWasReplaced = false - break - } - } - } - if (iconWasReplaced) { - var statusBarIcons = replacingIcons[removedGroupKey] - if (statusBarIcons == null) { - statusBarIcons = ArrayList() - replacingIcons[removedGroupKey] = statusBarIcons - } - statusBarIcons.add(child.statusBarIcon) - } - toRemove.add(child) - } - } - // removing all duplicates - val duplicates = ArrayList<String?>() - for (key in replacingIcons.keys) { - val statusBarIcons = replacingIcons[key]!! - if (statusBarIcons.size != 1) { - duplicates.add(key) - } - } - replacingIcons.removeAll(duplicates) - hostLayout.setReplacingIcons(replacingIcons) - val toRemoveCount = toRemove.size - for (i in 0 until toRemoveCount) { - hostLayout.removeView(toRemove[i]) - } - val params = generateIconLayoutParams() - for (i in toShow.indices) { - val v = toShow[i] - // The view might still be transiently added if it was just removed and added again - hostLayout.removeTransientView(v) - if (v.parent == null) { - if (hideDismissed) { - v.setOnDismissListener(updateStatusBarIcons) - } - hostLayout.addView(v, i, params) - } - } - hostLayout.setChangingViewPositions(true) - // Re-sort notification icons - val childCount = hostLayout.childCount - for (i in 0 until childCount) { - val actual = hostLayout.getChildAt(i) - val expected = toShow[i] - if (actual === expected) { - continue - } - hostLayout.removeView(expected) - hostLayout.addView(expected, i) - } - hostLayout.setChangingViewPositions(false) - hostLayout.setReplacingIcons(null) - } - companion object { val unsupported: Nothing get() = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt index 079004c2a60a3f95234064e45c667fc06d8a2a82..75926194af581c9be86d831535d233e7dca10380 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt @@ -15,11 +15,17 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewbinder +import android.animation.Animator +import android.animation.AnimatorListenerAdapter import android.graphics.Rect import android.view.View +import android.view.ViewPropertyAnimator +import android.widget.FrameLayout +import androidx.collection.ArrayMap import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators +import com.android.internal.policy.SystemBarUtils import com.android.internal.util.ContrastColorUtil import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.flags.FeatureFlagsClassic @@ -29,78 +35,235 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.CrossFadeHelper import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.notification.NotificationUtils +import com.android.systemui.statusbar.notification.collection.NotifCollection +import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.onConfigChanged import com.android.systemui.util.children +import com.android.systemui.util.kotlin.mapValuesNotNullTo +import com.android.systemui.util.kotlin.sample +import com.android.systemui.util.kotlin.stateFlow +import com.android.systemui.util.ui.isAnimating +import com.android.systemui.util.ui.stopAnimating +import com.android.systemui.util.ui.value +import javax.inject.Inject import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch /** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */ object NotificationIconContainerViewBinder { + @JvmStatic fun bind( view: NotificationIconContainer, viewModel: NotificationIconContainerViewModel, configuration: ConfigurationState, + configurationController: ConfigurationController, dozeParameters: DozeParameters, featureFlags: FeatureFlagsClassic, screenOffAnimationController: ScreenOffAnimationController, + viewStore: IconViewStore, ): DisposableHandle { val contrastColorUtil = ContrastColorUtil.getInstance(view.context) return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { - launch { viewModel.animationsEnabled.collect(view::setAnimationsEnabled) } - launch { - viewModel.isDozing.collect { (isDozing, animate) -> - val animateIfNotBlanking = animate && !dozeParameters.displayNeedsBlanking - view.setDozing(isDozing, animateIfNotBlanking, /* delay= */ 0) { - viewModel.completeDozeAnimation() - } - } - } + launch { bindAnimationsEnabled(viewModel, view) } + launch { bindIsDozing(viewModel, view, dozeParameters) } // TODO(b/278765923): this should live where AOD is bound, not inside of the NIC // view-binder launch { - val iconAppearTranslation = - configuration - .getDimensionPixelSize(R.dimen.shelf_appear_translation) - .stateIn(this) bindVisibility( viewModel, view, + configuration, featureFlags, screenOffAnimationController, - iconAppearTranslation, - ) { - viewModel.completeVisibilityAnimation() - } + ) } + launch { bindIconColors(viewModel, view, contrastColorUtil) } launch { - viewModel.iconColors - .mapNotNull { lookup -> lookup.iconColors(view.viewBounds) } - .collect { iconLookup -> applyTint(view, iconLookup, contrastColorUtil) } + bindIconViewData( + viewModel, + view, + configuration, + configurationController, + viewStore, + ) } + launch { bindIsolatedIcon(viewModel, view, viewStore) } } } } - // TODO(b/305739416): Once SBIV has its own Recommended Architecture stack, this can be moved - // there and cleaned up. - private fun applyTint( + private suspend fun bindAnimationsEnabled( + viewModel: NotificationIconContainerViewModel, + view: NotificationIconContainer + ) { + viewModel.animationsEnabled.collect(view::setAnimationsEnabled) + } + + private suspend fun bindIconColors( + viewModel: NotificationIconContainerViewModel, view: NotificationIconContainer, - iconColors: IconColors, contrastColorUtil: ContrastColorUtil, ) { - view.children.filterIsInstance<StatusBarIconView>().forEach { iv -> - if (iv.width != 0) { - updateTintForIcon(iv, iconColors, contrastColorUtil) + viewModel.iconColors + .mapNotNull { lookup -> lookup.iconColors(view.viewBounds) } + .collect { iconLookup -> applyTint(view, iconLookup, contrastColorUtil) } + } + + private suspend fun bindIsDozing( + viewModel: NotificationIconContainerViewModel, + view: NotificationIconContainer, + dozeParameters: DozeParameters, + ) { + viewModel.isDozing.collect { isDozing -> + if (isDozing.isAnimating) { + val animate = !dozeParameters.displayNeedsBlanking + view.setDozing( + /* dozing = */ isDozing.value, + /* fade = */ animate, + /* delay = */ 0, + /* endRunnable = */ isDozing::stopAnimating, + ) + } else { + view.setDozing( + /* dozing = */ isDozing.value, + /* fade= */ false, + /* delay= */ 0, + ) + } + } + } + + private suspend fun bindIsolatedIcon( + viewModel: NotificationIconContainerViewModel, + view: NotificationIconContainer, + viewStore: IconViewStore, + ) { + coroutineScope { + launch { + viewModel.isolatedIconLocation.collect { location -> + view.setIsolatedIconLocation(location, true) + } + } + launch { + viewModel.isolatedIcon.collect { iconInfo -> + val iconView = iconInfo.value?.let { viewStore.iconView(it.notifKey) } + if (iconInfo.isAnimating) { + view.showIconIsolatedAnimated(iconView, iconInfo::stopAnimating) + } else { + view.showIconIsolated(iconView) + } + } + } + } + } + + private suspend fun bindIconViewData( + viewModel: NotificationIconContainerViewModel, + view: NotificationIconContainer, + configuration: ConfigurationState, + configurationController: ConfigurationController, + viewStore: IconViewStore, + ): Unit = coroutineScope { + val iconSizeFlow: Flow<Int> = + configuration.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_icon_size_sp, + ) + val iconHorizontalPaddingFlow: Flow<Int> = + configuration.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin) + val statusBarHeightFlow: StateFlow<Int> = + stateFlow(changedSignals = configurationController.onConfigChanged) { + SystemBarUtils.getStatusBarHeight(view.context) + } + val layoutParams: Flow<FrameLayout.LayoutParams> = + combine(iconSizeFlow, iconHorizontalPaddingFlow, statusBarHeightFlow) { + iconSize, + iconHPadding, + statusBarHeight, + -> + FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight) + } + + launch { + layoutParams.collect { params: FrameLayout.LayoutParams -> + for (child in view.children) { + child.layoutParams = params + } } } + + var prevIcons = IconsViewData() + viewModel.iconsViewData.sample(layoutParams, ::Pair).collect { + (iconsData: IconsViewData, layoutParams: FrameLayout.LayoutParams), + -> + val iconsDiff = IconsViewData.computeDifference(iconsData, prevIcons) + prevIcons = iconsData + + val replacingIcons = + iconsDiff.groupReplacements.mapValuesNotNullTo(ArrayMap()) { (_, v) -> + viewStore.iconView(v.notifKey)?.statusBarIcon + } + view.setReplacingIcons(replacingIcons) + + val childrenByNotifKey: Map<String, StatusBarIconView> = + view.children.filterIsInstance<StatusBarIconView>().associateByTo(ArrayMap()) { + it.notification.key + } + + iconsDiff.removed + .mapNotNull { key -> childrenByNotifKey[key] } + .forEach { child -> view.removeView(child) } + + val toAdd = iconsDiff.added.mapNotNull { viewStore.iconView(it.notifKey) } + for ((i, sbiv) in toAdd.withIndex()) { + // The view might still be transiently added if it was just removed + // and added again + view.removeTransientView(sbiv) + view.addView(sbiv, i, layoutParams) + } + + view.setChangingViewPositions(true) + // Re-sort notification icons + val childCount = view.childCount + for (i in 0 until childCount) { + val actual = view.getChildAt(i) + val expected = viewStore.iconView(iconsData.visibleKeys[i].notifKey)!! + if (actual === expected) { + continue + } + view.removeView(expected) + view.addView(expected, i) + } + view.setChangingViewPositions(false) + + view.setReplacingIcons(null) + } + } + + // TODO(b/305739416): Once StatusBarIconView has its own Recommended Architecture stack, this + // can be moved there and cleaned up. + private fun applyTint( + view: NotificationIconContainer, + iconColors: IconColors, + contrastColorUtil: ContrastColorUtil, + ) { + view.children + .filterIsInstance<StatusBarIconView>() + .filter { it.width != 0 } + .forEach { iv -> updateTintForIcon(iv, iconColors, contrastColorUtil) } } private fun updateTintForIcon( @@ -117,34 +280,41 @@ object NotificationIconContainerViewBinder { private suspend fun bindVisibility( viewModel: NotificationIconContainerViewModel, view: NotificationIconContainer, + configuration: ConfigurationState, featureFlags: FeatureFlagsClassic, screenOffAnimationController: ScreenOffAnimationController, - iconAppearTranslation: StateFlow<Int>, - onAnimationEnd: () -> Unit, - ) { + ): Unit = coroutineScope { + val iconAppearTranslation = + configuration.getDimensionPixelSize(R.dimen.shelf_appear_translation).stateIn(this) val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW) - viewModel.isVisible.collect { (isVisible, animate) -> + viewModel.isVisible.collect { isVisible -> view.animate().cancel() + val animatorListener = + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + isVisible.stopAnimating() + } + } when { - !animate -> { + !isVisible.isAnimating -> { view.alpha = 1f if (!statusViewMigrated) { view.translationY = 0f } - view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE + view.visibility = if (isVisible.value) View.VISIBLE else View.INVISIBLE } featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> { animateInIconTranslation(view, statusViewMigrated) - if (isVisible) { - CrossFadeHelper.fadeIn(view, onAnimationEnd) + if (isVisible.value) { + CrossFadeHelper.fadeIn(view, animatorListener) } else { - CrossFadeHelper.fadeOut(view, onAnimationEnd) + CrossFadeHelper.fadeOut(view, animatorListener) } } - !isVisible -> { + !isVisible.value -> { // Let's make sure the icon are translated to 0, since we cancelled it above animateInIconTranslation(view, statusViewMigrated) - CrossFadeHelper.fadeOut(view, onAnimationEnd) + CrossFadeHelper.fadeOut(view, animatorListener) } view.visibility != View.VISIBLE -> { // No fading here, let's just appear the icons instead! @@ -155,14 +325,14 @@ object NotificationIconContainerViewBinder { animate = screenOffAnimationController.shouldAnimateAodIcons(), iconAppearTranslation.value, statusViewMigrated, + animatorListener, ) - onAnimationEnd() } else -> { // Let's make sure the icons are translated to 0, since we cancelled it above animateInIconTranslation(view, statusViewMigrated) // We were fading out, let's fade in instead - CrossFadeHelper.fadeIn(view, onAnimationEnd) + CrossFadeHelper.fadeIn(view, animatorListener) } } } @@ -173,18 +343,20 @@ object NotificationIconContainerViewBinder { animate: Boolean, iconAppearTranslation: Int, statusViewMigrated: Boolean, + animatorListener: Animator.AnimatorListener, ) { if (animate) { if (!statusViewMigrated) { view.translationY = -iconAppearTranslation.toFloat() } view.alpha = 0f - animateInIconTranslation(view, statusViewMigrated) view .animate() .alpha(1f) .setInterpolator(Interpolators.LINEAR) .setDuration(AOD_ICONS_APPEAR_DURATION) + .apply { if (statusViewMigrated) animateInIconTranslation() } + .setListener(animatorListener) .start() } else { view.alpha = 1.0f @@ -196,15 +368,13 @@ object NotificationIconContainerViewBinder { private fun animateInIconTranslation(view: View, statusViewMigrated: Boolean) { if (!statusViewMigrated) { - view - .animate() - .setInterpolator(Interpolators.DECELERATE_QUINT) - .translationY(0f) - .setDuration(AOD_ICONS_APPEAR_DURATION) - .start() + view.animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start() } } + private fun ViewPropertyAnimator.animateInIconTranslation(): ViewPropertyAnimator = + setInterpolator(Interpolators.DECELERATE_QUINT).translationY(0f) + private const val AOD_ICONS_APPEAR_DURATION: Long = 200 private val View.viewBounds: Rect @@ -218,4 +388,39 @@ object NotificationIconContainerViewBinder { /* bottom = */ top + height, ) } + + /** External storage for [StatusBarIconView] instances. */ + fun interface IconViewStore { + fun iconView(key: String): StatusBarIconView? + } +} + +/** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */ +class ShelfNotificationIconViewStore +@Inject +constructor( + private val notifCollection: NotifCollection, +) : IconViewStore { + override fun iconView(key: String): StatusBarIconView? = + notifCollection.getEntry(key)?.icons?.shelfIcon +} + +/** [IconViewStore] for the always-on display. */ +class AlwaysOnDisplayNotificationIconViewStore +@Inject +constructor( + private val notifCollection: NotifCollection, +) : IconViewStore { + override fun iconView(key: String): StatusBarIconView? = + notifCollection.getEntry(key)?.icons?.aodIcon +} + +/** [IconViewStore] for the status bar. */ +class StatusBarNotificationIconViewStore +@Inject +constructor( + private val notifCollection: NotifCollection, +) : IconViewStore { + override fun iconView(key: String): StatusBarIconView? = + notifCollection.getEntry(key)?.icons?.statusBarIcon } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt index e9de4bd654d3fb8c2449dcfbadb51f9bec95aa60..120d342b18d842d682aedf9cac7a7fb80a334394 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt @@ -30,8 +30,11 @@ import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor +import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.util.kotlin.pairwise @@ -39,11 +42,13 @@ import com.android.systemui.util.kotlin.sample import com.android.systemui.util.ui.AnimatableEvent import com.android.systemui.util.ui.AnimatedValue import com.android.systemui.util.ui.toAnimatedValueFlow +import com.android.systemui.util.ui.zip import javax.inject.Inject import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map /** View-model for the row of notification icons displayed on the always-on display. */ @@ -55,6 +60,7 @@ constructor( private val deviceEntryInteractor: DeviceEntryInteractor, private val dozeParameters: DozeParameters, private val featureFlags: FeatureFlagsClassic, + iconsInteractor: AlwaysOnDisplayNotificationIconsInteractor, keyguardInteractor: KeyguardInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor, @@ -62,9 +68,6 @@ constructor( shadeInteractor: ShadeInteractor, ) : NotificationIconContainerViewModel { - private val onDozeAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1) - private val onVisAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1) - override val iconColors: Flow<ColorLookup> = configuration.getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR).map { tint -> ColorLookup { IconColorsImpl(tint) } @@ -93,7 +96,7 @@ constructor( AnimatableEvent(isDozing, animate) } .distinctUntilChanged() - .toAnimatedValueFlow(completionEvents = onDozeAnimationComplete) + .toAnimatedValueFlow() override val isVisible: Flow<AnimatedValue<Boolean>> = combine( @@ -103,46 +106,54 @@ constructor( isPulseExpandingAnimated(), ) { onKeyguard: Boolean, - bypassEnabled: Boolean, - (notifsFullyHidden: Boolean, isAnimatingHide: Boolean), - (pulseExpanding: Boolean, isAnimatingPulse: Boolean), + isBypassEnabled: Boolean, + notifsFullyHidden: AnimatedValue<Boolean>, + pulseExpanding: AnimatedValue<Boolean>, -> - val isAnimating = isAnimatingHide || isAnimatingPulse when { // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off // animation is playing, in which case we want them to be visible if we're // animating in the AOD UI and will be switching to KEYGUARD shortly. !onKeyguard && !screenOffAnimationController.shouldShowAodIconsWhenShade() -> - AnimatedValue(false, isAnimating = false) - // If we're bypassing, then we're visible - bypassEnabled -> AnimatedValue(true, isAnimating) - // If we are pulsing (and not bypassing), then we are hidden - pulseExpanding -> AnimatedValue(false, isAnimating) - // If notifs are fully gone, then we're visible - notifsFullyHidden -> AnimatedValue(true, isAnimating) - // Otherwise, we're hidden - else -> AnimatedValue(false, isAnimating) + AnimatedValue.NotAnimating(false) + else -> + zip(notifsFullyHidden, pulseExpanding) { + areNotifsFullyHidden, + isPulseExpanding, + -> + when { + // If we're bypassing, then we're visible + isBypassEnabled -> true + // If we are pulsing (and not bypassing), then we are hidden + isPulseExpanding -> false + // If notifs are fully gone, then we're visible + areNotifsFullyHidden -> true + // Otherwise, we're hidden + else -> false + } + } } } .distinctUntilChanged() - override fun completeDozeAnimation() { - onDozeAnimationComplete.tryEmit(Unit) - } + override val iconsViewData: Flow<IconsViewData> = + iconsInteractor.aodNotifs.map { entries -> + IconsViewData( + visibleKeys = entries.mapNotNull { it.toIconInfo(it.aodIcon) }, + ) + } - override fun completeVisibilityAnimation() { - onVisAnimationComplete.tryEmit(Unit) - } + override val isolatedIcon: Flow<AnimatedValue<IconInfo?>> = + flowOf(AnimatedValue.NotAnimating(null)) + override val isolatedIconLocation: Flow<Rect> = emptyFlow() /** Is there an expanded pulse, are we animating in response? */ private fun isPulseExpandingAnimated(): Flow<AnimatedValue<Boolean>> { return notificationsKeyguardInteractor.isPulseExpanding .pairwise(initialValue = null) // If pulsing changes, start animating, unless it's the first emission - .map { (prev, expanding) -> - AnimatableEvent(expanding!!, startAnimating = prev != null) - } - .toAnimatedValueFlow(completionEvents = onVisAnimationComplete) + .map { (prev, expanding) -> AnimatableEvent(expanding, startAnimating = prev != null) } + .toAnimatedValueFlow() } /** Are notifications completely hidden from view, are we animating in response? */ @@ -164,11 +175,11 @@ constructor( // We only want the appear animations to happen when the notifications // get fully hidden, since otherwise the un-hide animation overlaps. featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> true - else -> fullyHidden!! + else -> fullyHidden } - AnimatableEvent(fullyHidden!!, animate) + AnimatableEvent(fullyHidden, animate) } - .toAnimatedValueFlow(completionEvents = onVisAnimationComplete) + .toAnimatedValueFlow() } private class IconColorsImpl(override val tint: Int) : IconColors { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt index f305155e9b3d823c8755b48d68f24d56154a31c6..c6aabb7527da99dfd5bfd27fe8f4e2d8aff20191 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt @@ -15,20 +15,37 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import android.graphics.Rect +import com.android.systemui.statusbar.notification.icon.domain.interactor.NotificationIconsInteractor import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData import com.android.systemui.util.ui.AnimatedValue import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map /** View-model for the overflow row of notification icons displayed in the notification shade. */ -class NotificationIconContainerShelfViewModel @Inject constructor() : - NotificationIconContainerViewModel { +class NotificationIconContainerShelfViewModel +@Inject +constructor( + interactor: NotificationIconsInteractor, +) : NotificationIconContainerViewModel { + override val animationsEnabled: Flow<Boolean> = flowOf(true) override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow() override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow() - override fun completeDozeAnimation() {} - override fun completeVisibilityAnimation() {} override val iconColors: Flow<ColorLookup> = emptyFlow() + override val isolatedIcon: Flow<AnimatedValue<IconInfo?>> = + flowOf(AnimatedValue.NotAnimating(null)) + override val isolatedIconLocation: Flow<Rect> = emptyFlow() + + override val iconsViewData: Flow<IconsViewData> = + interactor.filteredNotifSet().map { entries -> + IconsViewData( + visibleKeys = entries.mapNotNull { it.toIconInfo(it.shelfIcon) }, + ) + } } 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 ee01fcca82d47400cd305673ba8b668e6fa530ac..4d14024fcd99fb11fb767cd9fd53d994c5fb1dad 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 @@ -20,20 +20,32 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor +import com.android.systemui.statusbar.notification.icon.domain.interactor.StatusBarNotificationIconsInteractor import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData import com.android.systemui.statusbar.phone.domain.interactor.DarkIconInteractor +import com.android.systemui.util.kotlin.pairwise +import com.android.systemui.util.kotlin.sample +import com.android.systemui.util.ui.AnimatableEvent import com.android.systemui.util.ui.AnimatedValue +import com.android.systemui.util.ui.toAnimatedValueFlow import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map /** View-model for the row of notification icons displayed in the status bar, */ class NotificationIconContainerStatusBarViewModel @Inject constructor( darkIconInteractor: DarkIconInteractor, + iconsInteractor: StatusBarNotificationIconsInteractor, + headsUpIconInteractor: HeadsUpNotificationIconInteractor, keyguardInteractor: KeyguardInteractor, notificationsInteractor: ActiveNotificationsInteractor, shadeInteractor: ShadeInteractor, @@ -45,6 +57,7 @@ constructor( ) { panelTouchesEnabled, isKeyguardShowing -> panelTouchesEnabled && !isKeyguardShowing } + override val iconColors: Flow<ColorLookup> = combine( darkIconInteractor.tintAreas, @@ -60,10 +73,40 @@ constructor( } } } + override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow() override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow() - override fun completeDozeAnimation() {} - override fun completeVisibilityAnimation() {} + + override val iconsViewData: Flow<IconsViewData> = + iconsInteractor.statusBarNotifs.map { entries -> + IconsViewData( + visibleKeys = entries.mapNotNull { it.toIconInfo(it.statusBarIcon) }, + ) + } + + override val isolatedIcon: Flow<AnimatedValue<IconInfo?>> = + headsUpIconInteractor.isolatedNotification + .pairwise(initialValue = null) + .sample(combine(iconsViewData, shadeInteractor.shadeExpansion, ::Pair)) { + (prev, isolatedNotif), + (iconsViewData, shadeExpansion), + -> + val iconInfo = + isolatedNotif?.let { + iconsViewData.visibleKeys.firstOrNull { it.notifKey == isolatedNotif } + } + val animate = + when { + isolatedNotif == prev -> false + isolatedNotif == null || prev == null -> shadeExpansion == 0f + else -> false + } + AnimatableEvent(iconInfo, animate) + } + .toAnimatedValueFlow() + + override val isolatedIconLocation: Flow<Rect> = + headsUpIconInteractor.isolatedIconLocation.filterNotNull() private class IconColorsImpl( override val tint: Int, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt index c98811b0e28552fdb2ee37e71319824893514578..a611323201e32afd463339ddbc338427e7cba640 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt @@ -16,6 +16,11 @@ package com.android.systemui.statusbar.notification.icon.ui.viewmodel import android.graphics.Rect +import android.graphics.drawable.Icon +import androidx.collection.ArrayMap +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel +import com.android.systemui.util.kotlin.mapValuesNotNullTo import com.android.systemui.util.ui.AnimatedValue import kotlinx.coroutines.flow.Flow @@ -37,17 +42,14 @@ interface NotificationIconContainerViewModel { /** The colors with which to display the notification icons. */ val iconColors: Flow<ColorLookup> - /** - * Signal completion of the [isDozing] animation; if [isDozing]'s [AnimatedValue.isAnimating] - * property was `true`, calling this method will update it to `false`. - */ - fun completeDozeAnimation() + /** [IconsViewData] indicating which icons to display in the view. */ + val iconsViewData: Flow<IconsViewData> - /** - * Signal completion of the [isVisible] animation; if [isVisible]'s [AnimatedValue.isAnimating] - * property was `true`, calling this method will update it to `false`. - */ - fun completeVisibilityAnimation() + /** An Icon to show "isolated" in the IconContainer. */ + val isolatedIcon: Flow<AnimatedValue<IconInfo?>> + + /** Location to show an isolated icon, if there is one. */ + val isolatedIconLocation: Flow<Rect> /** * Lookup the colors to use for the notification icons based on the bounds of the icon @@ -69,4 +71,126 @@ interface NotificationIconContainerViewModel { */ fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int } + + /** Encapsulates the collection of notification icons present on the device. */ + data class IconsViewData( + /** Icons that are visible in the container. */ + val visibleKeys: List<IconInfo> = emptyList(), + /** Keys of icons that are "behind" the overflow dot. */ + val collapsedKeys: Set<String> = emptySet(), + /** Whether the overflow dot should be shown regardless if [collapsedKeys] is empty. */ + val forceShowDot: Boolean = false, + ) { + /** The difference between two [IconsViewData]s. */ + data class Diff( + /** Icons added in the newer dataset. */ + val added: List<IconInfo> = emptyList(), + /** Icons removed from the older dataset. */ + val removed: List<String> = emptyList(), + /** + * Groups whose icon was replaced with a single new notification icon. The key of the + * [Map] is the notification group key, and the value is the new icon. + * + * Specifically, this models a difference where the older dataset had notification + * groups with a single icon in the set, and the newer dataset has a single, different + * icon for the same group. A view binder can use this information for special + * animations for this specific change. + */ + val groupReplacements: Map<String, IconInfo> = emptyMap(), + ) + + companion object { + /** + * Returns an [IconsViewData.Diff] calculated from a [new] and [previous][prev] + * [IconsViewData] state. + */ + fun computeDifference(new: IconsViewData, prev: IconsViewData): Diff { + val added: List<IconInfo> = + new.visibleKeys.filter { + it.notifKey !in prev.visibleKeys.asSequence().map { it.notifKey } + } + val removed: List<IconInfo> = + prev.visibleKeys.filter { + it.notifKey !in new.visibleKeys.asSequence().map { it.notifKey } + } + val groupsToShow: Set<IconGroupInfo> = + new.visibleKeys.asSequence().map { it.groupInfo }.toSet() + val replacements: ArrayMap<String, IconInfo> = + removed + .asSequence() + .filter { keyToRemove -> keyToRemove.groupInfo in groupsToShow } + .groupBy { it.groupInfo.groupKey } + .mapValuesNotNullTo(ArrayMap()) { (_, vs) -> + vs.takeIf { it.size == 1 }?.get(0) + } + return Diff(added, removed.map { it.notifKey }, replacements) + } + } + } + + /** An Icon, and keys for unique identification. */ + data class IconInfo( + val sourceIcon: Icon, + val notifKey: String, + val groupKey: String, + ) +} + +/** + * Construct an [IconInfo] out of an [ActiveNotificationModel], or return `null` if one cannot be + * created due to missing information. + */ +fun ActiveNotificationModel.toIconInfo(sourceIcon: Icon?): IconInfo? { + return sourceIcon?.let { + groupKey?.let { groupKey -> + IconInfo( + sourceIcon = sourceIcon, + notifKey = key, + groupKey = groupKey, + ) + } + } +} + +private val IconInfo.groupInfo: IconGroupInfo + get() = IconGroupInfo(sourceIcon, groupKey) + +private data class IconGroupInfo( + val sourceIcon: Icon, + val groupKey: String, +) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as IconGroupInfo + + if (groupKey != other.groupKey) return false + return sourceIcon.sameAs(other.sourceIcon) + } + + override fun hashCode(): Int { + var result = groupKey.hashCode() + result = 31 * result + sourceIcon.type.hashCode() + when (sourceIcon.type) { + Icon.TYPE_BITMAP, + Icon.TYPE_ADAPTIVE_BITMAP -> { + result = 31 * result + sourceIcon.bitmap.hashCode() + } + Icon.TYPE_DATA -> { + result = 31 * result + sourceIcon.dataLength.hashCode() + result = 31 * result + sourceIcon.dataOffset.hashCode() + } + Icon.TYPE_RESOURCE -> { + result = 31 * result + sourceIcon.resId.hashCode() + result = 31 * result + sourceIcon.resPackage.hashCode() + } + Icon.TYPE_URI, + Icon.TYPE_URI_ADAPTIVE_BITMAP -> { + result = 31 * result + sourceIcon.uriString.hashCode() + } + } + return result + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt index ea29cab3b7dcfc62b913c77c11564f306d8d1534..78370baa43116aa991c2def48c61d9981e8ffa15 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt @@ -15,8 +15,36 @@ package com.android.systemui.statusbar.notification.shared +import android.graphics.drawable.Icon + /** Model for entries in the notification stack. */ data class ActiveNotificationModel( /** Notification key associated with this entry. */ val key: String, + /** Notification group key associated with this entry. */ + val groupKey: String?, + /** Is this entry in the ambient / minimized section (lowest priority)? */ + val isAmbient: Boolean, + /** + * Is this entry dismissed? This is `true` when the user has dismissed the notification in the + * UI, but `NotificationManager` has not yet signalled to us that it has received the dismissal. + */ + val isRowDismissed: Boolean, + /** Is this entry in the silent section? */ + val isSilent: Boolean, + /** + * Does this entry represent a conversation, the last message of which was from a remote input + * reply? + */ + val isLastMessageFromReply: Boolean, + /** Is this entry suppressed from appearing in the status bar as an icon? */ + val isSuppressedFromStatusBar: Boolean, + /** Is this entry actively pulsing on AOD or bypassed-keyguard? */ + val isPulsing: Boolean, + /** Icon to display on AOD. */ + val aodIcon: Icon?, + /** Icon to display in the notification shelf. */ + val shelfIcon: Icon?, + /** Icon to display in the status bar. */ + val statusBarIcon: Icon?, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 77d5a2d70d4399ccc10341dace416c14e0d3146b..1e9cfa8d1d3a78bc15c038b79d9a8249ab1dc307 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -107,6 +107,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.footer.ui.view.FooterView; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.logging.NotificationLogger; @@ -694,7 +695,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable super.onFinishInflate(); inflateEmptyShadeView(); - inflateFooterView(); + if (!FooterViewRefactor.isEnabled()) { + inflateFooterView(); + } } /** @@ -729,9 +732,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } void reinflateViews() { - inflateFooterView(); + if (!FooterViewRefactor.isEnabled()) { + inflateFooterView(); + updateFooter(); + } inflateEmptyShadeView(); - updateFooter(); mSectionsManager.reinflateViews(); } @@ -746,7 +751,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @VisibleForTesting public void updateFooter() { - if (mFooterView == null) { + if (mFooterView == null || mController == null) { return; } // TODO: move this logic to controller, which will invoke updateFooterView directly @@ -4546,7 +4551,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return mFooterView != null && mFooterView.isHistoryShown(); } - void setFooterView(@NonNull FooterView footerView) { + /** Bind the {@link FooterView} to the NSSL. */ + public void setFooterView(@NonNull FooterView footerView) { int index = -1; if (mFooterView != null) { index = indexOfChild(mFooterView); @@ -4557,6 +4563,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (mManageButtonClickListener != null) { mFooterView.setManageButtonClickListener(mManageButtonClickListener); } + mFooterView.setClearAllButtonClickListener(v -> { + if (mFooterClearAllListener != null) { + mFooterClearAllListener.onClearAll(); + } + clearNotifications(ROWS_ALL, true /* closeShade */); + footerView.setSecondaryVisible(false /* visible */, true /* animate */); + }); + if (FooterViewRefactor.isEnabled()) { + updateFooter(); + } } public void setEmptyShadeView(EmptyShadeView emptyShadeView) { @@ -4619,7 +4635,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mFooterView.setVisible(visible, animate); mFooterView.setSecondaryVisible(showDismissView, animate); mFooterView.showHistory(showHistory); - mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications); + if (!FooterViewRefactor.isEnabled()) { + mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications); + } } @VisibleForTesting @@ -5370,15 +5388,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @VisibleForTesting protected void inflateFooterView() { + FooterViewRefactor.assertInLegacyMode(); FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_footer, this, false); - footerView.setClearAllButtonClickListener(v -> { - if (mFooterClearAllListener != null) { - mFooterClearAllListener.onClearAll(); - } - clearNotifications(ROWS_ALL, true /* closeShade */); - footerView.setSecondaryVisible(false /* visible */, true /* animate */); - }); setFooterView(footerView); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 8e88a91b5cd97ba7cded084aaa813d52a3d87256..79448b46fa06eba76413ae70f6aaada8c567b1d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -62,7 +62,7 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.flags.Flags; import com.android.systemui.flags.RefactorFlag; import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; @@ -206,7 +206,7 @@ public class NotificationStackScrollLayoutController { private HeadsUpAppearanceController mHeadsUpAppearanceController; private boolean mIsInTransitionToAod = false; - private final FeatureFlags mFeatureFlags; + private final FeatureFlagsClassic mFeatureFlags; private final RefactorFlag mShelfRefactor; private final NotificationTargetsHelper mNotificationTargetsHelper; private final SecureSettings mSecureSettings; @@ -669,7 +669,7 @@ public class NotificationStackScrollLayoutController { NotificationStackScrollLogger logger, NotificationStackSizeCalculator notificationStackSizeCalculator, NotificationIconAreaController notifIconAreaController, - FeatureFlags featureFlags, + FeatureFlagsClassic featureFlags, NotificationTargetsHelper notificationTargetsHelper, SecureSettings secureSettings, NotificationDismissibilityProvider dismissibilityProvider, @@ -832,7 +832,8 @@ public class NotificationStackScrollLayoutController { mViewModel.ifPresent( vm -> NotificationListViewBinder - .bind(mView, vm, mFalsingManager, mFeatureFlags, mNotifIconAreaController)); + .bind(mView, vm, mFalsingManager, mFeatureFlags, mNotifIconAreaController, + mConfigurationController)); collectFlow(mView, mKeyguardTransitionRepo.getTransitions(), this::onKeyguardTransitionChanged); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt index dee3973edb55169daff368612a6e3828b69403d2..a3792cf6a0f07cc8e4bc548998212a5605f6e18e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt @@ -17,14 +17,23 @@ package com.android.systemui.statusbar.notification.stack.ui.viewbinder import android.view.LayoutInflater -import com.android.systemui.res.R -import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager +import com.android.systemui.res.R import com.android.systemui.statusbar.NotificationShelf +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView +import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel import com.android.systemui.statusbar.phone.NotificationIconAreaController +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged +import com.android.systemui.statusbar.policy.onThemeChanged +import com.android.systemui.util.traceSection +import com.android.systemui.util.view.reinflateAndBindLatest +import kotlinx.coroutines.flow.merge /** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */ object NotificationListViewBinder { @@ -33,8 +42,9 @@ object NotificationListViewBinder { view: NotificationStackScrollLayout, viewModel: NotificationListViewModel, falsingManager: FalsingManager, - featureFlags: FeatureFlags, + featureFlags: FeatureFlagsClassic, iconAreaController: NotificationIconAreaController, + configurationController: ConfigurationController, ) { val shelf = LayoutInflater.from(view.context) @@ -47,5 +57,26 @@ object NotificationListViewBinder { iconAreaController ) view.setShelf(shelf) + + viewModel.footer.ifPresent { footerViewModel -> + // The footer needs to be re-inflated every time the theme or the font size changes. + view.repeatWhenAttached { + LayoutInflater.from(view.context).reinflateAndBindLatest( + R.layout.status_bar_notification_footer, + view, + attachToRoot = false, + // TODO(b/305930747): This may lead to duplicate invocations if both flows emit, + // find a solution to only emit one event. + merge( + configurationController.onThemeChanged, + configurationController.onDensityOrFontScaleChanged, + ), + ) { view -> + traceSection("bind FooterView") { + FooterViewBinder.bind(view as FooterView, footerViewModel) + } + } + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt index 11f68e0f663b6970b06f56ee84927f95b8a0a6bf..261371d59a3d983dcf0749352e863f2e6a88a54b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt @@ -17,8 +17,9 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel import dagger.Module import dagger.Provides @@ -28,6 +29,7 @@ import javax.inject.Provider /** ViewModel for the list of notifications. */ class NotificationListViewModel( val shelf: NotificationShelfViewModel, + val footer: Optional<FooterViewModel>, ) @Module @@ -36,12 +38,33 @@ object NotificationListViewModelModule { @Provides @SysUISingleton fun maybeProvideViewModel( - featureFlags: FeatureFlags, + featureFlags: FeatureFlagsClassic, shelfViewModel: Provider<NotificationShelfViewModel>, - ): Optional<NotificationListViewModel> = - if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { - Optional.of(NotificationListViewModel(shelfViewModel.get())) + footerViewModel: Provider<FooterViewModel>, + ): Optional<NotificationListViewModel> { + return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + if (com.android.systemui.Flags.notificationsFooterViewRefactor()) { + Optional.of( + NotificationListViewModel( + shelfViewModel.get(), + Optional.of(footerViewModel.get()) + ) + ) + } else { + Optional.of(NotificationListViewModel(shelfViewModel.get(), Optional.empty())) + } } else { + if (com.android.systemui.Flags.notificationsFooterViewRefactor()) { + throw IllegalStateException( + "The com.android.systemui.notifications_footer_view_refactor flag requires " + + "the notification_shelf_refactor flag to be enabled. First disable the " + + "footer flag using `adb shell device_config put systemui " + + "com.android.systemui.notifications_footer_view_refactor false`, then " + + "enable the notification_shelf_refactor flag in Flag Flipper. " + + "Afterwards, you can try re-enabling the footer refactor flag via adb." + ) + } Optional.empty() } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index 07d3a1cf24c06e9a51810b255eafc1ef4a0fd0b5..2d125462b16ef3c7fff0a16a5d70d8974a9eb0e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -30,7 +30,6 @@ import android.view.View import android.view.WindowManager import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.ActivityIntentHelper -import com.android.systemui.res.R import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.ActivityLaunchAnimator.PendingIntentStarter import com.android.systemui.animation.DelegateLaunchAnimatorController @@ -43,6 +42,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.ActivityStarter.OnDismissAction +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeController import com.android.systemui.shade.ShadeViewController @@ -134,6 +134,19 @@ constructor( ) } + override fun startPendingIntentMaybeDismissingKeyguard( + intent: PendingIntent, + intentSentUiThreadCallback: Runnable?, + animationController: ActivityLaunchAnimator.Controller? + ) { + activityStarterInternal.startPendingIntentDismissingKeyguard( + intent = intent, + intentSentUiThreadCallback = intentSentUiThreadCallback, + animationController = animationController, + showOverLockscreen = true, + ) + } + /** * TODO(b/279084380): Change callers to just call startActivityDismissingKeyguard and deprecate * this. @@ -454,7 +467,7 @@ constructor( !willLaunchResolverActivity && shouldAnimateLaunch(isActivityIntent = true) val animController = - wrapAnimationController( + wrapAnimationControllerForShadeOrStatusBar( animationController = animationController, dismissShade = dismissShade, isLaunchForActivity = true, @@ -547,12 +560,18 @@ constructor( ) } - /** Starts a pending intent after dismissing keyguard. */ + /** + * Starts a pending intent after dismissing keyguard. + * + * This can be called in a background thread (to prevent calls in [ActivityIntentHelper] in + * the main thread). + */ fun startPendingIntentDismissingKeyguard( intent: PendingIntent, intentSentUiThreadCallback: Runnable? = null, associatedView: View? = null, animationController: ActivityLaunchAnimator.Controller? = null, + showOverLockscreen: Boolean = false, ) { val animationController = if (associatedView is ExpandableNotificationRow) { @@ -566,79 +585,103 @@ constructor( lockScreenUserManager.currentUserId, )) + val actuallyShowOverLockscreen = + showOverLockscreen && + intent.isActivity && + activityIntentHelper.wouldPendingShowOverLockscreen( + intent, + lockScreenUserManager.currentUserId + ) + val animate = !willLaunchResolverActivity && animationController != null && - shouldAnimateLaunch(intent.isActivity) + shouldAnimateLaunch(intent.isActivity, actuallyShowOverLockscreen) + + // We wrap animationCallback with a StatusBarLaunchAnimatorController so + // that the shade is collapsed after the animation (or when it is cancelled, + // aborted, etc). + val statusBarController = + wrapAnimationControllerForShadeOrStatusBar( + animationController = animationController, + dismissShade = true, + isLaunchForActivity = intent.isActivity, + ) + val controller = + if (actuallyShowOverLockscreen) { + wrapAnimationControllerForLockscreen(statusBarController) + } else { + statusBarController + } // If we animate, don't collapse the shade and defer the keyguard dismiss (in case we // run the animation on the keyguard). The animation will take care of (instantly) // collapsing the shade and hiding the keyguard once it is done. val collapse = !animate - executeRunnableDismissingKeyguard( - runnable = { - try { - // We wrap animationCallback with a StatusBarLaunchAnimatorController so - // that the shade is collapsed after the animation (or when it is cancelled, - // aborted, etc). - val controller: ActivityLaunchAnimator.Controller? = - wrapAnimationController( - animationController = animationController, - dismissShade = true, - isLaunchForActivity = intent.isActivity, - ) - activityLaunchAnimator.startPendingIntentWithAnimation( - controller, - animate, - intent.creatorPackage, - object : PendingIntentStarter { - override fun startPendingIntent( - animationAdapter: RemoteAnimationAdapter? - ): Int { - val options = - ActivityOptions( - CentralSurfaces.getActivityOptions( - displayId, - animationAdapter - ) + val runnable = Runnable { + try { + activityLaunchAnimator.startPendingIntentWithAnimation( + controller, + animate, + intent.creatorPackage, + actuallyShowOverLockscreen, + object : PendingIntentStarter { + override fun startPendingIntent( + animationAdapter: RemoteAnimationAdapter? + ): Int { + val options = + ActivityOptions( + CentralSurfaces.getActivityOptions( + displayId, + animationAdapter ) - // TODO b/221255671: restrict this to only be set for - // notifications - options.isEligibleForLegacyPermissionPrompt = true - options.setPendingIntentBackgroundActivityStartMode( - ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED ) - return intent.sendAndReturnResult( - null, - 0, - null, - null, - null, - null, - options.toBundle() - ) - } - }, - ) - } catch (e: PendingIntent.CanceledException) { - // the stack trace isn't very helpful here. - // Just log the exception message. - Log.w(TAG, "Sending intent failed: $e") - if (!collapse) { - // executeRunnableDismissingKeyguard did not collapse for us already. - shadeControllerLazy.get().collapseOnMainThread() - } - // TODO: Dismiss Keyguard. - } - if (intent.isActivity) { - assistManagerLazy.get().hideAssist() + // TODO b/221255671: restrict this to only be set for + // notifications + options.isEligibleForLegacyPermissionPrompt = true + options.setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED + ) + return intent.sendAndReturnResult( + null, + 0, + null, + null, + null, + null, + options.toBundle() + ) + } + }, + ) + } catch (e: PendingIntent.CanceledException) { + // the stack trace isn't very helpful here. + // Just log the exception message. + Log.w(TAG, "Sending intent failed: $e") + if (!collapse) { + // executeRunnableDismissingKeyguard did not collapse for us already. + shadeControllerLazy.get().collapseOnMainThread() } - intentSentUiThreadCallback?.let { postOnUiThread(runnable = it) } - }, - afterKeyguardGone = willLaunchResolverActivity, - dismissShade = collapse, - willAnimateOnKeyguard = animate, - ) + // TODO: Dismiss Keyguard. + } + if (intent.isActivity) { + assistManagerLazy.get().hideAssist() + } + intentSentUiThreadCallback?.let { postOnUiThread(runnable = it) } + } + + if (!actuallyShowOverLockscreen) { + postOnUiThread(delay = 0) { + executeRunnableDismissingKeyguard( + runnable = runnable, + afterKeyguardGone = willLaunchResolverActivity, + dismissShade = collapse, + willAnimateOnKeyguard = animate, + ) + } + } else { + postOnUiThread(delay = 0, runnable) + } } /** Starts an Activity. */ @@ -678,71 +721,12 @@ constructor( // Wrap the animation controller to dismiss the shade and set // mIsLaunchingActivityOverLockscreen during the animation. val delegate = - wrapAnimationController( + wrapAnimationControllerForShadeOrStatusBar( animationController = animationController, dismissShade = dismissShade, isLaunchForActivity = true, ) - delegate?.let { - controller = - object : DelegateLaunchAnimatorController(delegate) { - override fun onIntentStarted(willAnimate: Boolean) { - delegate?.onIntentStarted(willAnimate) - if (willAnimate) { - centralSurfaces?.setIsLaunchingActivityOverLockscreen(true) - } - } - - override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { - super.onLaunchAnimationStart(isExpandingFullyAbove) - - // Double check that the keyguard is still showing and not going - // away, but if so set the keyguard occluded. Typically, WM will let - // KeyguardViewMediator know directly, but we're overriding that to - // play the custom launch animation, so we need to take care of that - // here. The unocclude animation is not overridden, so WM will call - // KeyguardViewMediator's unocclude animation runner when the - // activity is exited. - if ( - keyguardStateController.isShowing && - !keyguardStateController.isKeyguardGoingAway - ) { - Log.d(TAG, "Setting occluded = true in #startActivity.") - keyguardViewMediatorLazy - .get() - .setOccluded(true /* isOccluded */, true /* animate */) - } - } - - override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { - // Set mIsLaunchingActivityOverLockscreen to false before actually - // finishing the animation so that we can assume that - // mIsLaunchingActivityOverLockscreen being true means that we will - // collapse the shade (or at least run the post collapse runnables) - // later on. - centralSurfaces?.setIsLaunchingActivityOverLockscreen(false) - delegate?.onLaunchAnimationEnd(isExpandingFullyAbove) - } - - override fun onLaunchAnimationCancelled( - newKeyguardOccludedState: Boolean? - ) { - if (newKeyguardOccludedState != null) { - keyguardViewMediatorLazy - .get() - .setOccluded(newKeyguardOccludedState, false /* animate */) - } - - // Set mIsLaunchingActivityOverLockscreen to false before actually - // finishing the animation so that we can assume that - // mIsLaunchingActivityOverLockscreen being true means that we will - // collapse the shade (or at least run the // post collapse - // runnables) later on. - centralSurfaces?.setIsLaunchingActivityOverLockscreen(false) - delegate.onLaunchAnimationCancelled(newKeyguardOccludedState) - } - } - } + controller = wrapAnimationControllerForLockscreen(delegate) } else if (dismissShade) { // The animation will take care of dismissing the shade at the end of the animation. // If we don't animate, collapse it directly. @@ -874,7 +858,7 @@ constructor( * window. * @param isLaunchForActivity whether the launch is for an activity. */ - private fun wrapAnimationController( + private fun wrapAnimationControllerForShadeOrStatusBar( animationController: ActivityLaunchAnimator.Controller?, dismissShade: Boolean, isLaunchForActivity: Boolean, @@ -909,6 +893,72 @@ constructor( return animationController } + /** + * Wraps an animation controller so that if an activity would be launched on top of the + * lockscreen, the correct flags are set for it to be occluded. + */ + private fun wrapAnimationControllerForLockscreen( + animationController: ActivityLaunchAnimator.Controller? + ): ActivityLaunchAnimator.Controller? { + return animationController?.let { + object : DelegateLaunchAnimatorController(it) { + override fun onIntentStarted(willAnimate: Boolean) { + delegate.onIntentStarted(willAnimate) + if (willAnimate) { + centralSurfaces?.setIsLaunchingActivityOverLockscreen(true) + } + } + + override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { + super.onLaunchAnimationStart(isExpandingFullyAbove) + + // Double check that the keyguard is still showing and not going + // away, but if so set the keyguard occluded. Typically, WM will let + // KeyguardViewMediator know directly, but we're overriding that to + // play the custom launch animation, so we need to take care of that + // here. The unocclude animation is not overridden, so WM will call + // KeyguardViewMediator's unocclude animation runner when the + // activity is exited. + if ( + keyguardStateController.isShowing && + !keyguardStateController.isKeyguardGoingAway + ) { + Log.d(TAG, "Setting occluded = true in #startActivity.") + keyguardViewMediatorLazy + .get() + .setOccluded(true /* isOccluded */, true /* animate */) + } + } + + override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { + // Set mIsLaunchingActivityOverLockscreen to false before actually + // finishing the animation so that we can assume that + // mIsLaunchingActivityOverLockscreen being true means that we will + // collapse the shade (or at least run the post collapse runnables) + // later on. + centralSurfaces?.setIsLaunchingActivityOverLockscreen(false) + delegate.onLaunchAnimationEnd(isExpandingFullyAbove) + } + + override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) { + if (newKeyguardOccludedState != null) { + keyguardViewMediatorLazy + .get() + .setOccluded(newKeyguardOccludedState, false /* animate */) + } + + // Set mIsLaunchingActivityOverLockscreen to false before actually + // finishing the animation so that we can assume that + // mIsLaunchingActivityOverLockscreen being true means that we will + // collapse the shade (or at least run the // post collapse + // runnables) later on. + centralSurfaces?.setIsLaunchingActivityOverLockscreen(false) + delegate.onLaunchAnimationCancelled(newKeyguardOccludedState) + } + } + } + } + /** Retrieves the current user handle to start the Activity. */ private fun getActivityUserHandle(intent: Intent): UserHandle { val packages: Array<String> = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index daa4f1807625572a1079ff1aebd034a2bdfacbc5..cbe9d4b93ead10a01c72d351972c67cffdb79666 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -733,8 +733,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp // Suppress all face auth errors if fingerprint can be used to authenticate if ((biometricSourceType == BiometricSourceType.FACE - && !mUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( - mSelectedUserInteractor.get().getSelectedUserId())) + && !mUpdateMonitor.isUnlockWithFingerprintPossible( + mSelectedUserInteractor.get().getSelectedUserId())) || (biometricSourceType == BiometricSourceType.FINGERPRINT)) { mHapticsInteractor.vibrateError(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index cb85966ca581d789db5e75b47f632a1093ccebb3..8295f65f2eced92dfabf0edde45ffee690d50b96 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -202,7 +202,6 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -296,7 +295,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks; private float mTransitionToFullShadeProgress = 0f; private final NotificationListContainer mNotifListContainer; - private final NotificationExpansionRepository mNotificationExpansionRepository; private boolean mIsShortcutListSearchEnabled; private final KeyguardStateController.Callback mKeyguardStateControllerCallback = @@ -650,7 +648,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { Lazy<NotificationPresenter> notificationPresenterLazy, Lazy<NotificationActivityStarter> notificationActivityStarterLazy, NotificationLaunchAnimatorControllerProvider notifLaunchAnimatorControllerProvider, - NotificationExpansionRepository notificationExpansionRepository, DozeParameters dozeParameters, ScrimController scrimController, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, @@ -759,7 +756,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mPresenterLazy = notificationPresenterLazy; mNotificationActivityStarterLazy = notificationActivityStarterLazy; mNotificationAnimationProvider = notifLaunchAnimatorControllerProvider; - mNotificationExpansionRepository = notificationExpansionRepository; mDozeServiceHost = dozeServiceHost; mPowerManager = powerManager; mDozeParameters = dozeParameters; @@ -2530,7 +2526,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { && mFingerprintManager.get() != null && mFingerprintManager.get().isPowerbuttonFps() && mKeyguardUpdateMonitor - .getCachedIsUnlockWithFingerprintPossible( + .isUnlockWithFingerprintPossible( mUserTracker.getUserId()) && !touchToUnlockAnytime; if (DEBUG_WAKEUP_DELAY) { @@ -3106,7 +3102,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } // TODO: Bring these out of CentralSurfaces. mUserInfoControllerImpl.onDensityOrFontScaleChanged(); - mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); + if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java index d3d11ea4a9f38513f45456f5e6e6eaaafdcd6ca8..66341ba42a6449027c1f770574fadd7173cbc1e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -37,6 +37,8 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; import com.android.systemui.doze.DozeReceiver; +import com.android.systemui.flags.FeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.domain.interactor.DozeInteractor; import com.android.systemui.shade.NotificationShadeWindowViewController; @@ -82,6 +84,7 @@ public final class DozeServiceHost implements DozeHost { private final SysuiStatusBarStateController mStatusBarStateController; private final DeviceProvisionedController mDeviceProvisionedController; private final HeadsUpManager mHeadsUpManager; + private final FeatureFlagsClassic mFeatureFlags; private final BatteryController mBatteryController; private final ScrimController mScrimController; private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; @@ -107,6 +110,7 @@ public final class DozeServiceHost implements DozeHost { WakefulnessLifecycle wakefulnessLifecycle, SysuiStatusBarStateController statusBarStateController, DeviceProvisionedController deviceProvisionedController, + FeatureFlagsClassic featureFlags, HeadsUpManager headsUpManager, BatteryController batteryController, ScrimController scrimController, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, @@ -130,6 +134,7 @@ public final class DozeServiceHost implements DozeHost { mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; mAssistManagerLazy = assistManagerLazy; mDozeScrimController = dozeScrimController; + mFeatureFlags = featureFlags; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mPulseExpansionHandler = pulseExpansionHandler; mNotificationShadeWindowController = notificationShadeWindowController; @@ -173,8 +178,13 @@ public final class DozeServiceHost implements DozeHost { void fireNotificationPulse(NotificationEntry entry) { Runnable pulseSuppressedListener = () -> { - entry.setPulseSuppressed(true); - mNotificationIconAreaController.updateAodNotificationIcons(); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mHeadsUpManager.removeNotification( + entry.getKey(), /* releaseImmediately= */ true, /* animate= */ false); + } else { + entry.setPulseSuppressed(true); + mNotificationIconAreaController.updateAodNotificationIcons(); + } }; Assert.isMainThread(); for (Callback callback : mCallbacks) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index c493eeda707733945b1968a1a46e816eeb7f1b92..8fee5c0487f33a3c4df730d361e98ca29adf4f8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -26,6 +26,8 @@ import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ViewClippingUtil; +import com.android.systemui.flags.FeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; @@ -38,6 +40,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; @@ -104,6 +107,8 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar }; private boolean mAnimationsEnabled = true; private final KeyguardStateController mKeyguardStateController; + private final FeatureFlagsClassic mFeatureFlags; + private final HeadsUpNotificationIconInteractor mHeadsUpNotificationIconInteractor; @VisibleForTesting @Inject @@ -122,6 +127,8 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar NotificationRoundnessManager notificationRoundnessManager, HeadsUpStatusBarView headsUpStatusBarView, Clock clockView, + FeatureFlagsClassic featureFlags, + HeadsUpNotificationIconInteractor headsUpNotificationIconInteractor, @Named(OPERATOR_NAME_FRAME_VIEW) Optional<View> operatorNameViewOptional) { super(headsUpStatusBarView); mNotificationIconAreaController = notificationIconAreaController; @@ -139,6 +146,8 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar mStackScrollerController = stackScrollerController; mShadeViewController = shadeViewController; + mFeatureFlags = featureFlags; + mHeadsUpNotificationIconInteractor = headsUpNotificationIconInteractor; mStackScrollerController.setHeadsUpAppearanceController(this); mClockView = clockView; mOperatorNameViewOptional = operatorNameViewOptional; @@ -170,6 +179,9 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar mHeadsUpManager.addListener(this); mView.setOnDrawingRectChangedListener( () -> updateIsolatedIconLocation(true /* requireUpdate */)); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + updateIsolatedIconLocation(true); + } mWakeUpCoordinator.addListener(this); getShadeHeadsUpTracker().addTrackingHeadsUpListener(mSetTrackingHeadsUp); getShadeHeadsUpTracker().setHeadsUpAppearanceController(this); @@ -185,6 +197,9 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar protected void onViewDetached() { mHeadsUpManager.removeListener(this); mView.setOnDrawingRectChangedListener(null); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mHeadsUpNotificationIconInteractor.setIsolatedIconLocation(null); + } mWakeUpCoordinator.removeListener(this); getShadeHeadsUpTracker().removeTrackingHeadsUpListener(mSetTrackingHeadsUp); getShadeHeadsUpTracker().setHeadsUpAppearanceController(null); @@ -193,8 +208,13 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar } private void updateIsolatedIconLocation(boolean requireStateUpdate) { - mNotificationIconAreaController.setIsolatedIconLocation( - mView.getIconDrawingRect(), requireStateUpdate); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mHeadsUpNotificationIconInteractor + .setIsolatedIconLocation(mView.getIconDrawingRect()); + } else { + mNotificationIconAreaController.setIsolatedIconLocation( + mView.getIconDrawingRect(), requireStateUpdate); + } } @Override @@ -230,9 +250,14 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar setShown(true); animateIsolation = !isExpanded(); } - updateIsolatedIconLocation(false /* requireUpdate */); - mNotificationIconAreaController.showIconIsolated(newEntry == null ? null - : newEntry.getIcons().getStatusBarIcon(), animateIsolation); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey( + newEntry == null ? null : newEntry.getKey()); + } else { + updateIsolatedIconLocation(false /* requireUpdate */); + mNotificationIconAreaController.showIconIsolated(newEntry == null ? null + : newEntry.getIcons().getStatusBarIcon(), animateIsolation); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index 63591d7d6f51461bfdf90aeda89295ce20554ffe..b0183d3fbd407945582fbcda115f350d3fbf4ad2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -58,7 +58,7 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr private var pendingUnlock: PendingUnlock? = null private val listeners = mutableListOf<OnBypassStateChangedListener>() private val faceAuthEnabledChangedCallback = object : KeyguardStateController.Callback { - override fun onFaceAuthEnabledChanged() = notifyListeners() + override fun onFaceEnrolledChanged() = notifyListeners() } @IntDef( @@ -98,7 +98,7 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr FACE_UNLOCK_BYPASS_NEVER -> false else -> field } - return enabled && mKeyguardStateController.isFaceAuthEnabled && + return enabled && mKeyguardStateController.isFaceEnrolled && isPostureAllowedForFaceAuth() } private set(value) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt index 109e77e92b7becc34aa8e8ba5131627149239f69..332984413dde263c6f60abe0fef2e05f37b9883a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt @@ -117,9 +117,7 @@ class KeyguardLiftController @Inject constructor( val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible && !statusBarStateController.isDozing - val userId = selectedUserInteractor.getSelectedUserId() - val isFaceEnabled = keyguardUpdateMonitor.isFaceAuthEnabledForUser(userId) - val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled + val shouldListen = (onKeyguard || bouncerVisible) && keyguardUpdateMonitor.isFaceEnrolled if (shouldListen != isListening) { isListening = shouldListen diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java index f9856b0415e850e24687f9fc88f39f060820001a..4284c96c9966b2264a4c9938e9e5a2ba732c5b99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java @@ -448,7 +448,7 @@ public class LegacyNotificationIconAreaControllerImpl implements } } replacingIcons.removeAll(duplicates); - hostLayout.setReplacingIcons(replacingIcons); + hostLayout.setReplacingIconsLegacy(replacingIcons); final int toRemoveCount = toRemove.size(); for (int i = 0; i < toRemoveCount; i++) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index b15c0fdd5a4c5eddc46a73305ae73af01ed6cdd2..535f6acab5be48469009a565bdc66310caf58e91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -40,6 +40,8 @@ import androidx.collection.ArrayMap; import com.android.app.animation.Interpolators; import com.android.internal.statusbar.StatusBarIcon; import com.android.settingslib.Utils; +import com.android.systemui.flags.Flags; +import com.android.systemui.flags.RefactorFlag; import com.android.systemui.res.R; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.stack.AnimationFilter; @@ -131,6 +133,9 @@ public class NotificationIconContainer extends ViewGroup { } }.setDuration(CONTENT_FADE_DURATION); + private final RefactorFlag mIconContainerRefactorFlag = + RefactorFlag.forView(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR); + /* Maximum number of icons on AOD when also showing overflow dot. */ private int mMaxIconsOnAod; @@ -156,7 +161,8 @@ public class NotificationIconContainer extends ViewGroup { private int mIconSize; private boolean mDisallowNextAnimation; private boolean mAnimationsEnabled = true; - private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons; + private ArrayMap<String, StatusBarIcon> mReplacingIcons; + private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIconsLegacy; // Keep track of the last visible icon so collapsed container can report on its location private IconState mLastVisibleIconState; private IconState mFirstVisibleIconState; @@ -167,6 +173,7 @@ public class NotificationIconContainer extends ViewGroup { private final int[] mAbsolutePosition = new int[2]; private View mIsolatedIconForAnimation; private int mThemedTextColorPrimary; + private Runnable mIsolatedIconAnimationEndRunnable; public NotificationIconContainer(Context context, AttributeSet attrs) { super(context, attrs); @@ -339,23 +346,29 @@ public class NotificationIconContainer extends ViewGroup { } private boolean isReplacingIcon(View child) { - if (mReplacingIcons == null) { - return false; - } if (!(child instanceof StatusBarIconView)) { return false; } StatusBarIconView iconView = (StatusBarIconView) child; Icon sourceIcon = iconView.getSourceIcon(); String groupKey = iconView.getNotification().getGroupKey(); - ArrayList<StatusBarIcon> statusBarIcons = mReplacingIcons.get(groupKey); - if (statusBarIcons != null) { - StatusBarIcon replacedIcon = statusBarIcons.get(0); - if (sourceIcon.sameAs(replacedIcon.icon)) { - return true; + if (mIconContainerRefactorFlag.isEnabled()) { + if (mReplacingIcons == null) { + return false; + } + StatusBarIcon replacedIcon = mReplacingIcons.get(groupKey); + return replacedIcon != null && sourceIcon.sameAs(replacedIcon.icon); + } else { + if (mReplacingIconsLegacy == null) { + return false; } + ArrayList<StatusBarIcon> statusBarIcons = mReplacingIconsLegacy.get(groupKey); + if (statusBarIcons != null) { + StatusBarIcon replacedIcon = statusBarIcons.get(0); + return sourceIcon.sameAs(replacedIcon.icon); + } + return false; } - return false; } @Override @@ -681,14 +694,36 @@ public class NotificationIconContainer extends ViewGroup { mAnimationsEnabled = enabled; } - public void setReplacingIcons(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) { + public void setReplacingIconsLegacy(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) { + mIconContainerRefactorFlag.assertInLegacyMode(); + mReplacingIconsLegacy = replacingIcons; + } + + public void setReplacingIcons(ArrayMap<String, StatusBarIcon> replacingIcons) { + if (mIconContainerRefactorFlag.isUnexpectedlyInLegacyMode()) return; mReplacingIcons = replacingIcons; } + @Deprecated public void showIconIsolated(StatusBarIconView icon, boolean animated) { + mIconContainerRefactorFlag.assertInLegacyMode(); if (animated) { - mIsolatedIconForAnimation = icon != null ? icon : mIsolatedIcon; + showIconIsolatedAnimated(icon, null); + } else { + showIconIsolated(icon); } + } + + public void showIconIsolatedAnimated(StatusBarIconView icon, + @Nullable Runnable onAnimationEnd) { + if (mIconContainerRefactorFlag.isUnexpectedlyInLegacyMode()) return; + mIsolatedIconForAnimation = icon != null ? icon : mIsolatedIcon; + mIsolatedIconAnimationEndRunnable = onAnimationEnd; + showIconIsolated(icon); + } + + public void showIconIsolated(StatusBarIconView icon) { + if (mIconContainerRefactorFlag.isUnexpectedlyInLegacyMode()) return; mIsolatedIcon = icon; updateState(); } @@ -813,6 +848,11 @@ public class NotificationIconContainer extends ViewGroup { animationProperties = UNISOLATION_PROPERTY; animationProperties.setDelay( mIsolatedIcon != null ? CONTENT_FADE_DELAY : 0); + Consumer<Property> endAction = getEndAction(); + if (endAction != null) { + animationProperties.setAnimationEndAction(endAction); + animationProperties.setAnimationCancelAction(endAction); + } } else { animationProperties = UNISOLATION_PROPERTY_OTHERS; animationProperties.setDelay( @@ -836,6 +876,18 @@ public class NotificationIconContainer extends ViewGroup { needsCannedAnimation = false; } + @Nullable + private Consumer<Property> getEndAction() { + if (mIsolatedIconAnimationEndRunnable == null) return null; + final Runnable endRunnable = mIsolatedIconAnimationEndRunnable; + return prop -> { + endRunnable.run(); + if (mIsolatedIconAnimationEndRunnable == endRunnable) { + mIsolatedIconAnimationEndRunnable = null; + } + }; + } + @Override public void initFrom(View view) { super.initFrom(view); 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 63c022c5bb6b145114cfe69395220a6857e0f6aa..e2a4714e611684f019eda9edf310b1c4b9fd9ca8 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 @@ -38,9 +38,13 @@ import com.android.app.animation.Interpolators; import com.android.app.animation.InterpolatorsAndroidX; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; +import com.android.systemui.common.ui.ConfigurationState; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.demomode.DemoMode; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.FeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; @@ -52,8 +56,15 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; +import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder; +import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore; +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel; +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel; +import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.NotificationIconAreaController; +import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.phone.PhoneStatusBarView; +import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; @@ -66,6 +77,7 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder; import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; @@ -83,6 +95,7 @@ import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Inject; + import kotlin.Unit; /** @@ -128,7 +141,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final OngoingCallController mOngoingCallController; private final SystemStatusAnimationScheduler mAnimationScheduler; private final StatusBarLocationPublisher mLocationPublisher; - private final FeatureFlags mFeatureFlags; + private final FeatureFlagsClassic mFeatureFlags; private final NotificationIconAreaController mNotificationIconAreaController; private final ShadeExpansionStateManager mShadeExpansionStateManager; private final StatusBarIconController mStatusBarIconController; @@ -142,6 +155,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final DumpManager mDumpManager; private final StatusBarWindowStateController mStatusBarWindowStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final NotificationIconContainerViewModel mStatusBarIconsViewModel; + private final ConfigurationState mConfigurationState; + private final ConfigurationController mConfigurationController; + private final DozeParameters mDozeParameters; + private final ScreenOffAnimationController mScreenOffAnimationController; + private final NotificationIconContainerViewBinder.IconViewStore mStatusBarIconViewStore; + private final DemoModeController mDemoModeController; private List<String> mBlockedIcons = new ArrayList<>(); private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>(); @@ -211,9 +231,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue StatusBarLocationPublisher locationPublisher, NotificationIconAreaController notificationIconAreaController, ShadeExpansionStateManager shadeExpansionStateManager, - FeatureFlags featureFlags, + FeatureFlagsClassic featureFlags, StatusBarIconController statusBarIconController, - StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory, + DarkIconManager.Factory darkIconManagerFactory, CollapsedStatusBarViewModel collapsedStatusBarViewModel, CollapsedStatusBarViewBinder collapsedStatusBarViewBinder, StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager, @@ -228,8 +248,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Main Executor mainExecutor, DumpManager dumpManager, StatusBarWindowStateController statusBarWindowStateController, - KeyguardUpdateMonitor keyguardUpdateMonitor - ) { + KeyguardUpdateMonitor keyguardUpdateMonitor, + NotificationIconContainerStatusBarViewModel statusBarIconsViewModel, + ConfigurationState configurationState, + ConfigurationController configurationController, + DozeParameters dozeParameters, + ScreenOffAnimationController screenOffAnimationController, + StatusBarNotificationIconViewStore statusBarIconViewStore, + DemoModeController demoModeController) { mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory; mOngoingCallController = ongoingCallController; mAnimationScheduler = animationScheduler; @@ -254,18 +280,55 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mDumpManager = dumpManager; mStatusBarWindowStateController = statusBarWindowStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mStatusBarIconsViewModel = statusBarIconsViewModel; + mConfigurationState = configurationState; + mConfigurationController = configurationController; + mDozeParameters = dozeParameters; + mScreenOffAnimationController = screenOffAnimationController; + mStatusBarIconViewStore = statusBarIconViewStore; + mDemoModeController = demoModeController; } + private final DemoMode mDemoModeCallback = new DemoMode() { + @Override + public List<String> demoCommands() { + return List.of(DemoMode.COMMAND_NOTIFICATIONS); + } + + @Override + public void dispatchDemoCommand(String command, Bundle args) { + if (mNotificationIconAreaInner == null) return; + String visible = args.getString("visible"); + if ("false".equals(visible)) { + mNotificationIconAreaInner.setVisibility(View.INVISIBLE); + } else { + mNotificationIconAreaInner.setVisibility(View.VISIBLE); + } + } + + @Override + public void onDemoModeFinished() { + if (mNotificationIconAreaInner == null) return; + mNotificationIconAreaInner.setVisibility(View.VISIBLE); + } + }; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mStatusBarWindowStateController.addListener(mStatusBarWindowStateListener); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mDemoModeController.addCallback(mDemoModeCallback); + } } @Override public void onDestroy() { super.onDestroy(); mStatusBarWindowStateController.removeListener(mStatusBarWindowStateListener); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mDemoModeController.removeCallback(mDemoModeCallback); + } } @Override @@ -405,14 +468,31 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue /** Initializes views related to the notification icon area. */ public void initNotificationIconArea() { - ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area); - mNotificationIconAreaInner = - mNotificationIconAreaController.getNotificationInnerAreaView(); - if (mNotificationIconAreaInner.getParent() != null) { - ((ViewGroup) mNotificationIconAreaInner.getParent()) - .removeView(mNotificationIconAreaInner); - } - notificationIconArea.addView(mNotificationIconAreaInner); + ViewGroup notificationIconArea = mStatusBar.requireViewById(R.id.notification_icon_area); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mNotificationIconAreaInner = + LayoutInflater.from(getContext()) + .inflate(R.layout.notification_icon_area, notificationIconArea, true); + NotificationIconContainer notificationIcons = + notificationIconArea.requireViewById(R.id.notificationIcons); + NotificationIconContainerViewBinder.bind( + notificationIcons, + mStatusBarIconsViewModel, + mConfigurationState, + mConfigurationController, + mDozeParameters, + mFeatureFlags, + mScreenOffAnimationController, + mStatusBarIconViewStore); + } else { + mNotificationIconAreaInner = + mNotificationIconAreaController.getNotificationInnerAreaView(); + if (mNotificationIconAreaInner.getParent() != null) { + ((ViewGroup) mNotificationIconAreaInner.getParent()) + .removeView(mNotificationIconAreaInner); + } + notificationIconArea.addView(mNotificationIconAreaInner); + } updateNotificationIconAreaAndCallChip(/* animate= */ false); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt index 25d67aff50a9cb5c5d4b0d8dbc2c132bd1a2c2d5..0a2bbe580b99d2365f73c756963b05e47d91024b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt @@ -13,6 +13,7 @@ */ package com.android.systemui.statusbar.policy +import android.content.res.Configuration import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -50,3 +51,20 @@ val ConfigurationController.onThemeChanged: Flow<Unit> addCallback(listener) awaitClose { removeCallback(listener) } } + +/** + * A [Flow] that emits whenever the configuration has changed. + * + * @see ConfigurationController.ConfigurationListener.onConfigChanged + */ +val ConfigurationController.onConfigChanged: Flow<Configuration> + get() = conflatedCallbackFlow { + val listener = + object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration) { + trySend(newConfig) + } + } + addCallback(listener) + awaitClose { removeCallback(listener) } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java index 8929e024c00d18243f57c2e208cec3699bc9ae58..52133ee5b7cd41ac5893e48686d9d4045d079424 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java @@ -130,7 +130,7 @@ public interface KeyguardStateController extends CallbackController<Callback> { /** * If there are faces enrolled and user enabled face auth on keyguard. */ - default boolean isFaceAuthEnabled() { + default boolean isFaceEnrolled() { return false; } @@ -265,9 +265,9 @@ public interface KeyguardStateController extends CallbackController<Callback> { /** * Triggered when face auth becomes available or unavailable. Value should be queried with - * {@link KeyguardStateController#isFaceAuthEnabled()}. + * {@link KeyguardStateController#isFaceEnrolled()}. */ - default void onFaceAuthEnabledChanged() {} + default void onFaceEnrolledChanged() {} /** * Triggered when the notification panel is starting or has finished diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index c62451813699f553dd901d226c8a79abd775b5c7..8cc7e7d21fc2392d5f17b26a5709f250a0ccf0dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -85,7 +85,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum private boolean mTrustManaged; private boolean mTrusted; private boolean mDebugUnlocked = false; - private boolean mFaceAuthEnabled; + private boolean mFaceEnrolled; private float mDismissAmount = 0f; private boolean mDismissingFromTouch = false; @@ -216,7 +216,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum private void notifyKeyguardFaceAuthEnabledChanged() { // Copy the list to allow removal during callback. - new ArrayList<>(mCallbacks).forEach(Callback::onFaceAuthEnabledChanged); + new ArrayList<>(mCallbacks).forEach(Callback::onFaceEnrolledChanged); } private void notifyUnlockedChanged() { @@ -260,16 +260,16 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked); boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user); boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user); - boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user); + boolean faceEnrolled = mKeyguardUpdateMonitor.isFaceEnrolled(user); boolean changed = secure != mSecure || canDismissLockScreen != mCanDismissLockScreen || trustManaged != mTrustManaged || mTrusted != trusted - || mFaceAuthEnabled != faceAuthEnabled; + || mFaceEnrolled != faceEnrolled; if (changed || updateAlways) { mSecure = secure; mCanDismissLockScreen = canDismissLockScreen; mTrusted = trusted; mTrustManaged = trustManaged; - mFaceAuthEnabled = faceAuthEnabled; + mFaceEnrolled = faceEnrolled; mLogger.logKeyguardStateUpdate( mSecure, mCanDismissLockScreen, mTrusted, mTrustManaged); notifyUnlockedChanged(); @@ -290,8 +290,8 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum } @Override - public boolean isFaceAuthEnabled() { - return mFaceAuthEnabled; + public boolean isFaceEnrolled() { + return mFaceEnrolled; } @Override @@ -416,7 +416,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum pw.println(" mTrustManaged: " + mTrustManaged); pw.println(" mTrusted: " + mTrusted); pw.println(" mDebugUnlocked: " + mDebugUnlocked); - pw.println(" mFaceAuthEnabled: " + mFaceAuthEnabled); + pw.println(" mFaceEnrolled: " + mFaceEnrolled); pw.println(" isKeyguardFadingAway: " + isKeyguardFadingAway()); pw.println(" isKeyguardGoingAway: " + isKeyguardGoingAway()); pw.println(" isLaunchTransitionFadingAway: " + isLaunchTransitionFadingAway()); diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt index 0a44bda702388a548f38cb2a70a3d554eda38b2a..f0c7be63b64cc7cbabf313d3d3cbc02b23f19282 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt @@ -4,27 +4,74 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dagger.qualifiers.Tracing +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags +import com.android.systemui.util.TraceUtils.Companion.coroutineTracingIsEnabled +import com.android.systemui.util.tracing.TraceContextElement import dagger.Module import dagger.Provides import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import javax.inject.Qualifier +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +/** Key associated with a [Boolean] flag that enables or disables the coroutine tracing feature. */ +@Qualifier +annotation class CoroutineTracingEnabledKey + +/** + * Same as [@Application], but does not make use of flags. This should only be used when early usage + * of [@Application] would introduce a circular dependency on [FeatureFlagsClassic]. + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class UnflaggedApplication + +/** + * Same as [@Background], but does not make use of flags. This should only be used when early usage + * of [@Application] would introduce a circular dependency on [FeatureFlagsClassic]. + */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class UnflaggedBackground /** Providers for various coroutines-related constructs. */ @Module -object CoroutinesModule { +class CoroutinesModule { @Provides @SysUISingleton @Application fun applicationScope( - @Main dispatcher: CoroutineDispatcher, - ): CoroutineScope = CoroutineScope(dispatcher) + @Main dispatcherContext: CoroutineContext, + ): CoroutineScope = CoroutineScope(dispatcherContext) + + @Provides + @SysUISingleton + @UnflaggedApplication + fun unflaggedApplicationScope(): CoroutineScope = CoroutineScope(Dispatchers.Main.immediate) @Provides @SysUISingleton @Main + @Deprecated( + "Use @Main CoroutineContext instead", + ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext") + ) fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate + @Provides + @SysUISingleton + @Main + fun mainCoroutineContext(@Tracing tracingCoroutineContext: CoroutineContext): CoroutineContext { + return Dispatchers.Main.immediate + tracingCoroutineContext + } + /** * Provide a [CoroutineDispatcher] backed by a thread pool containing at most X threads, where * X is the number of CPU cores available. @@ -37,5 +84,42 @@ object CoroutinesModule { @Provides @SysUISingleton @Background + @Deprecated( + "Use @Background CoroutineContext instead", + ReplaceWith("bgCoroutineContext()", "kotlin.coroutines.CoroutineContext") + ) fun bgDispatcher(): CoroutineDispatcher = Dispatchers.IO + + + @Provides + @Background + @SysUISingleton + fun bgCoroutineContext(@Tracing tracingCoroutineContext: CoroutineContext): CoroutineContext { + return Dispatchers.IO + tracingCoroutineContext + } + + @Provides + @UnflaggedBackground + @SysUISingleton + fun unflaggedBackgroundCoroutineContext(): CoroutineContext { + return Dispatchers.IO + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Provides + @Tracing + @SysUISingleton + fun tracingCoroutineContext( + @CoroutineTracingEnabledKey enableTracing: Boolean + ): CoroutineContext = if (enableTracing) TraceContextElement() else EmptyCoroutineContext + + companion object { + @[Provides CoroutineTracingEnabledKey] + fun provideIsCoroutineTracingEnabledKey(featureFlags: FeatureFlagsClassic): Boolean { + return if (featureFlags.isEnabled(Flags.COROUTINE_TRACING)) { + coroutineTracingIsEnabled = true + true + } else false + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt index 31b90bae27eb116b563acb8d4c407038f2973f95..8fe57e116405a4adf4464179420f8c77c878ed08 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt @@ -61,10 +61,10 @@ fun <T, R> Flow<T>.pairwiseBy(transform: suspend (old: T, new: T) -> R): Flow<R> * * Useful for code that needs to compare the current value to the previous value. */ -fun <T, R> Flow<T>.pairwiseBy( - initialValue: T, - transform: suspend (previousValue: T, newValue: T) -> R, -): Flow<R> = onStart { emit(initialValue) }.pairwiseBy(transform) +fun <S, T : S, R> Flow<T>.pairwiseBy( + initialValue: S, + transform: suspend (previousValue: S, newValue: T) -> R, +): Flow<R> = pairwiseBy(getInitialValue = { initialValue }, transform) /** * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform]. @@ -75,10 +75,16 @@ fun <T, R> Flow<T>.pairwiseBy( * * Useful for code that needs to compare the current value to the previous value. */ -fun <T, R> Flow<T>.pairwiseBy( - getInitialValue: suspend () -> T, - transform: suspend (previousValue: T, newValue: T) -> R, -): Flow<R> = onStart { emit(getInitialValue()) }.pairwiseBy(transform) +fun <S, T : S, R> Flow<T>.pairwiseBy( + getInitialValue: suspend () -> S, + transform: suspend (previousValue: S, newValue: T) -> R, +): Flow<R> = flow { + var previousValue: S = getInitialValue() + collect { newVal -> + emit(transform(previousValue, newVal)) + previousValue = newVal + } +} /** * Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new @@ -86,7 +92,7 @@ fun <T, R> Flow<T>.pairwiseBy( * * Useful for code that needs to compare the current value to the previous value. */ -fun <T> Flow<T>.pairwise(): Flow<WithPrev<T>> = pairwiseBy(::WithPrev) +fun <T> Flow<T>.pairwise(): Flow<WithPrev<T, T>> = pairwiseBy(::WithPrev) /** * Returns a new [Flow] that produces the two most recent emissions from [this]. [initialValue] will @@ -94,10 +100,11 @@ fun <T> Flow<T>.pairwise(): Flow<WithPrev<T>> = pairwiseBy(::WithPrev) * * Useful for code that needs to compare the current value to the previous value. */ -fun <T> Flow<T>.pairwise(initialValue: T): Flow<WithPrev<T>> = pairwiseBy(initialValue, ::WithPrev) +fun <S, T : S> Flow<T>.pairwise(initialValue: S): Flow<WithPrev<S, T>> = + pairwiseBy(initialValue, ::WithPrev) /** Holds a [newValue] emitted from a [Flow], along with the [previousValue] emitted value. */ -data class WithPrev<T>(val previousValue: T, val newValue: T) +data class WithPrev<out S, out T : S>(val previousValue: S, val newValue: T) /** * Returns a new [Flow] that combines the [Set] changes between each emission from [this] using @@ -265,112 +272,120 @@ fun <T> Flow<T>.throttle(periodMs: Long, clock: SystemClock): Flow<T> = channelF * immediately invoke [getValue] to establish its initial value. */ inline fun <T> CoroutineScope.stateFlow( - changedSignals: Flow<Unit>, + changedSignals: Flow<*>, crossinline getValue: () -> T, ): StateFlow<T> = changedSignals.map { getValue() }.stateIn(this, SharingStarted.Eagerly, getValue()) inline fun <T1, T2, T3, T4, T5, T6, R> combine( - flow: Flow<T1>, - flow2: Flow<T2>, - flow3: Flow<T3>, - flow4: Flow<T4>, - flow5: Flow<T5>, - flow6: Flow<T6>, - crossinline transform: suspend (T1, T2, T3, T4, T5, T6) -> R + flow: Flow<T1>, + flow2: Flow<T2>, + flow3: Flow<T3>, + flow4: Flow<T4>, + flow5: Flow<T5>, + flow6: Flow<T6>, + crossinline transform: suspend (T1, T2, T3, T4, T5, T6) -> R ): Flow<R> { - return kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6) { - args: Array<*> -> + return kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6) { args: Array<*> + -> @Suppress("UNCHECKED_CAST") transform( - args[0] as T1, - args[1] as T2, - args[2] as T3, - args[3] as T4, - args[4] as T5, - args[5] as T6 + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6 ) } } inline fun <T1, T2, T3, T4, T5, T6, T7, R> combine( - flow: Flow<T1>, - flow2: Flow<T2>, - flow3: Flow<T3>, - flow4: Flow<T4>, - flow5: Flow<T5>, - flow6: Flow<T6>, - flow7: Flow<T7>, - crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7) -> R + flow: Flow<T1>, + flow2: Flow<T2>, + flow3: Flow<T3>, + flow4: Flow<T4>, + flow5: Flow<T5>, + flow6: Flow<T6>, + flow7: Flow<T7>, + crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7) -> R ): Flow<R> { return kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6, flow7) { args: Array<*> -> @Suppress("UNCHECKED_CAST") transform( - args[0] as T1, - args[1] as T2, - args[2] as T3, - args[3] as T4, - args[4] as T5, - args[5] as T6, - args[6] as T7 + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + args[6] as T7 ) } } inline fun <T1, T2, T3, T4, T5, T6, T7, T8, R> combine( - flow: Flow<T1>, - flow2: Flow<T2>, - flow3: Flow<T3>, - flow4: Flow<T4>, - flow5: Flow<T5>, - flow6: Flow<T6>, - flow7: Flow<T7>, - flow8: Flow<T8>, - crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8) -> R + flow: Flow<T1>, + flow2: Flow<T2>, + flow3: Flow<T3>, + flow4: Flow<T4>, + flow5: Flow<T5>, + flow6: Flow<T6>, + flow7: Flow<T7>, + flow8: Flow<T8>, + crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8) -> R ): Flow<R> { return kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8) { args: Array<*> -> @Suppress("UNCHECKED_CAST") transform( - args[0] as T1, - args[1] as T2, - args[2] as T3, - args[3] as T4, - args[4] as T5, - args[5] as T6, - args[6] as T7, - args[7] as T8 + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + args[6] as T7, + args[7] as T8 ) } } inline fun <T1, T2, T3, T4, T5, T6, T7, T8, T9, R> combine( - flow: Flow<T1>, - flow2: Flow<T2>, - flow3: Flow<T3>, - flow4: Flow<T4>, - flow5: Flow<T5>, - flow6: Flow<T6>, - flow7: Flow<T7>, - flow8: Flow<T8>, - flow9: Flow<T9>, - crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8, T9) -> R + flow: Flow<T1>, + flow2: Flow<T2>, + flow3: Flow<T3>, + flow4: Flow<T4>, + flow5: Flow<T5>, + flow6: Flow<T6>, + flow7: Flow<T7>, + flow8: Flow<T8>, + flow9: Flow<T9>, + crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8, T9) -> R ): Flow<R> { return kotlinx.coroutines.flow.combine( - flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8, flow9 + flow, + flow2, + flow3, + flow4, + flow5, + flow6, + flow7, + flow8, + flow9 ) { args: Array<*> -> @Suppress("UNCHECKED_CAST") transform( - args[0] as T1, - args[1] as T2, - args[2] as T3, - args[3] as T4, - args[4] as T5, - args[5] as T6, - args[6] as T7, - args[6] as T8, - args[6] as T9, + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + args[6] as T7, + args[6] as T8, + args[6] as T9, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt new file mode 100644 index 0000000000000000000000000000000000000000..41cd95b780c9ed575df6602ca75413bd457bb59a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +package com.android.systemui.util.kotlin + +/** Like [mapValues], but discards `null` values returned from [block]. */ +fun <K, V, R> Map<K, V>.mapValuesNotNull(block: (Map.Entry<K, V>) -> R?): Map<K, R> = buildMap { + this@mapValuesNotNull.mapValuesNotNullTo(this, block) +} + +/** Like [mapValuesTo], but discards `null` values returned from [block]. */ +fun <K, V, R, M : MutableMap<in K, in R>> Map<out K, V>.mapValuesNotNullTo( + destination: M, + block: (Map.Entry<K, V>) -> R?, +): M { + for (entry in this) { + block(entry)?.also { destination.put(entry.key, it) } + } + return destination +} diff --git a/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt index 51d2afabd7f9758c078cfc8c85a83aed4147f47a..1112d6f4f25c4df20ee135e116c38c8c0db311d4 100644 --- a/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt +++ b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt @@ -17,26 +17,58 @@ package com.android.systemui.util.ui +import com.android.systemui.util.ui.AnimatedValue.Animating +import com.android.systemui.util.ui.AnimatedValue.NotAnimating +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.transformLatest /** * A state comprised of a [value] of type [T] paired with a boolean indicating whether or not the * [value] [isAnimating] in the UI. */ -data class AnimatedValue<T>( - val value: T, - val isAnimating: Boolean, -) +sealed interface AnimatedValue<out T> { + + /** A [state][value] that is not actively animating in the UI. */ + data class NotAnimating<out T>(val value: T) : AnimatedValue<T> + + /** + * A [state][value] that is actively animating in the UI. Invoking [onStopAnimating] will signal + * the source of the state to stop animating. + */ + data class Animating<out T>( + val value: T, + val onStopAnimating: () -> Unit, + ) : AnimatedValue<T> +} + +/** The state held in this [AnimatedValue]. */ +inline val <T> AnimatedValue<T>.value: T + get() = + when (this) { + is Animating -> value + is NotAnimating -> value + } + +/** Returns whether or not this [AnimatedValue] is animating or not. */ +inline val <T> AnimatedValue<T>.isAnimating: Boolean + get() = this is Animating<T> + +/** + * If this [AnimatedValue] [isAnimating], then signal that the animation should be stopped. + * Otherwise, do nothing. + */ +@Suppress("NOTHING_TO_INLINE") +inline fun AnimatedValue<*>.stopAnimating() { + if (this is Animating) onStopAnimating() +} /** * An event comprised of a [value] of type [T] paired with a [boolean][startAnimating] indicating * whether or not this event should start an animation. */ -data class AnimatableEvent<T>( +data class AnimatableEvent<out T>( val value: T, val startAnimating: Boolean, ) @@ -47,16 +79,87 @@ data class AnimatableEvent<T>( * [AnimatableEvent.startAnimating] value is `true`. When [completionEvents] emits a value, the * [AnimatedValue.isAnimating] will flip to `false`. */ -fun <T> Flow<AnimatableEvent<T>>.toAnimatedValueFlow( - completionEvents: Flow<Any?>, -): Flow<AnimatedValue<T>> = transformLatest { (value, startAnimating) -> - emit(AnimatedValue(value, isAnimating = startAnimating)) - if (startAnimating) { - // Wait for a completion now that we've started animating - completionEvents - .map { Unit } // replace the event so that it's never `null` - .firstOrNull() // `null` indicates an empty flow - // emit the new state if the flow was not empty. - ?.run { emit(AnimatedValue(value, isAnimating = false)) } +fun <T> Flow<AnimatableEvent<T>>.toAnimatedValueFlow(): Flow<AnimatedValue<T>> = + transformLatest { (value, startAnimating) -> + if (startAnimating) { + val onCompleted = CompletableDeferred<Unit>() + emit(Animating(value) { onCompleted.complete(Unit) }) + // Wait for a completion now that we've started animating + onCompleted.await() + } + emit(NotAnimating(value)) + } + +/** + * Zip two [AnimatedValue]s together into a single [AnimatedValue], using [block] to combine the + * [value]s of each. + * + * If either [AnimatedValue] [isAnimating], then the result is also animating. Invoking + * [stopAnimating] on the result is equivalent to invoking [stopAnimating] on each input. + */ +inline fun <A, B, Z> zip( + valueA: AnimatedValue<A>, + valueB: AnimatedValue<B>, + block: (A, B) -> Z, +): AnimatedValue<Z> { + val zippedValue = block(valueA.value, valueB.value) + return when (valueA) { + is Animating -> + when (valueB) { + is Animating -> + Animating(zippedValue) { + valueA.onStopAnimating() + valueB.onStopAnimating() + } + is NotAnimating -> Animating(zippedValue, valueA.onStopAnimating) + } + is NotAnimating -> + when (valueB) { + is Animating -> Animating(zippedValue, valueB.onStopAnimating) + is NotAnimating -> NotAnimating(zippedValue) + } } } + +/** + * Flattens a nested [AnimatedValue], the result of which holds the [value] of the inner + * [AnimatedValue]. + * + * If either the outer or inner [AnimatedValue] [isAnimating], then the flattened result is also + * animating. Invoking [stopAnimating] on the result is equivalent to invoking [stopAnimating] on + * both the outer and inner values. + */ +@Suppress("NOTHING_TO_INLINE") +inline fun <T> AnimatedValue<AnimatedValue<T>>.flatten(): AnimatedValue<T> = flatMap { it } + +/** + * Returns an [AnimatedValue], the [value] of which is the result of the given [value] applied to + * [block]. + */ +inline fun <A, B> AnimatedValue<A>.map(block: (A) -> B): AnimatedValue<B> = + when (this) { + is Animating -> Animating(block(value), ::stopAnimating) + is NotAnimating -> NotAnimating(block(value)) + } + +/** + * Returns an [AnimatedValue] from the result of [block] being invoked on the [value] original + * [AnimatedValue]. + * + * If either the input [AnimatedValue] or the result of [block] [isAnimating], then the flattened + * result is also animating. Invoking [stopAnimating] on the result is equivalent to invoking + * [stopAnimating] on both values. + */ +inline fun <A, B> AnimatedValue<A>.flatMap(block: (A) -> AnimatedValue<B>): AnimatedValue<B> = + when (this) { + is NotAnimating -> block(value) + is Animating -> + when (val inner = block(value)) { + is Animating -> + Animating(inner.value) { + onStopAnimating() + inner.onStopAnimating() + } + is NotAnimating -> Animating(inner.value, onStopAnimating) + } + } diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt index ff1d5b2ea3bfc3f2004c7b8bd8bd78ed1b564a3c..f49ba646e0a1423ce8d82173e39456e484f1b060 100644 --- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt +++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.NotificationShadeDepthController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.collection.NotifCollection import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator @@ -75,6 +76,7 @@ data class TestMocksModule( @get:Provides val keyguardSecurityModel: KeyguardSecurityModel = mock(), @get:Provides val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(), @get:Provides val mediaHierarchyManager: MediaHierarchyManager = mock(), + @get:Provides val notifCollection: NotifCollection = mock(), @get:Provides val notificationListener: NotificationListener = mock(), @get:Provides val notificationLockscreenUserManager: NotificationLockscreenUserManager = mock(), @get:Provides val notificationMediaManager: NotificationMediaManager = mock(), diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt index b31f630a43175df3fe136d71d0a1cabf86e0d84e..e429446f66cfc9d94ad53f856da408a333c6a5c7 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt @@ -262,7 +262,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { // GIVEN fingerprint and face are NOT enrolled activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false) - `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false) + `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false) // WHEN unlock intent is allowed when NO biometrics are enrolled (0) @@ -292,7 +292,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { // GIVEN fingerprint and face are both enrolled activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true) - `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true) // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs // are enrolled @@ -315,7 +315,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { // WHEN fingerprint ONLY enrolled `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false) - `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true) + `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true) // THEN active unlock triggers allowed on unlock intent assertTrue( @@ -326,7 +326,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { // WHEN face ONLY enrolled `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true) - `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false) + `when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false) // THEN active unlock triggers allowed on unlock intent assertTrue( diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index aabdcb7fa6600a7b1ad2c851cd4e5fdc5dbc43ff..efb08876b4f30956d47c10dcba3471a0b74cc912 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -414,6 +414,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } private void setupFingerprintAuth(boolean isClass3) throws RemoteException { + when(mAuthController.isFingerprintEnrolled(anyInt())).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); mFingerprintSensorProperties = List.of( @@ -2898,18 +2899,22 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testFaceSensorProperties() throws RemoteException { + // GIVEN no face sensor properties + when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true); mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(new ArrayList<>()); - assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser( + // THEN face is not possible + assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible( mSelectedUserInteractor.getSelectedUserId())).isFalse(); + // WHEN there are face sensor properties mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties); - biometricsEnabledForCurrentUser(); + // THEN face is possible but face does NOT start listening immediately + assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible( + mSelectedUserInteractor.getSelectedUserId())).isTrue(); verifyFaceAuthenticateNeverCalled(); verifyFaceDetectNeverCalled(); - assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser( - mSelectedUserInteractor.getSelectedUserId())).isTrue(); } @Test @@ -3167,10 +3172,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } private void mockCanBypassLockscreen(boolean canBypass) { - // force update the isFaceEnrolled cache: - mKeyguardUpdateMonitor.isFaceAuthEnabledForUser( - mSelectedUserInteractor.getSelectedUserId()); - mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController); when(mKeyguardBypassController.canBypass()).thenReturn(canBypass); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index 22ebd995b472f1add3c083e214258d93f5d47ea5..f15164e3fdcca7ad66edf027a9e1f09d77470da6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.animation.ValueAnimator; @@ -33,8 +34,8 @@ import android.content.Context; import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; -import android.os.SystemClock; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; @@ -46,13 +47,15 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.model.SysUiState; +import com.android.systemui.res.R; import com.android.systemui.util.settings.SecureSettings; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; @@ -61,22 +64,18 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @LargeTest @RunWith(AndroidTestingRunner.class) +@RunWithLooper(setAsMainLooper = true) public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { + @Rule + public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); private static final float DEFAULT_SCALE = 4.0f; private static final float DEFAULT_CENTER_X = 400.0f; private static final float DEFAULT_CENTER_Y = 500.0f; - // The duration and period couldn't too short, otherwise the ValueAnimator and - // Instrumentation.runOnMainSync won't work in expectation. (b/288926821) - private static final long ANIMATION_DURATION_MS = 600; - private static final long WAIT_FULL_ANIMATION_PERIOD = 1000; - private static final long WAIT_INTERMEDIATE_ANIMATION_PERIOD = 250; private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0); private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0); @@ -105,10 +104,11 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { private WindowMagnificationController mSpyController; private WindowMagnificationAnimationController mWindowMagnificationAnimationController; private Instrumentation mInstrumentation; - private long mWaitingAnimationPeriod; - private long mWaitIntermediateAnimationPeriod; + private long mWaitAnimationDuration; + private long mWaitPartialAnimationDuration; private TestableWindowManager mWindowManager; + private ValueAnimator mValueAnimator; @Before public void setUp() throws Exception { @@ -118,10 +118,14 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { mWindowManager = spy(new TestableWindowManager(wm)); mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); - mWaitingAnimationPeriod = WAIT_FULL_ANIMATION_PERIOD; - mWaitIntermediateAnimationPeriod = WAIT_INTERMEDIATE_ANIMATION_PERIOD; + // Using the animation duration in WindowMagnificationAnimationController for testing. + mWaitAnimationDuration = mContext.getResources() + .getInteger(com.android.internal.R.integer.config_longAnimTime); + mWaitPartialAnimationDuration = mWaitAnimationDuration / 2; + + mValueAnimator = newValueAnimator(); mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( - mContext, newValueAnimator()); + mContext, mValueAnimator); mController = new SpyWindowMagnificationController(mContext, mHandler, mWindowMagnificationAnimationController, mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(), @@ -131,13 +135,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @After public void tearDown() throws Exception { - mInstrumentation.runOnMainSync(() -> mController.deleteWindowMagnification()); + mController.deleteWindowMagnification(); } @Test public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); + enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback); verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( mScaleCaptor.capture(), @@ -161,16 +165,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnificationWithoutCallback_enabled_expectedValues() { - enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); + enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback); final float targetScale = DEFAULT_SCALE + 1.0f; final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; - mInstrumentation.runOnMainSync( - () -> { - mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, - targetCenterX, targetCenterY, null); - }); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, null); verifyFinalSpec(targetScale, targetCenterX, targetCenterY); } @@ -178,12 +179,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback() throws RemoteException { - mInstrumentation.runOnMainSync( - () -> { - mWindowMagnificationAnimationController.enableWindowMagnification(1, - DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationCallback); - }); - SystemClock.sleep(mWaitingAnimationPeriod); + mWindowMagnificationAnimationController.enableWindowMagnification(1, + DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationCallback); verify(mSpyController).enableWindowMagnificationInternal(1, DEFAULT_CENTER_X, DEFAULT_CENTER_Y, 0f, 0f); @@ -193,22 +190,19 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, mAnimationCallback); final float targetScale = DEFAULT_SCALE + 1.0f; final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; - mInstrumentation.runOnMainSync(() -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, - targetCenterX, targetCenterY, mAnimationCallback2); - mCurrentScale.set(mController.getScale()); - mCurrentCenterX.set(mController.getCenterX()); - mCurrentCenterY.set(mController.getCenterY()); - }); - - SystemClock.sleep(mWaitingAnimationPeriod); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, mAnimationCallback2); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + advanceTimeBy(mWaitAnimationDuration); verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( mScaleCaptor.capture(), @@ -226,24 +220,18 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnificationWithUnchanged_enabling_expectedValuesToDefault() - throws InterruptedException { - final CountDownLatch countDownLatch = new CountDownLatch(2); - final MockMagnificationAnimationCallback animationCallback = - new MockMagnificationAnimationCallback(countDownLatch); - - enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, - animationCallback); - mInstrumentation.runOnMainSync( - () -> { - mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, - Float.NaN, Float.NaN, animationCallback); - }); - - assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); + throws RemoteException { + enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, + mAnimationCallback); + + mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, + Float.NaN, Float.NaN, mAnimationCallback2); + advanceTimeBy(mWaitAnimationDuration); + // The callback in 2nd enableWindowMagnification will return true - assertEquals(1, animationCallback.getSuccessCount()); + verify(mAnimationCallback2).onResult(true); // The callback in 1st enableWindowMagnification will return false - assertEquals(1, animationCallback.getFailedCount()); + verify(mAnimationCallback).onResult(false); verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); } @@ -256,16 +244,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; - mInstrumentation.runOnMainSync(() -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, - targetCenterX, targetCenterY, mAnimationCallback); - mCurrentScale.set(mController.getScale()); - mCurrentCenterX.set(mController.getCenterX()); - mCurrentCenterY.set(mController.getCenterY()); - }); - - SystemClock.sleep(mWaitingAnimationPeriod); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, mAnimationCallback); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + advanceTimeBy(mWaitAnimationDuration); verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( mScaleCaptor.capture(), @@ -293,16 +278,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; - mInstrumentation.runOnMainSync(() -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, - targetCenterX, targetCenterY, mAnimationCallback); - mCurrentScale.set(mController.getScale()); - mCurrentCenterX.set(mController.getCenterX()); - mCurrentCenterY.set(mController.getCenterY()); - }); - - SystemClock.sleep(mWaitingAnimationPeriod); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, mAnimationCallback); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + advanceTimeBy(mWaitAnimationDuration); verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( mScaleCaptor.capture(), @@ -331,11 +313,9 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; - mInstrumentation.runOnMainSync(() -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, - targetCenterX, targetCenterY, null); - }); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, null); verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); assertEquals(WindowMagnificationAnimationController.STATE_DISABLED, @@ -346,17 +326,16 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { public void enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, mAnimationCallback); final float targetScale = DEFAULT_SCALE - 1.0f; final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; - mInstrumentation.runOnMainSync(() -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, - targetCenterX, targetCenterY, null); - }); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, null); + verifyFinalSpec(targetScale, targetCenterX, targetCenterY); verify(mAnimationCallback).onResult(false); } @@ -364,15 +343,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, mAnimationCallback); - mInstrumentation.runOnMainSync(() -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, - Float.NaN, Float.NaN, mAnimationCallback2); - }); - SystemClock.sleep(mWaitingAnimationPeriod); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, + Float.NaN, Float.NaN, mAnimationCallback2); + advanceTimeBy(mWaitAnimationDuration); verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), anyFloat()); @@ -383,28 +360,30 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); - deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + enableWindowMagnificationWithoutAnimation(); + deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, mAnimationCallback); final float targetScale = DEFAULT_SCALE + 1.0f; final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; - mInstrumentation.runOnMainSync( - () -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, - targetCenterX, targetCenterY, mAnimationCallback2); - mCurrentScale.set(mController.getScale()); - mCurrentCenterX.set(mController.getCenterX()); - mCurrentCenterY.set(mController.getCenterY()); - }); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, mAnimationCallback2); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + // Current spec shouldn't match given spec. verify(mAnimationCallback2, never()).onResult(anyBoolean()); verify(mAnimationCallback).onResult(false); - SystemClock.sleep(mWaitingAnimationPeriod); + // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it is + // using SystemClock in reverse() (b/305731398). Therefore, we call end() on the animator + // directly to verify the result of animation is correct instead of querying the animation + // frame at a specific timing. + mValueAnimator.end(); - verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( + verify(mSpyController).enableWindowMagnificationInternal( mScaleCaptor.capture(), mCenterXCaptor.capture(), mCenterYCaptor.capture(), mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); @@ -424,18 +403,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback() throws RemoteException { enableWindowMagnificationWithoutAnimation(); - deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, mAnimationCallback); final float targetScale = DEFAULT_SCALE + 1.0f; final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; - mInstrumentation.runOnMainSync( - () -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, - targetCenterX, targetCenterY, null); - }); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, null); verify(mAnimationCallback).onResult(false); verifyFinalSpec(targetScale, targetCenterX, targetCenterY); @@ -444,16 +420,14 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); - deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + enableWindowMagnificationWithoutAnimation(); + deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, mAnimationCallback); - mInstrumentation.runOnMainSync(() -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, - Float.NaN, Float.NaN, mAnimationCallback2); - }); - SystemClock.sleep(mWaitingAnimationPeriod); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, + Float.NaN, Float.NaN, mAnimationCallback2); + advanceTimeBy(mWaitAnimationDuration); verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), anyFloat()); @@ -470,16 +444,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; - mInstrumentation.runOnMainSync(() -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, - targetCenterX, targetCenterY, mAnimationCallback2); - mCurrentScale.set(mController.getScale()); - mCurrentCenterX.set(mController.getCenterX()); - mCurrentCenterY.set(mController.getCenterY()); - }); - - SystemClock.sleep(mWaitingAnimationPeriod); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, mAnimationCallback2); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + advanceTimeBy(mWaitAnimationDuration); verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( mScaleCaptor.capture(), @@ -498,129 +469,110 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { public void enableWindowMagnificationWithOffset_expectedValues() { final float offsetRatio = -0.1f; final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); - mInstrumentation.runOnMainSync(() -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, - windowBounds.exactCenterX(), windowBounds.exactCenterY(), - offsetRatio, offsetRatio, mAnimationCallback); - }); - SystemClock.sleep(mWaitingAnimationPeriod); - final View attachedView = mWindowManager.getAttachedView(); - assertNotNull(attachedView); - final Rect mirrorViewBound = new Rect(); - final View mirrorView = attachedView.findViewById(R.id.surface_view); - assertNotNull(mirrorView); - mirrorView.getBoundsOnScreen(mirrorViewBound); - - assertEquals((int) (offsetRatio * mirrorViewBound.width() / 2), - (int) (mirrorViewBound.exactCenterX() - windowBounds.exactCenterX())); - assertEquals((int) (offsetRatio * mirrorViewBound.height() / 2), - (int) (mirrorViewBound.exactCenterY() - windowBounds.exactCenterY())); - - } - - @Test - public void moveWindowMagnifierToPosition_enabled_expectedValues() - throws InterruptedException { - final CountDownLatch countDownLatch = new CountDownLatch(1); - final MockMagnificationAnimationCallback animationCallback = - new MockMagnificationAnimationCallback(countDownLatch); + + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, + windowBounds.exactCenterX(), windowBounds.exactCenterY(), + offsetRatio, offsetRatio, mAnimationCallback); + advanceTimeBy(mWaitAnimationDuration); + + // We delay the time of verifying to wait for the measurement and layout of the view + mHandler.postDelayed(() -> { + final View attachedView = mWindowManager.getAttachedView(); + assertNotNull(attachedView); + final Rect mirrorViewBound = new Rect(); + final View mirrorView = attachedView.findViewById(R.id.surface_view); + assertNotNull(mirrorView); + mirrorView.getBoundsOnScreen(mirrorViewBound); + + assertEquals((int) (offsetRatio * mirrorViewBound.width() / 2), + (int) (mirrorViewBound.exactCenterX() - windowBounds.exactCenterX())); + assertEquals((int) (offsetRatio * mirrorViewBound.height() / 2), + (int) (mirrorViewBound.exactCenterY() - windowBounds.exactCenterY())); + }, 100); + } + + @Test + public void moveWindowMagnifierToPosition_enabled_expectedValues() throws RemoteException { final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; enableWindowMagnificationWithoutAnimation(); - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( - targetCenterX, targetCenterY, animationCallback); - }); + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + targetCenterX, targetCenterY, mAnimationCallback); + advanceTimeBy(mWaitAnimationDuration); - assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); - assertEquals(1, animationCallback.getSuccessCount()); - assertEquals(0, animationCallback.getFailedCount()); + verify(mAnimationCallback).onResult(true); + verify(mAnimationCallback, never()).onResult(false); verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); } @Test public void moveWindowMagnifierToPositionMultipleTimes_enabled_expectedValuesToLastOne() - throws InterruptedException { - final CountDownLatch countDownLatch = new CountDownLatch(4); - final MockMagnificationAnimationCallback animationCallback = - new MockMagnificationAnimationCallback(countDownLatch); + throws RemoteException { enableWindowMagnificationWithoutAnimation(); - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( - DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, animationCallback); - mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( - DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, animationCallback); - mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( - DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, animationCallback); - mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( - DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, animationCallback); - }); - - assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, mAnimationCallback); + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, mAnimationCallback); + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, mAnimationCallback); + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, mAnimationCallback2); + advanceTimeBy(mWaitAnimationDuration); + // only the last one callback will return true - assertEquals(1, animationCallback.getSuccessCount()); + verify(mAnimationCallback2).onResult(true); // the others will return false - assertEquals(3, animationCallback.getFailedCount()); + verify(mAnimationCallback, times(3)).onResult(false); verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40); } @Test public void moveWindowMagnifierToPosition_enabling_expectedValuesToLastOne() - throws InterruptedException { - final CountDownLatch countDownLatch = new CountDownLatch(2); - final MockMagnificationAnimationCallback animationCallback = - new MockMagnificationAnimationCallback(countDownLatch); + throws RemoteException { final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; - enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, - animationCallback); - mInstrumentation.runOnMainSync( - () -> { - mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( - targetCenterX, targetCenterY, animationCallback); - }); + enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, + mAnimationCallback); + + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + targetCenterX, targetCenterY, mAnimationCallback2); + advanceTimeBy(mWaitAnimationDuration); - assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); // The callback in moveWindowMagnifierToPosition will return true - assertEquals(1, animationCallback.getSuccessCount()); + verify(mAnimationCallback2).onResult(true); // The callback in enableWindowMagnification will return false - assertEquals(1, animationCallback.getFailedCount()); + verify(mAnimationCallback).onResult(false); verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); } @Test public void moveWindowMagnifierToPositionWithCenterUnchanged_enabling_expectedValuesToDefault() - throws InterruptedException { - final CountDownLatch countDownLatch = new CountDownLatch(2); - final MockMagnificationAnimationCallback animationCallback = - new MockMagnificationAnimationCallback(countDownLatch); - - enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, - animationCallback); - mInstrumentation.runOnMainSync( - () -> { - mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( - Float.NaN, Float.NaN, animationCallback); - }); - - assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); + throws RemoteException { + + enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, + mAnimationCallback); + + mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( + Float.NaN, Float.NaN, mAnimationCallback2); + advanceTimeBy(mWaitAnimationDuration); + // The callback in moveWindowMagnifierToPosition will return true - assertEquals(1, animationCallback.getSuccessCount()); + verify(mAnimationCallback2).onResult(true); // The callback in enableWindowMagnification will return false - assertEquals(1, animationCallback.getFailedCount()); + verify(mAnimationCallback).onResult(false); verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); } @Test public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); + enableWindowMagnificationWithoutAnimation(); - enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); + enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback); verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), anyFloat()); @@ -632,7 +584,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { throws RemoteException { enableWindowMagnificationWithoutAnimation(); - deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); + deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback); verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( mScaleCaptor.capture(), @@ -659,7 +611,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback() throws RemoteException { - deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); + deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback); Mockito.verifyNoMoreInteractions(mSpyController); verify(mAnimationCallback).onResult(true); @@ -668,20 +620,23 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + + enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, mAnimationCallback); - mInstrumentation.runOnMainSync( - () -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.deleteWindowMagnification( - mAnimationCallback2); - mCurrentScale.set(mController.getScale()); - mCurrentCenterX.set(mController.getCenterX()); - mCurrentCenterY.set(mController.getCenterY()); - }); - SystemClock.sleep(mWaitingAnimationPeriod); - verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.deleteWindowMagnification( + mAnimationCallback2); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it is + // using SystemClock in reverse() (b/305731398). Therefore, we call end() on the animator + // directly to verify the result of animation is correct instead of querying the animation + // frame at a specific timing. + mValueAnimator.end(); + + verify(mSpyController).enableWindowMagnificationInternal( mScaleCaptor.capture(), mCenterXCaptor.capture(), mCenterYCaptor.capture(), mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); @@ -702,14 +657,11 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, mAnimationCallback); - mInstrumentation.runOnMainSync( - () -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.deleteWindowMagnification(null); - }); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.deleteWindowMagnification(null); verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); verify(mAnimationCallback).onResult(false); @@ -717,13 +669,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void deleteWindowMagnification_disabling_checkStartAndValues() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); - deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + enableWindowMagnificationWithoutAnimation(); + deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, mAnimationCallback); - deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback2); + deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback2); - verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( + verify(mSpyController).enableWindowMagnificationInternal( mScaleCaptor.capture(), mCenterXCaptor.capture(), mCenterYCaptor.capture(), mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); @@ -738,8 +690,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); - deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + enableWindowMagnificationWithoutAnimation(); + deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration, mAnimationCallback); deleteWindowMagnificationAndWaitAnimating(0, null); @@ -757,9 +709,9 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { final float offsetX = 50.0f; final float offsetY = (float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE) - + 1.0f; - mInstrumentation.runOnMainSync( - () -> mController.moveWindowMagnifier(offsetX, offsetY)); + + 1.0f; + + mController.moveWindowMagnifier(offsetX, offsetY); verify(mSpyController).moveWindowMagnifier(offsetX, offsetY); verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y + offsetY); @@ -774,8 +726,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { final float offsetY = (float) Math.floor(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE) - 1.0f; - mInstrumentation.runOnMainSync( - () -> mController.moveWindowMagnifier(offsetX, offsetY)); + + mController.moveWindowMagnifier(offsetX, offsetY); verify(mSpyController).moveWindowMagnifier(offsetX, offsetY); verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y); @@ -790,11 +742,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { (float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE); // while diagonal scrolling enabled, // should move with both offsetX and offsetY without regrading offsetY/offsetX - mInstrumentation.runOnMainSync( - () -> { - mController.setDiagonalScrolling(true); - mController.moveWindowMagnifier(offsetX, offsetY); - }); + mController.setDiagonalScrolling(true); + mController.moveWindowMagnifier(offsetX, offsetY); verify(mSpyController).moveWindowMagnifier(offsetX, offsetY); verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y + offsetY); @@ -806,14 +755,17 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { final float targetCenterY = DEFAULT_CENTER_Y + 100; enableWindowMagnificationWithoutAnimation(); - mInstrumentation.runOnMainSync( - () -> mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY, - mAnimationCallback)); - SystemClock.sleep(mWaitingAnimationPeriod); + mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY, + mAnimationCallback); + advanceTimeBy(mWaitAnimationDuration); verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); } + private void advanceTimeBy(long timeDelta) { + mAnimatorTestRule.advanceTimeBy(timeDelta); + } + private void verifyFinalSpec(float expectedScale, float expectedCenterX, float expectedCenterY) { assertEquals(expectedScale, mController.getScale(), 0f); @@ -822,33 +774,24 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { } private void enableWindowMagnificationWithoutAnimation() { - mInstrumentation.runOnMainSync( - () -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, - DEFAULT_CENTER_X, DEFAULT_CENTER_Y, null); - }); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, + DEFAULT_CENTER_X, DEFAULT_CENTER_Y, null); } private void enableWindowMagnificationAndWaitAnimating(long duration, @Nullable IRemoteMagnificationAnimationCallback callback) { - mInstrumentation.runOnMainSync( - () -> { - Mockito.reset(mSpyController); - mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, - DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback); - }); - SystemClock.sleep(duration); + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, + DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback); + advanceTimeBy(duration); } private void deleteWindowMagnificationAndWaitAnimating(long duration, @Nullable IRemoteMagnificationAnimationCallback callback) { - mInstrumentation.runOnMainSync( - () -> { - resetMockObjects(); - mWindowMagnificationAnimationController.deleteWindowMagnification(callback); - }); - SystemClock.sleep(duration); + resetMockObjects(); + mWindowMagnificationAnimationController.deleteWindowMagnification(callback); + advanceTimeBy(duration); } private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) { @@ -937,9 +880,9 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { } } - private static ValueAnimator newValueAnimator() { + private ValueAnimator newValueAnimator() { final ValueAnimator valueAnimator = new ValueAnimator(); - valueAnimator.setDuration(ANIMATION_DURATION_MS); + valueAnimator.setDuration(mWaitAnimationDuration); valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f)); valueAnimator.setFloatValues(0.0f, 1.0f); return valueAnimator; diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt index f6b284fffa3dd7c9742c9e0475a5cdba29d0f33c..d6aa9ac87d396429db0460fa8b9bd02189e55b41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -423,7 +423,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { mainHandler.setMode(FakeHandler.Mode.QUEUEING) // GIVEN bouncer should be delayed due to face auth - whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true) + whenever(keyguardStateController.isFaceEnrolled).thenReturn(true) whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE)) .thenReturn(true) whenever(keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()).thenReturn(true) @@ -449,7 +449,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { // GIVEN bouncer should not be delayed because device isn't in the right posture for // face auth - whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true) + whenever(keyguardStateController.isFaceEnrolled).thenReturn(true) whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE)) .thenReturn(true) whenever(keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()).thenReturn(false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index 819d08a2086e9f65efdf46bdd8f089a3cca63510..9bb2434f84ac3c92921098509ce4c26ee31c51c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -43,7 +43,6 @@ import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMET import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository @@ -82,6 +81,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest import com.android.systemui.power.domain.interactor.PowerInteractorFactory +import com.android.systemui.res.R import com.android.systemui.statusbar.phone.FakeKeyguardStateController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.user.data.model.SelectionStatus @@ -94,6 +94,8 @@ import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.SystemClock import com.google.common.truth.Truth.assertThat +import java.io.PrintWriter +import java.io.StringWriter import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestDispatcher @@ -116,8 +118,6 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations -import java.io.PrintWriter -import java.io.StringWriter @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -195,9 +195,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } powerRepository = FakePowerRepository() - powerInteractor = PowerInteractorFactory.create( - repository = powerRepository, - ).powerInteractor + powerInteractor = + PowerInteractorFactory.create( + repository = powerRepository, + ) + .powerInteractor val withDeps = KeyguardInteractorFactory.create( @@ -210,10 +212,10 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { keyguardTransitionRepository = FakeKeyguardTransitionRepository() keyguardTransitionInteractor = - KeyguardTransitionInteractorFactory.create( - scope = testScope.backgroundScope, - repository = keyguardTransitionRepository, - keyguardInteractor = keyguardInteractor, + KeyguardTransitionInteractorFactory.create( + scope = testScope.backgroundScope, + repository = keyguardTransitionRepository, + keyguardInteractor = keyguardInteractor, ) .keyguardTransitionInteractor @@ -635,11 +637,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { @Test fun authenticateDoesNotRunWhenDeviceIsGoingToSleep() = - testScope.runTest { - testGatingCheckForFaceAuth { - powerInteractor.setAsleepForTest() - } - } + testScope.runTest { testGatingCheckForFaceAuth { powerInteractor.setAsleepForTest() } } @Test fun authenticateDoesNotRunWhenSecureCameraIsActive() = @@ -736,17 +734,21 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { allPreconditionsToRunFaceAuthAreTrue() Log.i("TEST", "started waking") - keyguardTransitionRepository.sendTransitionStep(TransitionStep( + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( from = KeyguardState.LOCKSCREEN, to = KeyguardState.OFF, transitionState = TransitionState.FINISHED, - )) + ) + ) runCurrent() - keyguardTransitionRepository.sendTransitionStep(TransitionStep( + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( from = KeyguardState.OFF, to = KeyguardState.LOCKSCREEN, transitionState = TransitionState.STARTED, - )) + ) + ) runCurrent() Log.i("TEST", "sending display off") @@ -766,11 +768,13 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { testScope.runTest { testGatingCheckForFaceAuth { powerInteractor.onFinishedWakingUp() - keyguardTransitionRepository.sendTransitionStep(TransitionStep( + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( from = KeyguardState.OFF, to = KeyguardState.LOCKSCREEN, transitionState = TransitionState.FINISHED, - )) + ) + ) runCurrent() displayRepository.emit( @@ -919,11 +923,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { @Test fun detectDoesNotRunWhenDeviceSleepingStartingToSleep() = - testScope.runTest { - testGatingCheckForDetect { - powerInteractor.setAsleepForTest() - } - } + testScope.runTest { testGatingCheckForDetect { powerInteractor.setAsleepForTest() } } @Test fun detectDoesNotRunWhenSecureCameraIsActive() = @@ -1114,6 +1114,32 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true) faceAuthenticateIsCalled() } + @Test + fun authFailedCallAfterAuthLockedOutErrorShouldBeIgnored() = + testScope.runTest { + initCollectors() + allPreconditionsToRunFaceAuthAreTrue() + runCurrent() + assertThat(canFaceAuthRun()).isTrue() + + underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, false) + runCurrent() + + faceAuthenticateIsCalled() + authenticationCallback.value.onAuthenticationError( + FACE_ERROR_LOCKOUT_PERMANENT, + "Too many attempts, face not available" + ) + + val lockoutError = authStatus() as ErrorFaceAuthenticationStatus + assertThat(lockedOut()).isTrue() + assertThat(lockoutError.isLockoutError()).isTrue() + + authenticationCallback.value.onAuthenticationFailed() + runCurrent() + + assertThat(authStatus()).isEqualTo(lockoutError) + } private suspend fun TestScope.testGatingCheckForFaceAuth( gatingCheckModifier: suspend () -> Unit diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt index 43cf1b5ecc403a2ba23be6241cf834467229f2c3..ae47a7bfa63d2f72720c38e18837a67af4e7f4b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt @@ -37,7 +37,6 @@ import android.testing.TestableLooper import android.view.IWindowManager import android.view.View import com.android.internal.logging.MetricsLogger -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.view.LaunchableFrameLayout @@ -48,6 +47,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.res.R import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -343,8 +343,7 @@ class CustomTileTest : SysuiTestCase() { testableLooper.processAllMessages() verify(activityStarter, never()) - .startPendingIntentDismissingKeyguard( - any(), any(), any(ActivityLaunchAnimator.Controller::class.java)) + .startPendingIntentMaybeDismissingKeyguard(any(), nullable(), nullable()) } @Test @@ -357,8 +356,7 @@ class CustomTileTest : SysuiTestCase() { testableLooper.processAllMessages() verify(activityStarter, never()) - .startPendingIntentDismissingKeyguard( - any(), any(), any(ActivityLaunchAnimator.Controller::class.java)) + .startPendingIntentMaybeDismissingKeyguard(any(), nullable(), nullable()) } @Test @@ -373,7 +371,7 @@ class CustomTileTest : SysuiTestCase() { testableLooper.processAllMessages() verify(activityStarter) - .startPendingIntentDismissingKeyguard( + .startPendingIntentMaybeDismissingKeyguard( eq(pi), nullable(), nullable<ActivityLaunchAnimator.Controller>()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index e1345d28dbd0e4206519df738345b4f51e1a0764..c1f2d0cc518f5c57c6bd88983cdcf402b64dd5d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -22,11 +22,11 @@ import android.os.PowerManager import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags as AconfigFlags import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.Flags import com.android.systemui.model.SysUiState import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest @@ -45,7 +45,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import org.junit.Assume.assumeTrue +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.clearInvocations @@ -87,6 +87,11 @@ class SceneContainerStartableTest : SysuiTestCase() { powerInteractor = powerInteractor, ) + @Before + fun setUp() { + mSetFlagsRule.enableFlags(AconfigFlags.FLAG_SCENE_CONTAINER) + } + @Test fun hydrateVisibility() = testScope.runTest { @@ -520,7 +525,6 @@ class SceneContainerStartableTest : SysuiTestCase() { authenticationMethod: AuthenticationMethodModel? = null, startsAwake: Boolean = true, ): MutableStateFlow<ObservableTransitionState> { - assumeTrue(Flags.SCENE_CONTAINER_ENABLED) sceneContainerFlags.enabled = true utils.deviceEntryRepository.setUnlocked(isDeviceUnlocked) utils.deviceEntryRepository.setBypassEnabled(isBypassEnabled) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt index f6195aa9c3f77c1dea4bb099159695169251c3c8..0bed4d0d376a353026c940af71624cf83b5a1e57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt @@ -16,7 +16,10 @@ package com.android.systemui.scene.shared.flag +import android.platform.test.flag.junit.SetFlagsRule import androidx.test.filters.SmallTest +import com.android.systemui.FakeFeatureFlagsImpl +import com.android.systemui.Flags as AconfigFlags import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags @@ -24,8 +27,8 @@ import com.android.systemui.flags.ReleasedFlag import com.android.systemui.flags.ResourceBooleanFlag import com.android.systemui.flags.UnreleasedFlag import com.google.common.truth.Truth.assertThat -import org.junit.Assume.assumeTrue import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -36,27 +39,50 @@ internal class SceneContainerFlagsTest( private val testCase: TestCase, ) : SysuiTestCase() { + @Rule @JvmField val setFlagsRule: SetFlagsRule = SetFlagsRule() + private lateinit var underTest: SceneContainerFlags @Before fun setUp() { + // TODO(b/283300105): remove this reflection setting once the hard-coded + // Flags.SCENE_CONTAINER_ENABLED is no longer needed. + val field = Flags::class.java.getField("SCENE_CONTAINER_ENABLED") + field.isAccessible = true + field.set(null, true) + val featureFlags = FakeFeatureFlagsClassic().apply { - SceneContainerFlagsImpl.flags.forEach { flag -> - when (flag) { - is ResourceBooleanFlag -> set(flag, testCase.areAllFlagsSet) - is ReleasedFlag -> set(flag, testCase.areAllFlagsSet) - is UnreleasedFlag -> set(flag, testCase.areAllFlagsSet) - else -> error("Unsupported flag type ${flag.javaClass}") + SceneContainerFlagsImpl.classicFlagTokens.forEach { flagToken -> + when (flagToken) { + is ResourceBooleanFlag -> set(flagToken, testCase.areAllFlagsSet) + is ReleasedFlag -> set(flagToken, testCase.areAllFlagsSet) + is UnreleasedFlag -> set(flagToken, testCase.areAllFlagsSet) + else -> error("Unsupported flag type ${flagToken.javaClass}") } } } - underTest = SceneContainerFlagsImpl(featureFlags, testCase.isComposeAvailable) + // TODO(b/306421592): get the aconfig FeatureFlags from the SetFlagsRule. + val aconfigFlags = FakeFeatureFlagsImpl() + + listOf( + AconfigFlags.FLAG_SCENE_CONTAINER, + ) + .forEach { flagToken -> + setFlagsRule.enableFlags(flagToken) + aconfigFlags.setFlag(flagToken, testCase.areAllFlagsSet) + } + + underTest = + SceneContainerFlagsImpl( + featureFlagsClassic = featureFlags, + featureFlags = aconfigFlags, + isComposeAvailable = testCase.isComposeAvailable, + ) } @Test fun isEnabled() { - assumeTrue(Flags.SCENE_CONTAINER_ENABLED) assertThat(underTest.isEnabled()).isEqualTo(testCase.expectedEnabled) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index b421e1b454773e5c82e2cea122d06d8c769858fb..4e3e165c83bb6e392b6769edaddaad294d202038 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -79,7 +79,8 @@ import com.android.systemui.statusbar.NotificationInsetsController import com.android.systemui.statusbar.NotificationShadeDepthController import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.SysuiStatusBarStateController -import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository +import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository +import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.CentralSurfaces @@ -163,7 +164,9 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock lateinit var sysUIKeyEventHandler: SysUIKeyEventHandler @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor - private val notificationExpansionRepository = NotificationExpansionRepository() + private val notificationLaunchAnimationRepository = NotificationLaunchAnimationRepository() + private val notificationLaunchAnimationInteractor = + NotificationLaunchAnimationInteractor(notificationLaunchAnimationRepository) private lateinit var fakeClock: FakeSystemClock private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> @@ -234,7 +237,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { primaryBouncerToGoneTransitionViewModel, mCommunalViewModel, mCommunalRepository, - notificationExpansionRepository, + notificationLaunchAnimationInteractor, featureFlagsClassic, fakeClock, BouncerMessageInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index 9c571015f750da7b6f96896309b762e488605f84..3d5d26ab194e8023a88dab039eeff114c915b2de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -69,7 +69,8 @@ import com.android.systemui.statusbar.NotificationInsetsController import com.android.systemui.statusbar.NotificationShadeDepthController import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.SysuiStatusBarStateController -import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository +import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository +import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController @@ -226,7 +227,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { primaryBouncerToGoneTransitionViewModel, mCommunalViewModel, mCommunalRepository, - NotificationExpansionRepository(), + NotificationLaunchAnimationInteractor(NotificationLaunchAnimationRepository()), featureFlags, FakeSystemClock(), BouncerMessageInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 2bcad1d27b62eee9b571ca809a6aa707ed04b0d7..dd3ac927ebdf7ea98e300742ac77249847ce76e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -73,10 +73,10 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.TrustGrantFlags; import com.android.settingslib.fuelgauge.BatteryStatus; -import com.android.systemui.res.R; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; +import com.android.systemui.res.R; import org.junit.Test; import org.junit.runner.RunWith; @@ -1543,7 +1543,7 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll private void setupFingerprintUnlockPossible(boolean possible) { when(mKeyguardUpdateMonitor - .getCachedIsUnlockWithFingerprintPossible(getCurrentUser())) + .isUnlockWithFingerprintPossible(getCurrentUser())) .thenReturn(possible); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java index 34126798e87e98aad3f5379cdc7cc8a778d0a24a..2b3fd34cedbf6d0b411deec2588fa4845257dde4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java @@ -37,8 +37,12 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.flags.FakeFeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.PluginManager; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; +import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository; +import com.android.systemui.statusbar.domain.interactor.SilentNotificationStatusIconsVisibilityInteractor; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -68,9 +72,14 @@ public class NotificationListenerTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); + FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic(); + featureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR); mListener = new NotificationListener( mContext, + featureFlags, mNotificationManager, + new SilentNotificationStatusIconsVisibilityInteractor( + new NotificationListenerSettingsRepository()), mFakeSystemClock, mFakeExecutor, mPluginManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt index 235ac5c2e9cc33ca065d4dbb789926e9e0463a84..605936372e7acbe0874a4aad2aabf3c2888da7f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt @@ -11,7 +11,8 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.res.R import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder -import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository +import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository +import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.stack.NotificationListContainer @@ -44,7 +45,8 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { private lateinit var notificationTestHelper: NotificationTestHelper private lateinit var notification: ExpandableNotificationRow private lateinit var controller: NotificationLaunchAnimatorController - private val notificationExpansionRepository = NotificationExpansionRepository() + private val notificationLaunchAnimationInteractor = + NotificationLaunchAnimationInteractor(NotificationLaunchAnimationRepository()) private val testScope = TestScope() @@ -57,16 +59,17 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { fun setUp() { allowTestableLooperAsMainThread() notificationTestHelper = - NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)) + NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)) notification = notificationTestHelper.createRow() - controller = NotificationLaunchAnimatorController( - notificationExpansionRepository, + controller = + NotificationLaunchAnimatorController( + notificationLaunchAnimationInteractor, notificationListContainer, headsUpManager, notification, jankMonitor, onFinishAnimationCallback - ) + ) } private fun flagNotificationAsHun() { @@ -80,13 +83,14 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification)) assertFalse(notification.entry.isExpandAnimationRunning) - val isExpandAnimationRunning by testScope.collectLastValue( - notificationExpansionRepository.isExpandAnimationRunning - ) + val isExpandAnimationRunning by + testScope.collectLastValue( + notificationLaunchAnimationInteractor.isLaunchAnimationRunning + ) assertFalse(isExpandAnimationRunning!!) - verify(headsUpManager).removeNotification( - notificationKey, true /* releaseImmediately */, true /* animate */) + verify(headsUpManager) + .removeNotification(notificationKey, true /* releaseImmediately */, true /* animate */) verify(onFinishAnimationCallback).run() } @@ -97,13 +101,14 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification)) assertFalse(notification.entry.isExpandAnimationRunning) - val isExpandAnimationRunning by testScope.collectLastValue( - notificationExpansionRepository.isExpandAnimationRunning - ) + val isExpandAnimationRunning by + testScope.collectLastValue( + notificationLaunchAnimationInteractor.isLaunchAnimationRunning + ) assertFalse(isExpandAnimationRunning!!) - verify(headsUpManager).removeNotification( - notificationKey, true /* releaseImmediately */, true /* animate */) + verify(headsUpManager) + .removeNotification(notificationKey, true /* releaseImmediately */, true /* animate */) verify(onFinishAnimationCallback).run() } @@ -114,13 +119,14 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { assertFalse(HeadsUpUtil.isClickedHeadsUpNotification(notification)) assertFalse(notification.entry.isExpandAnimationRunning) - val isExpandAnimationRunning by testScope.collectLastValue( - notificationExpansionRepository.isExpandAnimationRunning - ) + val isExpandAnimationRunning by + testScope.collectLastValue( + notificationLaunchAnimationInteractor.isLaunchAnimationRunning + ) assertFalse(isExpandAnimationRunning!!) - verify(headsUpManager).removeNotification( - notificationKey, true /* releaseImmediately */, false /* animate */) + verify(headsUpManager) + .removeNotification(notificationKey, true /* releaseImmediately */, false /* animate */) verify(onFinishAnimationCallback).run() } @@ -128,15 +134,21 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { fun testAlertingSummaryHunRemovedOnNonAlertingChildLaunch() { val GROUP_KEY = "test_group_key" - val summary = NotificationEntryBuilder().setGroup(mContext, GROUP_KEY).setId(0).apply { - modifyNotification(mContext).setSmallIcon(R.drawable.ic_person) - }.build() + val summary = + NotificationEntryBuilder() + .setGroup(mContext, GROUP_KEY) + .setId(0) + .apply { modifyNotification(mContext).setSmallIcon(R.drawable.ic_person) } + .build() assertNotSame(summary.key, notification.entry.key) notificationTestHelper.createRow(summary) - GroupEntryBuilder().setKey(GROUP_KEY).setSummary(summary).addChild(notification.entry) - .build() + GroupEntryBuilder() + .setKey(GROUP_KEY) + .setSummary(summary) + .addChild(notification.entry) + .build() assertSame(summary, notification.entry.parent?.summary) `when`(headsUpManager.isAlerting(notificationKey)).thenReturn(false) @@ -147,10 +159,14 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { controller.onLaunchAnimationEnd(isExpandingFullyAbove = true) - verify(headsUpManager).removeNotification( - summary.key, true /* releaseImmediately */, false /* animate */) - verify(headsUpManager, never()).removeNotification( - notification.entry.key, true /* releaseImmediately */, false /* animate */) + verify(headsUpManager) + .removeNotification(summary.key, true /* releaseImmediately */, false /* animate */) + verify(headsUpManager, never()) + .removeNotification( + notification.entry.key, + true /* releaseImmediately */, + false /* animate */ + ) } @Test @@ -158,9 +174,10 @@ class NotificationLaunchAnimatorControllerTest : SysuiTestCase() { controller.onIntentStarted(willAnimate = true) assertTrue(notification.entry.isExpandAnimationRunning) - val isExpandAnimationRunning by testScope.collectLastValue( - notificationExpansionRepository.isExpandAnimationRunning - ) + val isExpandAnimationRunning by + testScope.collectLastValue( + notificationLaunchAnimationInteractor.isLaunchAnimationRunning + ) assertTrue(isExpandAnimationRunning!!) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt similarity index 55% rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepositoryTest.kt rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt index f28d9ab06bc7c5b91a95162e3df45557c490c61f..a0faab56345204f8adec33707027f8c17e45a08a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt @@ -14,42 +14,44 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification.data.repository +package com.android.systemui.statusbar.notification.domain.interactor import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test @SmallTest -class NotificationExpansionRepositoryTest : SysuiTestCase() { - private val underTest = NotificationExpansionRepository() +class NotificationLaunchAnimationInteractorTest : SysuiTestCase() { + private val repository = NotificationLaunchAnimationRepository() + private val underTest = NotificationLaunchAnimationInteractor(repository) @Test - fun setIsExpandAnimationRunning_startsAsFalse() = runTest { - val latest by collectLastValue(underTest.isExpandAnimationRunning) + fun setIsLaunchAnimationRunning_startsAsFalse() = runTest { + val latest by collectLastValue(underTest.isLaunchAnimationRunning) assertThat(latest).isFalse() } @Test - fun setIsExpandAnimationRunning_false_emitsTrue() = runTest { - val latest by collectLastValue(underTest.isExpandAnimationRunning) + fun setIsLaunchAnimationRunning_false_emitsTrue() = runTest { + val latest by collectLastValue(underTest.isLaunchAnimationRunning) - underTest.setIsExpandAnimationRunning(true) + underTest.setIsLaunchAnimationRunning(true) assertThat(latest).isTrue() } @Test - fun setIsExpandAnimationRunning_false_emitsFalse() = runTest { - val latest by collectLastValue(underTest.isExpandAnimationRunning) - underTest.setIsExpandAnimationRunning(true) + fun setIsLaunchAnimationRunning_false_emitsFalse() = runTest { + val latest by collectLastValue(underTest.isLaunchAnimationRunning) + underTest.setIsLaunchAnimationRunning(true) // WHEN the animation is no longer running - underTest.setIsExpandAnimationRunning(false) + underTest.setIsLaunchAnimationRunning(false) // THEN the flow emits false assertThat(latest).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt index 8c5c43986f8ae30ded02fc311edb174d4c86ebd3..ca8ea4eb9d1a5d89d4944f3951a6dad9594cafd8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt @@ -35,6 +35,7 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() { private val underTest = RenderNotificationListInteractor( notifsRepository, + sectionStyleProvider = mock(), ) @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java index f72142ffc6d7cca5041bde553d6d242f896de3d5..cc87d7ce377b619690c5e26154dee38b8f551bf5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java @@ -22,8 +22,15 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import android.content.Context; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; import android.view.View; @@ -31,6 +38,7 @@ import android.widget.TextView; import androidx.test.filters.SmallTest; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.res.R; @@ -44,9 +52,13 @@ public class FooterViewTest extends SysuiTestCase { FooterView mView; + Context mSpyContext = spy(mContext); + @Before public void setUp() { - mView = (FooterView) LayoutInflater.from(mContext).inflate( + mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR); + + mView = (FooterView) LayoutInflater.from(mSpyContext).inflate( R.layout.status_bar_notification_footer, null, false); mView.setDuration(0); } @@ -101,6 +113,37 @@ public class FooterViewTest extends SysuiTestCase { mView.setSecondaryVisible(true /* visible */, true /* animate */); } + @Test + public void testSetMessageString_resourceOnlyFetchedOnce() { + mView.setMessageString(R.string.unlock_to_see_notif_text); + verify(mSpyContext).getString(eq(R.string.unlock_to_see_notif_text)); + + clearInvocations(mSpyContext); + + assertThat(((TextView) mView.findViewById(R.id.unlock_prompt_footer)) + .getText().toString()).contains("Unlock"); + + // Set it a few more times, it shouldn't lead to the resource being fetched again + mView.setMessageString(R.string.unlock_to_see_notif_text); + mView.setMessageString(R.string.unlock_to_see_notif_text); + + verify(mSpyContext, never()).getString(anyInt()); + } + + @Test + public void testSetMessageIcon_resourceOnlyFetchedOnce() { + mView.setMessageIcon(R.drawable.ic_friction_lock_closed); + verify(mSpyContext).getDrawable(eq(R.drawable.ic_friction_lock_closed)); + + clearInvocations(mSpyContext); + + // Set it a few more times, it shouldn't lead to the resource being fetched again + mView.setMessageIcon(R.drawable.ic_friction_lock_closed); + mView.setMessageIcon(R.drawable.ic_friction_lock_closed); + + verify(mSpyContext, never()).getDrawable(anyInt()); + } + @Test public void testSetFooterLabelVisible() { mView.setFooterLabelVisible(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..57a7c3c7e2bfecc81a45b50aaaabcb73a2cd83d2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 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.footer.ui.viewmodel + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository +import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class FooterViewModelTest : SysuiTestCase() { + private val repository = ActiveNotificationListRepository() + private val interactor = SeenNotificationsInteractor(repository) + private val underTest = FooterViewModel(interactor) + + @Test + fun testMessageVisible_whenFilteredNotifications() = runTest { + val message by collectLastValue(underTest.message) + + repository.hasFilteredOutSeenNotifications.value = true + + assertThat(message?.visible).isTrue() + } + + @Test + fun testMessageVisible_whenNoFilteredNotifications() = runTest { + val message by collectLastValue(underTest.message) + + repository.hasFilteredOutSeenNotifications.value = false + + assertThat(message?.visible).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..ec80e5f8821c26cd9072155412ebb3125e6b2e34 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2023 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.icon.domain.interactor + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.SysUITestModule +import com.android.TestMocksModule +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository +import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository +import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository +import com.android.systemui.statusbar.notification.shared.activeNotificationModel +import com.android.systemui.statusbar.notification.shared.byIsAmbient +import com.android.systemui.statusbar.notification.shared.byIsLastMessageFromReply +import com.android.systemui.statusbar.notification.shared.byIsPulsing +import com.android.systemui.statusbar.notification.shared.byIsRowDismissed +import com.android.systemui.statusbar.notification.shared.byIsSilent +import com.android.systemui.statusbar.notification.shared.byIsSuppressedFromStatusBar +import com.android.systemui.statusbar.notification.shared.byKey +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.wm.shell.bubbles.Bubbles +import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component +import java.util.Optional +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class NotificationIconsInteractorTest : SysuiTestCase() { + + private val bubbles: Bubbles = mock() + + @Component(modules = [SysUITestModule::class]) + @SysUISingleton + interface TestComponent { + val underTest: NotificationIconsInteractor + + val activeNotificationListRepository: ActiveNotificationListRepository + val keyguardViewStateRepository: FakeNotificationsKeyguardViewStateRepository + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create(@BindsInstance test: SysuiTestCase, mocks: TestMocksModule): TestComponent + } + } + + val testComponent: TestComponent = + DaggerNotificationIconsInteractorTest_TestComponent.factory() + .create(test = this, mocks = TestMocksModule(bubbles = Optional.of(bubbles))) + + @Before + fun setup() = + with(testComponent) { + activeNotificationListRepository.activeNotifications.value = + testIcons.associateBy { it.key } + } + + @Test + fun filteredEntrySet() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.filteredNotifSet()) + assertThat(filteredSet).containsExactlyElementsIn(testIcons) + } + } + + @Test + fun filteredEntrySet_noExpandedBubbles() = + with(testComponent) { + testScope.runTest { + whenever(bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true) + val filteredSet by collectLastValue(underTest.filteredNotifSet()) + assertThat(filteredSet).comparingElementsUsing(byKey).doesNotContain("notif1") + } + } + + @Test + fun filteredEntrySet_noAmbient() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.filteredNotifSet(showAmbient = false)) + assertThat(filteredSet).comparingElementsUsing(byIsAmbient).doesNotContain(true) + assertThat(filteredSet) + .comparingElementsUsing(byIsSuppressedFromStatusBar) + .doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_noLowPriority() = + with(testComponent) { + testScope.runTest { + val filteredSet by + collectLastValue(underTest.filteredNotifSet(showLowPriority = false)) + assertThat(filteredSet).comparingElementsUsing(byIsSilent).doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_noDismissed() = + with(testComponent) { + testScope.runTest { + val filteredSet by + collectLastValue(underTest.filteredNotifSet(showDismissed = false)) + assertThat(filteredSet) + .comparingElementsUsing(byIsRowDismissed) + .doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_noRepliedMessages() = + with(testComponent) { + testScope.runTest { + val filteredSet by + collectLastValue(underTest.filteredNotifSet(showRepliedMessages = false)) + assertThat(filteredSet) + .comparingElementsUsing(byIsLastMessageFromReply) + .doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_noPulsing_notifsNotFullyHidden() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.filteredNotifSet(showPulsing = false)) + keyguardViewStateRepository.setNotificationsFullyHidden(false) + assertThat(filteredSet).comparingElementsUsing(byIsPulsing).doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_noPulsing_notifsFullyHidden() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.filteredNotifSet(showPulsing = false)) + keyguardViewStateRepository.setNotificationsFullyHidden(true) + assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true) + } + } +} + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() { + + private val bubbles: Bubbles = mock() + + @Component(modules = [SysUITestModule::class]) + @SysUISingleton + interface TestComponent { + val underTest: AlwaysOnDisplayNotificationIconsInteractor + + val activeNotificationListRepository: ActiveNotificationListRepository + val deviceEntryRepository: FakeDeviceEntryRepository + val keyguardViewStateRepository: FakeNotificationsKeyguardViewStateRepository + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create(@BindsInstance test: SysuiTestCase, mocks: TestMocksModule): TestComponent + } + } + + val testComponent: TestComponent = + DaggerAlwaysOnDisplayNotificationIconsInteractorTest_TestComponent.factory() + .create(test = this, mocks = TestMocksModule(bubbles = Optional.of(bubbles))) + + @Before + fun setup() = + with(testComponent) { + activeNotificationListRepository.activeNotifications.value = + testIcons.associateBy { it.key } + } + + @Test + fun filteredEntrySet_noExpandedBubbles() = + with(testComponent) { + testScope.runTest { + whenever(bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true) + val filteredSet by collectLastValue(underTest.aodNotifs) + assertThat(filteredSet).comparingElementsUsing(byKey).doesNotContain("notif1") + } + } + + @Test + fun filteredEntrySet_noAmbient() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.aodNotifs) + assertThat(filteredSet).comparingElementsUsing(byIsAmbient).doesNotContain(true) + assertThat(filteredSet) + .comparingElementsUsing(byIsSuppressedFromStatusBar) + .doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_noDismissed() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.aodNotifs) + assertThat(filteredSet) + .comparingElementsUsing(byIsRowDismissed) + .doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_noRepliedMessages() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.aodNotifs) + assertThat(filteredSet) + .comparingElementsUsing(byIsLastMessageFromReply) + .doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_showPulsing_notifsNotFullyHidden_bypassDisabled() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.aodNotifs) + deviceEntryRepository.setBypassEnabled(false) + keyguardViewStateRepository.setNotificationsFullyHidden(false) + assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true) + } + } + + @Test + fun filteredEntrySet_showPulsing_notifsFullyHidden_bypassDisabled() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.aodNotifs) + deviceEntryRepository.setBypassEnabled(false) + keyguardViewStateRepository.setNotificationsFullyHidden(true) + assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true) + } + } + + @Test + fun filteredEntrySet_noPulsing_notifsNotFullyHidden_bypassEnabled() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.aodNotifs) + deviceEntryRepository.setBypassEnabled(true) + keyguardViewStateRepository.setNotificationsFullyHidden(false) + assertThat(filteredSet).comparingElementsUsing(byIsPulsing).doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_showPulsing_notifsFullyHidden_bypassEnabled() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.aodNotifs) + deviceEntryRepository.setBypassEnabled(true) + keyguardViewStateRepository.setNotificationsFullyHidden(true) + assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true) + } + } +} + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class StatusBarNotificationIconsInteractorTest : SysuiTestCase() { + + private val bubbles: Bubbles = mock() + + @Component(modules = [SysUITestModule::class]) + @SysUISingleton + interface TestComponent { + val underTest: StatusBarNotificationIconsInteractor + + val activeNotificationListRepository: ActiveNotificationListRepository + val keyguardViewStateRepository: FakeNotificationsKeyguardViewStateRepository + val notificationListenerSettingsRepository: NotificationListenerSettingsRepository + val testScope: TestScope + + @Component.Factory + interface Factory { + fun create(@BindsInstance test: SysuiTestCase, mocks: TestMocksModule): TestComponent + } + } + + val testComponent: TestComponent = + DaggerStatusBarNotificationIconsInteractorTest_TestComponent.factory() + .create(test = this, mocks = TestMocksModule(bubbles = Optional.of(bubbles))) + + @Before + fun setup() = + with(testComponent) { + activeNotificationListRepository.activeNotifications.value = + testIcons.associateBy { it.key } + } + + @Test + fun filteredEntrySet_noExpandedBubbles() = + with(testComponent) { + testScope.runTest { + whenever(bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true) + val filteredSet by collectLastValue(underTest.statusBarNotifs) + assertThat(filteredSet).comparingElementsUsing(byKey).doesNotContain("notif1") + } + } + + @Test + fun filteredEntrySet_noAmbient() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.statusBarNotifs) + assertThat(filteredSet).comparingElementsUsing(byIsAmbient).doesNotContain(true) + assertThat(filteredSet) + .comparingElementsUsing(byIsSuppressedFromStatusBar) + .doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_noLowPriority_whenDontShowSilentIcons() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.statusBarNotifs) + notificationListenerSettingsRepository.showSilentStatusIcons.value = false + assertThat(filteredSet).comparingElementsUsing(byIsSilent).doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_showLowPriority_whenShowSilentIcons() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.statusBarNotifs) + notificationListenerSettingsRepository.showSilentStatusIcons.value = true + assertThat(filteredSet).comparingElementsUsing(byIsSilent).contains(true) + } + } + + @Test + fun filteredEntrySet_noDismissed() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.statusBarNotifs) + assertThat(filteredSet) + .comparingElementsUsing(byIsRowDismissed) + .doesNotContain(true) + } + } + + @Test + fun filteredEntrySet_noRepliedMessages() = + with(testComponent) { + testScope.runTest { + val filteredSet by collectLastValue(underTest.statusBarNotifs) + assertThat(filteredSet) + .comparingElementsUsing(byIsLastMessageFromReply) + .doesNotContain(true) + } + } +} + +private val testIcons = + listOf( + activeNotificationModel( + key = "notif1", + ), + activeNotificationModel( + key = "notif2", + isAmbient = true, + ), + activeNotificationModel( + key = "notif3", + isRowDismissed = true, + ), + activeNotificationModel( + key = "notif4", + isSilent = true, + ), + activeNotificationModel( + key = "notif5", + isLastMessageFromReply = true, + ), + activeNotificationModel( + key = "notif6", + isSuppressedFromStatusBar = true, + ), + activeNotificationModel( + key = "notif7", + isPulsing = true, + ), + ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt deleted file mode 100644 index e57986ddfa184c3bb715c7635f83e2ab78f1c2fa..0000000000000000000000000000000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2023 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.icon.ui.viewbinder - -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper -import androidx.test.filters.SmallTest -import com.android.SysUITestModule -import com.android.TestMocksModule -import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FakeFeatureFlagsClassicModule -import com.android.systemui.flags.Flags -import com.android.systemui.statusbar.phone.DozeParameters -import com.android.systemui.user.domain.UserDomainLayerModule -import dagger.BindsInstance -import dagger.Component -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@RunWithLooper(setAsMainLooper = true) -class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() { - - @Mock private lateinit var dozeParams: DozeParameters - - private lateinit var testComponent: TestComponent - private val underTest - get() = testComponent.underTest - - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - allowTestableLooperAsMainThread() - - testComponent = - DaggerNotificationIconAreaControllerViewBinderWrapperImplTest_TestComponent.factory() - .create( - test = this, - featureFlags = - FakeFeatureFlagsClassicModule { - set(Flags.FACE_AUTH_REFACTOR, value = false) - set(Flags.MIGRATE_KEYGUARD_STATUS_VIEW, value = false) - }, - mocks = - TestMocksModule( - dozeParameters = dozeParams, - ), - ) - } - - @Test - fun testNotificationIcons_settingHideIcons() { - underTest.settingsListener.onStatusBarIconsBehaviorChanged(true) - assertFalse(underTest.shouldShowLowPriorityIcons()) - } - - @Test - fun testNotificationIcons_settingShowIcons() { - underTest.settingsListener.onStatusBarIconsBehaviorChanged(false) - assertTrue(underTest.shouldShowLowPriorityIcons()) - } - - @SysUISingleton - @Component( - modules = - [ - SysUITestModule::class, - BiometricsDomainLayerModule::class, - UserDomainLayerModule::class, - ] - ) - interface TestComponent { - - val underTest: NotificationIconAreaControllerViewBinderWrapperImpl - - @Component.Factory - interface Factory { - fun create( - @BindsInstance test: SysuiTestCase, - mocks: TestMocksModule, - featureFlags: FakeFeatureFlagsClassicModule, - ): TestComponent - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt index 31efebbc5b60deeaad8cc866573e16c39358c57a..41c7071a616d3bd3fce864f6ffa12b859d07f60b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt @@ -44,7 +44,9 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.whenever -import com.android.systemui.util.ui.AnimatedValue +import com.android.systemui.util.ui.isAnimating +import com.android.systemui.util.ui.stopAnimating +import com.android.systemui.util.ui.value import com.google.common.truth.Truth.assertThat import dagger.BindsInstance import dagger.Component @@ -243,6 +245,7 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { ) ) val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() keyguardRepository.setKeyguardShowing(true) keyguardRepository.setKeyguardOccluded(false) @@ -266,6 +269,7 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { fun isDozing_startAodTransition() = scope.runTest { val isDozing by collectLastValue(underTest.isDozing) + runCurrent() keyguardTransitionRepository.sendTransitionStep( TransitionStep( from = KeyguardState.GONE, @@ -274,13 +278,15 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { ) ) runCurrent() - assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = true)) + assertThat(isDozing?.value).isTrue() + assertThat(isDozing?.isAnimating).isTrue() } @Test fun isDozing_startDozeTransition() = scope.runTest { val isDozing by collectLastValue(underTest.isDozing) + runCurrent() keyguardTransitionRepository.sendTransitionStep( TransitionStep( from = KeyguardState.GONE, @@ -289,13 +295,15 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { ) ) runCurrent() - assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = false)) + assertThat(isDozing?.value).isTrue() + assertThat(isDozing?.isAnimating).isFalse() } @Test fun isDozing_startDozeToAodTransition() = scope.runTest { val isDozing by collectLastValue(underTest.isDozing) + runCurrent() keyguardTransitionRepository.sendTransitionStep( TransitionStep( from = KeyguardState.DOZING, @@ -304,13 +312,15 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { ) ) runCurrent() - assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = true)) + assertThat(isDozing?.value).isTrue() + assertThat(isDozing?.isAnimating).isTrue() } @Test fun isNotDozing_startAodToGoneTransition() = scope.runTest { val isDozing by collectLastValue(underTest.isDozing) + runCurrent() keyguardTransitionRepository.sendTransitionStep( TransitionStep( from = KeyguardState.AOD, @@ -319,13 +329,15 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { ) ) runCurrent() - assertThat(isDozing).isEqualTo(AnimatedValue(false, isAnimating = true)) + assertThat(isDozing?.value).isFalse() + assertThat(isDozing?.isAnimating).isTrue() } @Test fun isDozing_stopAnimation() = scope.runTest { val isDozing by collectLastValue(underTest.isDozing) + runCurrent() keyguardTransitionRepository.sendTransitionStep( TransitionStep( from = KeyguardState.AOD, @@ -335,7 +347,8 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { ) runCurrent() - underTest.completeDozeAnimation() + assertThat(isDozing?.isAnimating).isEqualTo(true) + isDozing?.stopAnimating() runCurrent() assertThat(isDozing?.isAnimating).isEqualTo(false) @@ -345,6 +358,7 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { fun isNotVisible_pulseExpanding() = scope.runTest { val isVisible by collectLastValue(underTest.isVisible) + runCurrent() notifsKeyguardRepository.setPulseExpanding(true) runCurrent() @@ -355,6 +369,7 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { fun isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() = scope.runTest { val isVisible by collectLastValue(underTest.isVisible) + runCurrent() keyguardTransitionRepository.sendTransitionStep( TransitionStep( to = KeyguardState.GONE, @@ -364,13 +379,15 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { whenever(screenOffAnimController.shouldShowAodIconsWhenShade()).thenReturn(false) runCurrent() - assertThat(isVisible).isEqualTo(AnimatedValue(false, isAnimating = false)) + assertThat(isVisible?.value).isFalse() + assertThat(isVisible?.isAnimating).isFalse() } @Test fun isVisible_bypassEnabled() = scope.runTest { val isVisible by collectLastValue(underTest.isVisible) + runCurrent() deviceEntryRepository.setBypassEnabled(true) runCurrent() @@ -381,6 +398,7 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { fun isNotVisible_pulseExpanding_notBypassing() = scope.runTest { val isVisible by collectLastValue(underTest.isVisible) + runCurrent() notifsKeyguardRepository.setPulseExpanding(true) deviceEntryRepository.setBypassEnabled(false) runCurrent() @@ -398,26 +416,30 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { notifsKeyguardRepository.setNotificationsFullyHidden(true) runCurrent() - assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = true)) + assertThat(isVisible?.value).isTrue() + assertThat(isVisible?.isAnimating).isTrue() } @Test fun isVisible_notifsFullyHidden_bypassDisabled_aodDisabled() = scope.runTest { val isVisible by collectLastValue(underTest.isVisible) + runCurrent() notifsKeyguardRepository.setPulseExpanding(false) deviceEntryRepository.setBypassEnabled(false) whenever(dozeParams.alwaysOn).thenReturn(false) notifsKeyguardRepository.setNotificationsFullyHidden(true) runCurrent() - assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = false)) + assertThat(isVisible?.value).isTrue() + assertThat(isVisible?.isAnimating).isFalse() } @Test fun isVisible_notifsFullyHidden_bypassDisabled_displayNeedsBlanking() = scope.runTest { val isVisible by collectLastValue(underTest.isVisible) + runCurrent() notifsKeyguardRepository.setPulseExpanding(false) deviceEntryRepository.setBypassEnabled(false) whenever(dozeParams.alwaysOn).thenReturn(true) @@ -425,7 +447,8 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { notifsKeyguardRepository.setNotificationsFullyHidden(true) runCurrent() - assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = false)) + assertThat(isVisible?.value).isTrue() + assertThat(isVisible?.isAnimating).isFalse() } @Test @@ -440,13 +463,15 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { notifsKeyguardRepository.setNotificationsFullyHidden(true) runCurrent() - assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = true)) + assertThat(isVisible?.value).isTrue() + assertThat(isVisible?.isAnimating).isTrue() } @Test fun isVisible_stopAnimation() = scope.runTest { val isVisible by collectLastValue(underTest.isVisible) + runCurrent() notifsKeyguardRepository.setPulseExpanding(false) deviceEntryRepository.setBypassEnabled(false) whenever(dozeParams.alwaysOn).thenReturn(true) @@ -454,7 +479,8 @@ class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() { notifsKeyguardRepository.setNotificationsFullyHidden(true) runCurrent() - underTest.completeVisibilityAnimation() + assertThat(isVisible?.isAnimating).isEqualTo(true) + isVisible?.stopAnimating() runCurrent() assertThat(isVisible?.isAnimating).isEqualTo(false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt index e21ebeb77f96f1d4a58e7609b1958800fa1300d4..ba68fbb981e6d0c73d2b73e99fa7fb4d8432e9f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.icon.ui.viewmodel import android.graphics.Rect +import android.graphics.drawable.Icon import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.SysUITestModule @@ -39,12 +40,19 @@ import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState +import com.android.systemui.shade.data.repository.FakeShadeRepository +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository +import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationIconViewStateRepository +import com.android.systemui.statusbar.notification.shared.activeNotificationModel import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher import com.android.systemui.statusbar.phone.data.repository.FakeDarkIconRepository import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository import com.android.systemui.user.domain.UserDomainLayerModule +import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.ui.isAnimating +import com.android.systemui.util.ui.value import com.google.common.truth.Truth.assertThat import dagger.BindsInstance import dagger.Component @@ -327,6 +335,58 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { } } + @Test + fun isolatedIcon_animateOnAppear_shadeCollapsed() = + with(testComponent) { + scope.runTest { + val icon: Icon = mock() + shadeRepository.setLegacyShadeExpansion(0f) + activeNotificationsRepository.activeNotifications.value = + listOf( + activeNotificationModel( + key = "notif1", + groupKey = "group", + statusBarIcon = icon + ) + ) + .associateBy { it.key } + val isolatedIcon by collectLastValue(underTest.isolatedIcon) + runCurrent() + + headsUpViewStateRepository.isolatedNotification.value = "notif1" + runCurrent() + + assertThat(isolatedIcon?.value?.notifKey).isEqualTo("notif1") + assertThat(isolatedIcon?.isAnimating).isTrue() + } + } + + @Test + fun isolatedIcon_dontAnimateOnAppear_shadeExpanded() = + with(testComponent) { + scope.runTest { + val icon: Icon = mock() + shadeRepository.setLegacyShadeExpansion(.5f) + activeNotificationsRepository.activeNotifications.value = + listOf( + activeNotificationModel( + key = "notif1", + groupKey = "group", + statusBarIcon = icon + ) + ) + .associateBy { it.key } + val isolatedIcon by collectLastValue(underTest.isolatedIcon) + runCurrent() + + headsUpViewStateRepository.isolatedNotification.value = "notif1" + runCurrent() + + assertThat(isolatedIcon?.value?.notifKey).isEqualTo("notif1") + assertThat(isolatedIcon?.isAnimating).isFalse() + } + } + @SysUISingleton @Component( modules = @@ -340,11 +400,14 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { val underTest: NotificationIconContainerStatusBarViewModel + val activeNotificationsRepository: ActiveNotificationListRepository val darkIconRepository: FakeDarkIconRepository val deviceProvisioningRepository: FakeDeviceProvisioningRepository + val headsUpViewStateRepository: HeadsUpNotificationIconViewStateRepository val keyguardTransitionRepository: FakeKeyguardTransitionRepository val keyguardRepository: FakeKeyguardRepository val powerRepository: FakePowerRepository + val shadeRepository: FakeShadeRepository val scope: TestScope @Component.Factory diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt index ed9405814e6aa61baa96a3b85bd89784b09857c8..ca105f3e52ea08ae3ec58bce97202085bfe32d1a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt @@ -15,7 +15,53 @@ package com.android.systemui.statusbar.notification.shared +import android.graphics.drawable.Icon import com.google.common.truth.Correspondence val byKey: Correspondence<ActiveNotificationModel, String> = Correspondence.transforming({ it?.key }, "has a key of") +val byIsAmbient: Correspondence<ActiveNotificationModel, Boolean> = + Correspondence.transforming({ it?.isAmbient }, "has an isAmbient value of") +val byIsSuppressedFromStatusBar: Correspondence<ActiveNotificationModel, Boolean> = + Correspondence.transforming( + { it?.isSuppressedFromStatusBar }, + "has an isSuppressedFromStatusBar value of", + ) +val byIsSilent: Correspondence<ActiveNotificationModel, Boolean> = + Correspondence.transforming({ it?.isSilent }, "has an isSilent value of") +val byIsRowDismissed: Correspondence<ActiveNotificationModel, Boolean> = + Correspondence.transforming({ it?.isRowDismissed }, "has an isRowDismissed value of") +val byIsLastMessageFromReply: Correspondence<ActiveNotificationModel, Boolean> = + Correspondence.transforming( + { it?.isLastMessageFromReply }, + "has an isLastMessageFromReply value of" + ) +val byIsPulsing: Correspondence<ActiveNotificationModel, Boolean> = + Correspondence.transforming({ it?.isPulsing }, "has an isPulsing value of") + +fun activeNotificationModel( + key: String, + groupKey: String? = null, + isAmbient: Boolean = false, + isRowDismissed: Boolean = false, + isSilent: Boolean = false, + isLastMessageFromReply: Boolean = false, + isSuppressedFromStatusBar: Boolean = false, + isPulsing: Boolean = false, + aodIcon: Icon? = null, + shelfIcon: Icon? = null, + statusBarIcon: Icon? = null, +) = + ActiveNotificationModel( + key = key, + groupKey = groupKey, + isAmbient = isAmbient, + isRowDismissed = isRowDismissed, + isSilent = isSilent, + isLastMessageFromReply = isLastMessageFromReply, + isSuppressedFromStatusBar = isSuppressedFromStatusBar, + isPulsing = isPulsing, + aodIcon = aodIcon, + shelfIcon = shelfIcon, + statusBarIcon = statusBarIcon, + ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 033c96ae84b0924e42bbcba800bf60219dde355e..8f36d4f5bf6c4ea1202849b35c813a2a9cd7def3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification.stack; import static android.view.View.GONE; import static android.view.WindowInsets.Type.ime; +import static com.android.systemui.Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR; +import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.RUBBER_BAND_FACTOR_NORMAL; @@ -165,6 +167,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR); mFeatureFlags.setDefault(Flags.NEW_AOD_TRANSITION); mFeatureFlags.setDefault(Flags.UNCLEARED_TRANSIENT_HUN_FIX); + // Some tests in this file test the FooterView. Since we're refactoring the FooterView + // business logic out of the NSSL, the behavior tested in this file will eventually be + // tested directly in the new FooterView stack. For now, we just want to make sure that the + // old behavior is preserved when the flag is off. + setFlagDefault(mSetFlagsRule, FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR); // Inject dependencies before initializing the layout mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index 49906dca03442e2fce30bec4bf06fa7d049f8f18..08ef477651748cb02e26e43828e43e8749949444 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -62,6 +62,10 @@ class StackScrollAlgorithmTest : SysuiTestCase() { ) private val testableResources = mContext.getOrCreateTestableResources() + private val maxPanelHeight = + mContext.resources.displayMetrics.heightPixels - + px(R.dimen.notification_panel_margin_top) - + px(R.dimen.notification_panel_margin_bottom) private fun px(@DimenRes id: Int): Float = testableResources.resources.getDimensionPixelSize(id).toFloat() @@ -147,7 +151,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() { stackScrollAlgorithm.initView(context) hostView.removeAllViews() hostView.addView(emptyShadeView) - ambientState.layoutMaxHeight = 1280 + ambientState.layoutMaxHeight = maxPanelHeight.toInt() stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) @@ -155,7 +159,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() { context.resources.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom) val fullHeight = ambientState.layoutMaxHeight + marginBottom - ambientState.stackY val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f - assertThat(emptyShadeView.viewState?.yTranslation).isEqualTo(centeredY) + assertThat(emptyShadeView.viewState.yTranslation).isEqualTo(centeredY) } @Test @@ -356,7 +360,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() { whenever(notificationRow.canViewBeCleared()).thenReturn(false) ambientState.isClearAllInProgress = true ambientState.isShadeExpanded = true - ambientState.stackEndHeight = 1000f // plenty space for the footer in the stack + ambientState.stackEndHeight = maxPanelHeight // plenty space for the footer in the stack hostView.addView(footerView) stackScrollAlgorithm.resetViewStates(ambientState, 0) @@ -370,7 +374,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() { whenever(notificationRow.canViewBeCleared()).thenReturn(true) ambientState.isClearAllInProgress = true ambientState.isShadeExpanded = true - ambientState.stackEndHeight = 1000f // plenty space for the footer in the stack + ambientState.stackEndHeight = maxPanelHeight // plenty space for the footer in the stack hostView.addView(footerView) stackScrollAlgorithm.resetViewStates(ambientState, 0) @@ -382,7 +386,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() { fun resetViewStates_clearAllInProgress_allRowsRemoved_emptyShade_footerHidden() { ambientState.isClearAllInProgress = true ambientState.isShadeExpanded = true - ambientState.stackEndHeight = 1000f // plenty space for the footer in the stack + ambientState.stackEndHeight = maxPanelHeight // plenty space for the footer in the stack hostView.removeAllViews() // remove all rows hostView.addView(footerView) @@ -1006,7 +1010,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() { } private fun resetViewStates_stackMargin_changesHunYTranslation() { - val stackTopMargin = 50 + val stackTopMargin = bigGap.toInt() // a gap smaller than the headsUpInset val headsUpTranslationY = stackScrollAlgorithm.mHeadsUpInset - stackTopMargin // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt index 68f2728c9acee97a754fcd1c2845c1179aee2654..7de05add288490d379683f3b9cad98b9669264ba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt @@ -19,11 +19,14 @@ import android.content.Intent import android.os.RemoteException import android.os.UserHandle import android.testing.AndroidTestingRunner +import android.view.View +import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.ActivityIntentHelper import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.LaunchableView import com.android.systemui.assist.AssistManager import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle @@ -41,6 +44,7 @@ import com.android.systemui.statusbar.window.StatusBarWindowController 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.mockito.nullable import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -49,6 +53,7 @@ import java.util.Optional import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.mock @@ -116,15 +121,50 @@ class ActivityStarterImplTest : SysuiTestCase() { @Test fun startPendingIntentDismissingKeyguard_keyguardShowing_dismissWithAction() { val pendingIntent = mock(PendingIntent::class.java) + whenever(pendingIntent.isActivity).thenReturn(true) whenever(keyguardStateController.isShowing).thenReturn(true) whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) underTest.startPendingIntentDismissingKeyguard(pendingIntent) + mainExecutor.runAllReady() verify(statusBarKeyguardViewManager) .dismissWithAction(any(OnDismissAction::class.java), eq(null), anyBoolean(), eq(null)) } + @Test + fun startPendingIntentMaybeDismissingKeyguard_keyguardShowing_showOverLockscreen_activityLaunchAnimator() { + val pendingIntent = mock(PendingIntent::class.java) + val parent = FrameLayout(context) + val view = + object : View(context), LaunchableView { + override fun setShouldBlockVisibilityChanges(block: Boolean) {} + } + parent.addView(view) + val controller = ActivityLaunchAnimator.Controller.fromView(view) + whenever(pendingIntent.isActivity).thenReturn(true) + whenever(keyguardStateController.isShowing).thenReturn(true) + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) + whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt())) + .thenReturn(true) + + underTest.startPendingIntentMaybeDismissingKeyguard( + intent = pendingIntent, + animationController = controller, + intentSentUiThreadCallback = null, + ) + mainExecutor.runAllReady() + + verify(activityLaunchAnimator) + .startPendingIntentWithAnimation( + nullable(), + eq(true), + nullable(), + eq(true), + any(), + ) + } + @Test fun startPendingIntentDismissingKeyguard_associatedView_getAnimatorController() { val pendingIntent = mock(PendingIntent::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index cfd220b45f1a0469f0495815b3020f5125553392..164325a431a5644dc0b624b64c0ccd4e207b279c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -140,7 +140,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); when(mKeyguardStateController.isShowing()).thenReturn(true); when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true); - when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true); + when(mKeyguardStateController.isFaceEnrolled()).thenReturn(true); when(mKeyguardStateController.isUnlocked()).thenReturn(false); when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean())) .thenReturn(true); 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 a59cd87d78da769e34ac3a2f21334bf81630d19b..41eaf858f27a8566340667e1319fe4d11bb01906 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 @@ -158,7 +158,6 @@ import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; -import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger; @@ -502,7 +501,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { (Lazy<NotificationPresenter>) () -> mNotificationPresenter, (Lazy<NotificationActivityStarter>) () -> mNotificationActivityStarter, mNotifLaunchAnimControllerProvider, - new NotificationExpansionRepository(), mDozeParameters, mScrimController, mBiometricUnlockControllerLazy, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index 593c587c0f514d9b5d0bde6b7927d28830b1e937..472709cd622e04ab021f93dbb855685e460d1166 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -42,6 +42,8 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.biometrics.AuthController; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; +import com.android.systemui.flags.FakeFeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.domain.interactor.DozeInteractor; import com.android.systemui.shade.NotificationShadeWindowViewController; @@ -96,18 +98,21 @@ public class DozeServiceHostTest extends SysuiTestCase { @Mock private BiometricUnlockController mBiometricUnlockController; @Mock private AuthController mAuthController; @Mock private DozeHost.Callback mCallback; - @Mock private DozeInteractor mDozeInteractor; + + private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic(); + @Before public void setup() { MockitoAnnotations.initMocks(this); + mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR); mDozeServiceHost = new DozeServiceHost(mDozeLog, mPowerManager, mWakefullnessLifecycle, - mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager, - mBatteryController, mScrimController, () -> mBiometricUnlockController, - () -> mAssistManager, mDozeScrimController, - mKeyguardUpdateMonitor, mPulseExpansionHandler, - mNotificationShadeWindowController, mNotificationWakeUpCoordinator, - mAuthController, mNotificationIconAreaController, mDozeInteractor); + mStatusBarStateController, mDeviceProvisionedController, mFeatureFlags, + mHeadsUpManager, mBatteryController, mScrimController, + () -> mBiometricUnlockController, () -> mAssistManager, mDozeScrimController, + mKeyguardUpdateMonitor, mPulseExpansionHandler, mNotificationShadeWindowController, + mNotificationWakeUpCoordinator, mAuthController, mNotificationIconAreaController, + mDozeInteractor); mDozeServiceHost.initialize( mCentralSurfaces, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index d84bb728a856954b8f8a136792d723d9a959affb..529e2c9a8e7ee36cbe650e329fabc2a200b0b2bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -34,6 +34,8 @@ import android.widget.TextView; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.flags.FakeFeatureFlagsClassic; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeHeadsUpTracker; @@ -42,6 +44,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; @@ -82,10 +85,12 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { private KeyguardStateController mKeyguardStateController; private CommandQueue mCommandQueue; private NotificationRoundnessManager mNotificationRoundnessManager; + private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic(); @Before public void setUp() throws Exception { allowTestableLooperAsMainThread(); + mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR); mTestHelper = new NotificationTestHelper( mContext, mDependency, @@ -119,6 +124,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mNotificationRoundnessManager, mHeadsUpStatusBarView, new Clock(mContext, null), + mFeatureFlags, + mock(HeadsUpNotificationIconInteractor.class), Optional.of(mOperatorNameView)); mHeadsUpAppearanceController.setAppearFraction(0.0f, 0.0f); } @@ -203,6 +210,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mNotificationRoundnessManager, mHeadsUpStatusBarView, new Clock(mContext, null), + mFeatureFlags, mock(HeadsUpNotificationIconInteractor.class), Optional.empty()); assertEquals(expandedHeight, newController.mExpandedHeight, 0.0f); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt index 6209f73f2e92f1f4e2269ee0bbf84067af619eb1..bd0dbeebd7529af597ff89637b584e1e72481d07 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt @@ -86,7 +86,7 @@ class KeyguardBypassControllerTest : SysuiTestCase() { featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true) - whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true) + whenever(keyguardStateController.isFaceEnrolled).thenReturn(true) } @After @@ -158,7 +158,7 @@ class KeyguardBypassControllerTest : SysuiTestCase() { keyguardBypassController.registerOnBypassStateChangedListener(bypassListener) verify(keyguardStateController).addCallback(callback.capture()) - callback.value.onFaceAuthEnabledChanged() + callback.value.onFaceEnrolledChanged() verify(bypassListener).onBypassStateChanged(anyBoolean()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index beac995c893b493797ef8850ec41a6ad5620ad37..1e31977306267d2b972a368fa058a9191e70d7f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -86,7 +86,8 @@ import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorCon import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; -import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository; +import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository; +import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; @@ -222,7 +223,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { HeadsUpManager headsUpManager = mock(HeadsUpManager.class); NotificationLaunchAnimatorControllerProvider notificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( - new NotificationExpansionRepository(), + new NotificationLaunchAnimationInteractor( + new NotificationLaunchAnimationRepository()), mock(NotificationListContainer.class), headsUpManager, mJankMonitor); 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 bcb34d6d93421e1c670a82c0745e838e53e17d21..9a77f0c0300dfe801aa5a61213e890a466d521c7 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 @@ -48,23 +48,29 @@ import android.widget.FrameLayout; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.res.R; import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.animation.AnimatorTestRule; +import com.android.systemui.common.ui.ConfigurationState; +import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogcatEchoTracker; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; +import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore; +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel; +import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.NotificationIconAreaController; +import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarLocationPublisher; @@ -72,6 +78,7 @@ import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentCom import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewBinder; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; @@ -682,7 +689,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mLocationPublisher, mMockNotificationAreaController, mShadeExpansionStateManager, - mock(FeatureFlags.class), + mock(FeatureFlagsClassic.class), mStatusBarIconController, mIconManagerFactory, mCollapsedStatusBarViewModel, @@ -702,7 +709,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mExecutor, mDumpManager, mStatusBarWindowStateController, - mKeyguardUpdateMonitor); + mKeyguardUpdateMonitor, + mock(NotificationIconContainerStatusBarViewModel.class), + mock(ConfigurationState.class), + mock(ConfigurationController.class), + mock(DozeParameters.class), + mock(ScreenOffAnimationController.class), + mock(StatusBarNotificationIconViewStore.class), + mock(DemoModeController.class)); } private void setUpDaggerComponent() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java index e6b09e36cae999b6292f9c323865a165baae858c..5c960b6633dbdebc378b1403786c419a2d17640d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java @@ -97,20 +97,20 @@ public class KeyguardStateControllerTest extends SysuiTestCase { } @Test - public void testFaceAuthEnabledChanged_calledWhenFaceEnrollmentStateChanges() { + public void testFaceAuthEnrolleddChanged_calledWhenFaceEnrollmentStateChanges() { KeyguardStateController.Callback callback = mock(KeyguardStateController.Callback.class); - when(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(anyInt())).thenReturn(false); + when(mKeyguardUpdateMonitor.isFaceEnrolled(anyInt())).thenReturn(false); verify(mKeyguardUpdateMonitor).registerCallback(mUpdateCallbackCaptor.capture()); mKeyguardStateController.addCallback(callback); - assertThat(mKeyguardStateController.isFaceAuthEnabled()).isFalse(); + assertThat(mKeyguardStateController.isFaceEnrolled()).isFalse(); - when(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(anyInt())).thenReturn(true); + when(mKeyguardUpdateMonitor.isFaceEnrolled(anyInt())).thenReturn(true); mUpdateCallbackCaptor.getValue().onBiometricEnrollmentStateChanged( BiometricSourceType.FACE); - assertThat(mKeyguardStateController.isFaceAuthEnabled()).isTrue(); - verify(callback).onFaceAuthEnabledChanged(); + assertThat(mKeyguardStateController.isFaceEnrolled()).isTrue(); + verify(callback).onFaceEnrolledChanged(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt index 6e3a732aa8ec1623d498c7b0add88a5f8c4015e1..94100fe7f4c457d0e93b7e6488110963bcce538d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt @@ -24,8 +24,6 @@ import com.android.systemui.coroutines.collectLastValue import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test @@ -38,64 +36,193 @@ class AnimatedValueTest : SysuiTestCase() { @Test fun animatableEvent_updatesValue() = runTest { val events = MutableSharedFlow<AnimatableEvent<Int>>() - val values = events.toAnimatedValueFlow(completionEvents = emptyFlow()) + val values = events.toAnimatedValueFlow() val value by collectLastValue(values) runCurrent() events.emit(AnimatableEvent(value = 1, startAnimating = false)) - assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = false)) + assertThat(value?.value).isEqualTo(1) + assertThat(value?.isAnimating).isFalse() } @Test fun animatableEvent_startAnimation() = runTest { val events = MutableSharedFlow<AnimatableEvent<Int>>() - val values = events.toAnimatedValueFlow(completionEvents = emptyFlow()) + val values = events.toAnimatedValueFlow() val value by collectLastValue(values) runCurrent() events.emit(AnimatableEvent(value = 1, startAnimating = true)) - assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = true)) + assertThat(value?.value).isEqualTo(1) + assertThat(value?.isAnimating).isTrue() } @Test fun animatableEvent_startAnimation_alreadyAnimating() = runTest { val events = MutableSharedFlow<AnimatableEvent<Int>>() - val values = events.toAnimatedValueFlow(completionEvents = emptyFlow()) + val values = events.toAnimatedValueFlow() val value by collectLastValue(values) runCurrent() events.emit(AnimatableEvent(value = 1, startAnimating = true)) events.emit(AnimatableEvent(value = 2, startAnimating = true)) - assertThat(value).isEqualTo(AnimatedValue(value = 2, isAnimating = true)) + assertThat(value?.value).isEqualTo(2) + assertThat(value?.isAnimating).isTrue() } @Test fun animatedValue_stopAnimating() = runTest { val events = MutableSharedFlow<AnimatableEvent<Int>>() - val stopEvent = MutableSharedFlow<Unit>() - val values = events.toAnimatedValueFlow(completionEvents = stopEvent) + val values = events.toAnimatedValueFlow() val value by collectLastValue(values) runCurrent() events.emit(AnimatableEvent(value = 1, startAnimating = true)) - stopEvent.emit(Unit) + assertThat(value?.isAnimating).isTrue() + value?.stopAnimating() - assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = false)) + assertThat(value?.value).isEqualTo(1) + assertThat(value?.isAnimating).isFalse() } @Test - fun animatedValue_stopAnimating_notAnimating() = runTest { + fun animatedValue_stopAnimatingPrevValue_doesNothing() = runTest { val events = MutableSharedFlow<AnimatableEvent<Int>>() - val stopEvent = MutableSharedFlow<Unit>() - val values = events.toAnimatedValueFlow(completionEvents = stopEvent) - values.launchIn(backgroundScope) + val values = events.toAnimatedValueFlow() + val value by collectLastValue(values) runCurrent() - events.emit(AnimatableEvent(value = 1, startAnimating = false)) + events.emit(AnimatableEvent(value = 1, startAnimating = true)) + val prevValue = value + assertThat(prevValue?.isAnimating).isTrue() + + events.emit(AnimatableEvent(value = 2, startAnimating = true)) + assertThat(value?.isAnimating).isTrue() + prevValue?.stopAnimating() + + assertThat(value?.value).isEqualTo(2) + assertThat(value?.isAnimating).isTrue() + } + + @Test + fun zipValues_applyTransform() { + val animating = AnimatedValue.Animating(1) {} + val notAnimating = AnimatedValue.NotAnimating(2) + val sum = zip(animating, notAnimating) { a, b -> a + b } + assertThat(sum.value).isEqualTo(3) + } + + @Test + fun zipValues_firstIsAnimating_resultIsAnimating() { + var stopped = false + val animating = AnimatedValue.Animating(1) { stopped = true } + val notAnimating = AnimatedValue.NotAnimating(2) + val sum = zip(animating, notAnimating) { a, b -> a + b } + assertThat(sum.isAnimating).isTrue() + + sum.stopAnimating() + assertThat(stopped).isTrue() + } + + @Test + fun zipValues_secondIsAnimating_resultIsAnimating() { + var stopped = false + val animating = AnimatedValue.Animating(1) { stopped = true } + val notAnimating = AnimatedValue.NotAnimating(2) + val sum = zip(notAnimating, animating) { a, b -> a + b } + assertThat(sum.isAnimating).isTrue() + + sum.stopAnimating() + assertThat(stopped).isTrue() + } + + @Test + fun zipValues_bothAnimating_resultIsAnimating() { + var firstStopped = false + var secondStopped = false + val first = AnimatedValue.Animating(1) { firstStopped = true } + val second = AnimatedValue.Animating(2) { secondStopped = true } + val sum = zip(first, second) { a, b -> a + b } + assertThat(sum.isAnimating).isTrue() + + sum.stopAnimating() + assertThat(firstStopped).isTrue() + assertThat(secondStopped).isTrue() + } - assertThat(stopEvent.subscriptionCount.value).isEqualTo(0) + @Test + fun zipValues_neitherAnimating_resultIsNotAnimating() { + val first = AnimatedValue.NotAnimating(1) + val second = AnimatedValue.NotAnimating(2) + val sum = zip(first, second) { a, b -> a + b } + assertThat(sum.isAnimating).isFalse() + } + + @Test + fun mapAnimatedValue_isAnimating() { + var stopped = false + val animating = AnimatedValue.Animating(3) { stopped = true } + val squared = animating.map { it * it } + assertThat(squared.value).isEqualTo(9) + assertThat(squared.isAnimating).isTrue() + squared.stopAnimating() + assertThat(stopped).isTrue() + } + + @Test + fun mapAnimatedValue_notAnimating() { + val notAnimating = AnimatedValue.NotAnimating(3) + val squared = notAnimating.map { it * it } + assertThat(squared.value).isEqualTo(9) + assertThat(squared.isAnimating).isFalse() + } + + @Test + fun flattenAnimatingValue_neitherAnimating() { + val nested = AnimatedValue.NotAnimating(AnimatedValue.NotAnimating(10)) + val flattened = nested.flatten() + assertThat(flattened.value).isEqualTo(10) + assertThat(flattened.isAnimating).isFalse() + } + + @Test + fun flattenAnimatingValue_outerAnimating() { + var stopped = false + val inner = AnimatedValue.NotAnimating(10) + val nested = AnimatedValue.Animating(inner) { stopped = true } + val flattened = nested.flatten() + assertThat(flattened.value).isEqualTo(10) + assertThat(flattened.isAnimating).isTrue() + flattened.stopAnimating() + assertThat(stopped).isTrue() + } + + @Test + fun flattenAnimatingValue_innerAnimating() { + var stopped = false + val inner = AnimatedValue.Animating(10) { stopped = true } + val nested = AnimatedValue.NotAnimating(inner) + val flattened = nested.flatten() + assertThat(flattened.value).isEqualTo(10) + assertThat(flattened.isAnimating).isTrue() + flattened.stopAnimating() + assertThat(stopped).isTrue() + } + + @Test + fun flattenAnimatingValue_bothAnimating() { + var innerStopped = false + var outerStopped = false + val inner = AnimatedValue.Animating(10) { innerStopped = true } + val nested = AnimatedValue.Animating(inner) { outerStopped = true } + val flattened = nested.flatten() + assertThat(flattened.value).isEqualTo(10) + assertThat(flattened.isAnimating).isTrue() + flattened.stopAnimating() + assertThat(innerStopped).isTrue() + assertThat(outerStopped).isTrue() } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 65975e44ee2a36b1e51b582d38936dca23d35b75..76ebdf4d4c1ccc430ad1f2eedd3cb142e3eee4e4 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -103,9 +103,8 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.IResultReceiver; import com.android.internal.util.DumpUtils; import com.android.server.LocalServices; -import com.android.server.contentprotection.ContentProtectionBlocklistManager; +import com.android.server.contentprotection.ContentProtectionAllowlistManager; import com.android.server.contentprotection.ContentProtectionConsentManager; -import com.android.server.contentprotection.ContentProtectionPackageManager; import com.android.server.contentprotection.RemoteContentProtectionService; import com.android.server.infra.AbstractMasterSystemService; import com.android.server.infra.FrameworkResourcesServiceNameResolver; @@ -206,9 +205,6 @@ public class ContentCaptureManagerService extends @GuardedBy("mLock") boolean mDevCfgEnableContentProtectionReceiver; - @GuardedBy("mLock") - int mDevCfgContentProtectionAppsBlocklistSize; - @GuardedBy("mLock") int mDevCfgContentProtectionBufferSize; @@ -237,7 +233,7 @@ public class ContentCaptureManagerService extends @Nullable private final ComponentName mContentProtectionServiceComponentName; - @Nullable private final ContentProtectionBlocklistManager mContentProtectionBlocklistManager; + @Nullable private final ContentProtectionAllowlistManager mContentProtectionAllowlistManager; @Nullable private final ContentProtectionConsentManager mContentProtectionConsentManager; @@ -287,17 +283,15 @@ public class ContentCaptureManagerService extends if (getEnableContentProtectionReceiverLocked()) { mContentProtectionServiceComponentName = getContentProtectionServiceComponentName(); if (mContentProtectionServiceComponentName != null) { - mContentProtectionBlocklistManager = createContentProtectionBlocklistManager(); - mContentProtectionBlocklistManager.updateBlocklist( - mDevCfgContentProtectionAppsBlocklistSize); + mContentProtectionAllowlistManager = createContentProtectionAllowlistManager(); mContentProtectionConsentManager = createContentProtectionConsentManager(); } else { - mContentProtectionBlocklistManager = null; + mContentProtectionAllowlistManager = null; mContentProtectionConsentManager = null; } } else { mContentProtectionServiceComponentName = null; - mContentProtectionBlocklistManager = null; + mContentProtectionAllowlistManager = null; mContentProtectionConsentManager = null; } } @@ -445,8 +439,6 @@ public class ContentCaptureManagerService extends case ContentCaptureManager .DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER: case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE: - case ContentCaptureManager - .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE: case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG: case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG: case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD: @@ -502,14 +494,6 @@ public class ContentCaptureManagerService extends ContentCaptureManager .DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER, ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER); - mDevCfgContentProtectionAppsBlocklistSize = - DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager - .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE, - ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE); - // mContentProtectionBlocklistManager.updateBlocklist not called on purpose here to keep - // it immutable at this point mDevCfgContentProtectionBufferSize = DeviceConfig.getInt( DeviceConfig.NAMESPACE_CONTENT_CAPTURE, @@ -542,7 +526,7 @@ public class ContentCaptureManagerService extends + mDevCfgMaxBufferSize + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs - + ", textFluxh=" + + ", textFlush=" + mDevCfgTextChangeFlushingFrequencyMs + ", logHistory=" + mDevCfgLogHistorySize @@ -552,8 +536,6 @@ public class ContentCaptureManagerService extends + mDevCfgDisableFlushForViewTreeAppearing + ", enableContentProtectionReceiver=" + mDevCfgEnableContentProtectionReceiver - + ", contentProtectionAppsBlocklistSize=" - + mDevCfgContentProtectionAppsBlocklistSize + ", contentProtectionBufferSize=" + mDevCfgContentProtectionBufferSize + ", contentProtectionRequiredGroupsConfig=" @@ -844,9 +826,6 @@ public class ContentCaptureManagerService extends pw.print("enableContentProtectionReceiver: "); pw.println(mDevCfgEnableContentProtectionReceiver); pw.print(prefix2); - pw.print("contentProtectionAppsBlocklistSize: "); - pw.println(mDevCfgContentProtectionAppsBlocklistSize); - pw.print(prefix2); pw.print("contentProtectionBufferSize: "); pw.println(mDevCfgContentProtectionBufferSize); pw.print(prefix2); @@ -877,9 +856,8 @@ public class ContentCaptureManagerService extends /** @hide */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @NonNull - protected ContentProtectionBlocklistManager createContentProtectionBlocklistManager() { - return new ContentProtectionBlocklistManager( - new ContentProtectionPackageManager(getContext())); + protected ContentProtectionAllowlistManager createContentProtectionAllowlistManager() { + return new ContentProtectionAllowlistManager(); } /** @hide */ @@ -1429,7 +1407,7 @@ public class ContentCaptureManagerService extends private boolean isContentProtectionReceiverEnabled( @UserIdInt int userId, @NonNull String packageName) { if (mContentProtectionServiceComponentName == null - || mContentProtectionBlocklistManager == null + || mContentProtectionAllowlistManager == null || mContentProtectionConsentManager == null) { return false; } @@ -1443,7 +1421,7 @@ public class ContentCaptureManagerService extends } } return mContentProtectionConsentManager.isConsentGranted(userId) - && mContentProtectionBlocklistManager.isAllowed(packageName); + && mContentProtectionAllowlistManager.isAllowed(packageName); } } diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java new file mode 100644 index 0000000000000000000000000000000000000000..59af5263fa9e8eddf445485d1274782c90d5e094 --- /dev/null +++ b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.contentprotection; + +import android.annotation.NonNull; +import android.util.Slog; + +/** + * Manages whether the content protection is enabled for an app using a allowlist. + * + * @hide + */ +public class ContentProtectionAllowlistManager { + + private static final String TAG = "ContentProtectionAllowlistManager"; + + public ContentProtectionAllowlistManager() {} + + /** Returns true if the package is allowed. */ + public boolean isAllowed(@NonNull String packageName) { + Slog.v(TAG, packageName); + return false; + } +} diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionBlocklistManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionBlocklistManager.java deleted file mode 100644 index a0fd28b3f27992b7f58ef5cc39c14533c395b3a2..0000000000000000000000000000000000000000 --- a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionBlocklistManager.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.contentprotection; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.pm.PackageInfo; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Manages whether the content protection is enabled for an app using a blocklist. - * - * @hide - */ -public class ContentProtectionBlocklistManager { - - private static final String TAG = "ContentProtectionBlocklistManager"; - - private static final String PACKAGE_NAME_BLOCKLIST_FILENAME = - "/product/etc/res/raw/content_protection/package_name_blocklist.txt"; - - @NonNull private final ContentProtectionPackageManager mContentProtectionPackageManager; - - @Nullable private Set<String> mPackageNameBlocklist; - - public ContentProtectionBlocklistManager( - @NonNull ContentProtectionPackageManager contentProtectionPackageManager) { - mContentProtectionPackageManager = contentProtectionPackageManager; - } - - public boolean isAllowed(@NonNull String packageName) { - if (mPackageNameBlocklist == null) { - // List not loaded or failed to load, don't run on anything - return false; - } - if (mPackageNameBlocklist.contains(packageName)) { - return false; - } - PackageInfo packageInfo = mContentProtectionPackageManager.getPackageInfo(packageName); - if (packageInfo == null) { - return false; - } - if (!mContentProtectionPackageManager.hasRequestedInternetPermissions(packageInfo)) { - return false; - } - if (mContentProtectionPackageManager.isSystemApp(packageInfo)) { - return false; - } - if (mContentProtectionPackageManager.isUpdatedSystemApp(packageInfo)) { - return false; - } - return true; - } - - public void updateBlocklist(int blocklistSize) { - Slog.i(TAG, "Blocklist size updating to: " + blocklistSize); - mPackageNameBlocklist = readPackageNameBlocklist(blocklistSize); - } - - @Nullable - private Set<String> readPackageNameBlocklist(int blocklistSize) { - if (blocklistSize <= 0) { - // Explicitly requested an empty blocklist - return Collections.emptySet(); - } - List<String> lines = readLinesFromRawFile(PACKAGE_NAME_BLOCKLIST_FILENAME); - if (lines == null) { - return null; - } - return lines.stream().limit(blocklistSize).collect(Collectors.toSet()); - } - - @VisibleForTesting - @Nullable - protected List<String> readLinesFromRawFile(@NonNull String filename) { - try (FileReader fileReader = new FileReader(filename); - BufferedReader bufferedReader = new BufferedReader(fileReader)) { - return bufferedReader - .lines() - .map(line -> line.trim()) - .filter(line -> !line.isBlank()) - .collect(Collectors.toList()); - } catch (Exception ex) { - Slog.e(TAG, "Failed to read: " + filename, ex); - return null; - } - } -} diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionPackageManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionPackageManager.java deleted file mode 100644 index 4ebac07ec3ea71fc27bf6418a017ab99d9608231..0000000000000000000000000000000000000000 --- a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionPackageManager.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.contentprotection; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.PackageManager.PackageInfoFlags; -import android.util.Slog; - -import java.util.Arrays; - -/** - * Basic package manager for content protection using content capture. - * - * @hide - */ -public class ContentProtectionPackageManager { - - private static final String TAG = "ContentProtectionPackageManager"; - - private static final PackageInfoFlags PACKAGE_INFO_FLAGS = - PackageInfoFlags.of(PackageManager.GET_PERMISSIONS); - - @NonNull private final PackageManager mPackageManager; - - public ContentProtectionPackageManager(@NonNull Context context) { - mPackageManager = context.getPackageManager(); - } - - @Nullable - public PackageInfo getPackageInfo(@NonNull String packageName) { - try { - return mPackageManager.getPackageInfo(packageName, PACKAGE_INFO_FLAGS); - } catch (NameNotFoundException ex) { - Slog.w(TAG, "Package info not found for: " + packageName, ex); - return null; - } - } - - public boolean isSystemApp(@NonNull PackageInfo packageInfo) { - return packageInfo.applicationInfo != null && isSystemApp(packageInfo.applicationInfo); - } - - private boolean isSystemApp(@NonNull ApplicationInfo applicationInfo) { - return (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - } - - public boolean isUpdatedSystemApp(@NonNull PackageInfo packageInfo) { - return packageInfo.applicationInfo != null - && isUpdatedSystemApp(packageInfo.applicationInfo); - } - - private boolean isUpdatedSystemApp(@NonNull ApplicationInfo applicationInfo) { - return (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; - } - - public boolean hasRequestedInternetPermissions(@NonNull PackageInfo packageInfo) { - return packageInfo.requestedPermissions != null - && Arrays.asList(packageInfo.requestedPermissions) - .contains(Manifest.permission.INTERNET); - } -} diff --git a/services/core/Android.bp b/services/core/Android.bp index 4dca5a5c807b8f5d481f1b151bdf179e09bf3bcf..975b1e830ba6c9614c058afd30cecb254a298328 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -205,6 +205,7 @@ java_library_static { "com.android.sysprop.watchdog", "ImmutabilityAnnotation", "securebox", + "android.content.pm.flags-aconfig-java", "apache-commons-math", "backstage_power_flags_lib", "notification_flags_lib", diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index bdb5d9356a0d9d41bc8c30348b8f0126e74f1a8f..ef67cbe5024b32b2e62a87d80b0465f66e30a3e4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -77,8 +77,10 @@ import static android.os.IServiceManager.DUMP_FLAG_PROTO; import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; +import static android.os.PowerExemptionManager.REASON_BOOT_COMPLETED; import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER; import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; +import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED; import static android.os.PowerExemptionManager.REASON_PROC_STATE_BTOP; import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT; import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI; @@ -4840,6 +4842,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (!mConstants.mEnableWaitForFinishAttachApplication) { finishAttachApplicationInner(startSeq, callingUid, pid); } + maybeSendBootCompletedLocked(app); } catch (Exception e) { // We need kill the process group here. (b/148588589) Slog.wtf(TAG, "Exception thrown during bind of " + app, e); @@ -5066,6 +5069,45 @@ public class ActivityManagerService extends IActivityManager.Stub } } + /** + * Send LOCKED_BOOT_COMPLETED and BOOT_COMPLETED to the package explicitly when unstopped + */ + private void maybeSendBootCompletedLocked(ProcessRecord app) { + // Nothing to do if it wasn't previously stopped + if (!android.content.pm.Flags.stayStopped() || !app.wasForceStopped()) return; + + // Send LOCKED_BOOT_COMPLETED, if necessary + if (app.getApplicationInfo().isEncryptionAware()) { + sendBootBroadcastToAppLocked(app, new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED), + REASON_LOCKED_BOOT_COMPLETED); + } + // Send BOOT_COMPLETED if the user is unlocked + if (StorageManager.isUserKeyUnlocked(app.userId)) { + sendBootBroadcastToAppLocked(app, new Intent(Intent.ACTION_BOOT_COMPLETED), + REASON_BOOT_COMPLETED); + } + app.setWasForceStopped(false); + } + + /** Send a boot_completed broadcast to app */ + private void sendBootBroadcastToAppLocked(ProcessRecord app, Intent intent, + @PowerExemptionManager.ReasonCode int reason) { + intent.setPackage(app.info.packageName); + intent.putExtra(Intent.EXTRA_USER_HANDLE, app.userId); + intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND + | Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + final BroadcastOptions bOptions = mUserController.getTemporaryAppAllowlistBroadcastOptions( + reason); + + broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, + new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, + null, null, AppOpsManager.OP_NONE, + bOptions.toBundle(), true, + false, MY_PID, SYSTEM_UID, + SYSTEM_UID, MY_PID, app.userId); + } + @Override public void showBootMessage(final CharSequence msg, final boolean always) { if (Binder.getCallingUid() != myUid()) { diff --git a/services/core/java/com/android/server/am/LowMemDetector.java b/services/core/java/com/android/server/am/LowMemDetector.java index 8f791331f0cf3dbe4d4aa8e924d7d18ec23510b4..016d3cddae31863010ff9cf7a3ca6fb409b305ce 100644 --- a/services/core/java/com/android/server/am/LowMemDetector.java +++ b/services/core/java/com/android/server/am/LowMemDetector.java @@ -23,6 +23,7 @@ import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NOR import static com.android.internal.app.procstats.ProcessStats.ADJ_NOTHING; import android.annotation.IntDef; +import android.os.Trace; import com.android.internal.annotations.GuardedBy; @@ -90,17 +91,31 @@ public final class LowMemDetector { private native int waitForPressure(); private final class LowMemThread extends Thread { + private boolean mIsTracingMemCriticalLow; + + LowMemThread() { + super("LowMemThread"); + } + public void run() { while (true) { // sleep waiting for a PSI event int newPressureState = waitForPressure(); + // PSI event detected + boolean isCriticalLowMemory = newPressureState == ADJ_MEM_FACTOR_CRITICAL; + if (isCriticalLowMemory && !mIsTracingMemCriticalLow) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "criticalLowMemory"); + } else if (!isCriticalLowMemory && mIsTracingMemCriticalLow) { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + mIsTracingMemCriticalLow = isCriticalLowMemory; if (newPressureState == -1) { // epoll broke, tear this down mAvailable = false; break; } - // got a PSI event? let's update lowmem info + // got an actual PSI event? let's update lowmem info synchronized (mPressureStateLock) { mPressureState = newPressureState; } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 59d8e7e96ba62283dbb42366bdc96eeb01f9394b..f04198ed39ec6e51a63dd89c9357974b95a5165f 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -3257,6 +3257,17 @@ public final class ProcessList { hostingRecord.getDefiningUid(), hostingRecord.getDefiningProcessName()); final ProcessStateRecord state = r.mState; + // Check if we should mark the processrecord for first launch after force-stopping + if ((r.getApplicationInfo().flags & ApplicationInfo.FLAG_STOPPED) != 0) { + final boolean wasPackageEverLaunched = mService.getPackageManagerInternal() + .wasPackageEverLaunched(r.getApplicationInfo().packageName, r.userId); + // If the package was launched in the past but is currently stopped, only then it + // should be considered as stopped after use. Do not mark it if it's the first launch. + if (wasPackageEverLaunched) { + r.setWasForceStopped(true); + } + } + if (!isolated && !isSdkSandbox && userId == UserHandle.USER_SYSTEM && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index f02b8c737f90c7376feb633b9c70167e2a6710e8..2c6e598ef0a4a5d40e3e315f7637870ad6ccaa02 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -438,6 +438,9 @@ class ProcessRecord implements WindowProcessListener { final ProcessRecordNode[] mLinkedNodes = new ProcessRecordNode[NUM_NODE_TYPE]; + /** Whether the app was launched from a stopped state and is being unstopped. */ + volatile boolean mWasForceStopped; + void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo, long startUptime, long startElapsedTime) { this.mStartUid = startUid; @@ -1602,4 +1605,12 @@ class ProcessRecord implements WindowProcessListener { List<ProcessRecord> getLruProcessList() { return mService.mProcessList.getLruProcessesLOSP(); } + + public void setWasForceStopped(boolean stopped) { + mWasForceStopped = stopped; + } + + public boolean wasForceStopped() { + return mWasForceStopped; + } } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index de4ad20bdcccd9363551db9888fcfc5adf546169..0dd579fd0b154f263ab8524e25a665c36ca0903d 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -3387,7 +3387,7 @@ class UserController implements Handler.Callback { } - private BroadcastOptions getTemporaryAppAllowlistBroadcastOptions( + BroadcastOptions getTemporaryAppAllowlistBroadcastOptions( @PowerWhitelistManager.ReasonCode int reasonCode) { long duration = 10_000; final ActivityManagerInternal amInternal = diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 74b7f0866b5484153a6d18bcd81ba4ba330a8d58..323cdc54edae6f584fca783e1b80dab2a5810bd8 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -474,13 +474,14 @@ public class LocationManagerService extends ILocationManager.Stub implements // If we have a GNSS provider override, add the hardware provider as a standalone // option for use by apps with the correct permission. Note the GNSS HAL can only // support a single client, so mGnssManagerService.getGnssLocationProvider() can - // only be installed with a single provider. + // only be installed with a single provider. Locations from this provider won't + // be reported through the passive provider. LocationProviderManager gnssHardwareManager = new LocationProviderManager( mContext, mInjector, GPS_HARDWARE_PROVIDER, - mPassiveManager, + /*passiveManager=*/ null, Collections.singletonList(Manifest.permission.LOCATION_HARDWARE)); addLocationProviderManager( gnssHardwareManager, mGnssManagerService.getGnssLocationProvider()); diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 30017be96085106e9d3c2fe12081ddd6316c988b..510c06e9509e95fde589ad66f629dcb4b5ff7459 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -24,7 +24,6 @@ import static android.content.ContentProvider.isAuthorityRedirectedForCloneProfi import static android.content.Intent.ACTION_MAIN; import static android.content.Intent.CATEGORY_DEFAULT; import static android.content.Intent.CATEGORY_HOME; -import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE; import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509; import static android.content.pm.PackageManager.CERT_INPUT_SHA256; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; @@ -1525,9 +1524,6 @@ public class ComputerEngine implements Computer { ai.secondaryCpuAbi = ps.getSecondaryCpuAbiLegacy(); ai.volumeUuid = ps.getVolumeUuid(); ai.storageUuid = StorageManager.convert(ai.volumeUuid); - if (ps.isDefaultToDeviceProtectedStorage()) { - ai.privateFlags |= PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE; - } ai.setVersionCode(ps.getVersionCode()); ai.flags = ps.getFlags(); ai.privateFlags = ps.getPrivateFlags(); @@ -4596,6 +4592,7 @@ public class ComputerEngine implements Computer { flags = updateFlagsForApplication(flags, userId); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; final boolean listApex = (flags & MATCH_APEX) != 0; + final boolean listArchivedOnly = !listUninstalled && (flags & MATCH_ARCHIVED_PACKAGES) != 0; enforceCrossUserPermission( callingUid, @@ -4607,7 +4604,7 @@ public class ComputerEngine implements Computer { ArrayList<ApplicationInfo> list; final ArrayMap<String, ? extends PackageStateInternal> packageStates = getPackageStates(); - if (listUninstalled) { + if (listUninstalled || listArchivedOnly) { list = new ArrayList<>(packageStates.size()); for (PackageStateInternal ps : packageStates.values()) { ApplicationInfo ai; @@ -4619,6 +4616,11 @@ public class ComputerEngine implements Computer { if (!listApex && ps.getPkg().isApex()) { continue; } + PackageUserStateInternal userState = ps.getUserStateOrDefault(userId); + if (listArchivedOnly && !userState.isInstalled() + && userState.getArchiveState() == null) { + continue; + } if (filterSharedLibPackage(ps, callingUid, userId, flags)) { continue; } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index a5c5ae2702614df996adccbd0e84557de6929d4a..7cac870088d8335d2b9d8495900cfe045af55a93 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -1630,7 +1630,8 @@ final class InstallPackageHelper { synchronized (mPm.mLock) { if (DEBUG_INSTALL) { Slog.d(TAG, - "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage); + "replacePackageLI: new=" + parsedPackage + + ", old=" + oldPackageState.getName()); } ps = mPm.mSettings.getPackageLPr(pkgName11); @@ -1789,7 +1790,7 @@ final class InstallPackageHelper { if (DEBUG_INSTALL) { Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage - + ", old=" + oldPackage); + + ", old=" + oldPackageState.getName()); } request.setReturnCode(PackageManager.INSTALL_SUCCEEDED); request.setApexModuleName(oldPackageState.getApexModuleName()); @@ -1799,7 +1800,7 @@ final class InstallPackageHelper { if (DEBUG_INSTALL) { Slog.d(TAG, "replaceNonSystemPackageLI: new=" + parsedPackage + ", old=" - + oldPackage); + + oldPackageState.getName()); } } } else { // new package install diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 1135466ae1d4d36e5aba2aa92fffe78e28047414..c260be9a51245c51b1d5b282b7f5815d92d4f721 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -56,6 +56,7 @@ import android.content.IntentSender; import android.content.LocusId; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.Flags; import android.content.pm.ILauncherApps; import android.content.pm.IOnAppsChangedListener; import android.content.pm.IPackageInstallerCallback; @@ -94,6 +95,7 @@ import android.os.ShellCommand; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; @@ -112,6 +114,8 @@ import com.android.internal.util.SizedInputStream; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.ArchiveState; +import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.wm.ActivityTaskManagerInternal; import java.io.DataInputStream; @@ -513,18 +517,27 @@ public class LauncherAppsService extends SystemService { @Override public ParceledListSlice<LauncherActivityInfoInternal> getLauncherActivities( - String callingPackage, String packageName, UserHandle user) throws RemoteException { + String callingPackage, @Nullable String packageName, UserHandle user) + throws RemoteException { ParceledListSlice<LauncherActivityInfoInternal> launcherActivities = - queryActivitiesForUser(callingPackage, + queryActivitiesForUser( + callingPackage, new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_LAUNCHER) .setPackage(packageName), user); - if (Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) { + if (Flags.archiving()) { + launcherActivities = + getActivitiesForArchivedApp(packageName, user, launcherActivities); + } + if (Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, + 1) + == 0) { return launcherActivities; } - if (launcherActivities == null) { + if (launcherActivities == null || launcherActivities.getList().isEmpty()) { // Cannot access profile, so we don't even return any hidden apps. return null; } @@ -565,15 +578,16 @@ public class LauncherAppsService extends SystemService { visiblePackages.add(info.getActivityInfo().packageName); } final List<ApplicationInfo> installedPackages = - mPackageManagerInternal.getInstalledApplications(/* flags= */ 0, - user.getIdentifier(), callingUid); + mPackageManagerInternal.getInstalledApplications( + /* flags= */ 0, user.getIdentifier(), callingUid); for (ApplicationInfo applicationInfo : installedPackages) { if (!visiblePackages.contains(applicationInfo.packageName)) { if (!shouldShowSyntheticActivity(user, applicationInfo)) { continue; } - LauncherActivityInfoInternal info = getHiddenAppActivityInfo( - applicationInfo.packageName, callingUid, user); + LauncherActivityInfoInternal info = + getHiddenAppActivityInfo( + applicationInfo.packageName, callingUid, user); if (info != null) { result.add(info); } @@ -585,6 +599,23 @@ public class LauncherAppsService extends SystemService { } } + private ParceledListSlice<LauncherActivityInfoInternal> getActivitiesForArchivedApp( + @Nullable String packageName, + UserHandle user, + ParceledListSlice<LauncherActivityInfoInternal> launcherActivities) { + final List<LauncherActivityInfoInternal> archivedActivities = + generateLauncherActivitiesForArchivedApp(packageName, user); + if (archivedActivities.isEmpty()) { + return launcherActivities; + } + if (launcherActivities == null) { + return new ParceledListSlice(archivedActivities); + } + List<LauncherActivityInfoInternal> result = launcherActivities.getList(); + result.addAll(archivedActivities); + return new ParceledListSlice(result); + } + private boolean shouldShowSyntheticActivity(UserHandle user, ApplicationInfo appInfo) { if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) { return false; @@ -650,23 +681,30 @@ public class LauncherAppsService extends SystemService { return null; } + if (component == null || component.getPackageName() == null) { + // should not happen + return null; + } + final int callingUid = injectBinderCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - final ActivityInfo activityInfo = mPackageManagerInternal.getActivityInfo(component, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - callingUid, user.getIdentifier()); + ActivityInfo activityInfo = + mPackageManagerInternal.getActivityInfo( + component, + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, + callingUid, + user.getIdentifier()); if (activityInfo == null) { - return null; - } - if (component == null || component.getPackageName() == null) { - // should not happen + if (Flags.archiving()) { + return getMatchingArchivedAppActivityInfo(component, user); + } return null; } final IncrementalStatesInfo incrementalStatesInfo = - mPackageManagerInternal.getIncrementalStatesInfo(component.getPackageName(), - callingUid, user.getIdentifier()); + mPackageManagerInternal.getIncrementalStatesInfo( + component.getPackageName(), callingUid, user.getIdentifier()); if (incrementalStatesInfo == null) { // package does not exist; should not happen return null; @@ -677,6 +715,26 @@ public class LauncherAppsService extends SystemService { } } + private @Nullable LauncherActivityInfoInternal getMatchingArchivedAppActivityInfo( + @NonNull ComponentName component, UserHandle user) { + List<LauncherActivityInfoInternal> archivedActivities = + generateLauncherActivitiesForArchivedApp(component.getPackageName(), user); + if (archivedActivities.isEmpty()) { + return null; + } + for (int i = 0; i < archivedActivities.size(); i++) { + if (archivedActivities.get(i).getComponentName().equals(component)) { + return archivedActivities.get(i); + } + } + Slog.w( + TAG, + TextUtils.formatSimple( + "Expected archived app component name: %s" + " is not available!", + component)); + return null; + } + @Override public ParceledListSlice getShortcutConfigActivities( String callingPackage, String packageName, UserHandle user) @@ -700,6 +758,96 @@ public class LauncherAppsService extends SystemService { } } + @NonNull + private List<LauncherActivityInfoInternal> generateLauncherActivitiesForArchivedApp( + @Nullable String packageName, UserHandle user) { + List<ApplicationInfo> applicationInfoList = + (packageName == null) + ? getApplicationInfoListForAllArchivedApps(user) + : getApplicationInfoForArchivedApp(packageName, user); + List<LauncherActivityInfoInternal> launcherActivityList = new ArrayList<>(); + for (int i = 0; i < applicationInfoList.size(); i++) { + ApplicationInfo applicationInfo = applicationInfoList.get(i); + PackageStateInternal packageState = + mPackageManagerInternal.getPackageStateInternal( + applicationInfo.packageName); + if (packageState == null) { + continue; + } + ArchiveState archiveState = + packageState.getUserStateOrDefault(user.getIdentifier()).getArchiveState(); + if (archiveState == null) { + Slog.w( + TAG, + TextUtils.formatSimple( + "Expected package: %s to be archived but missing ArchiveState" + + " in PackageState.", + applicationInfo.packageName)); + continue; + } + List<ArchiveState.ArchiveActivityInfo> archiveActivityInfoList = + archiveState.getActivityInfos(); + for (int j = 0; j < archiveActivityInfoList.size(); j++) { + launcherActivityList.add( + constructLauncherActivityInfoForArchivedApp( + user, applicationInfo, archiveActivityInfoList.get(j))); + } + } + return launcherActivityList; + } + + private static LauncherActivityInfoInternal constructLauncherActivityInfoForArchivedApp( + UserHandle user, + ApplicationInfo applicationInfo, + ArchiveState.ArchiveActivityInfo archiveActivityInfo) { + ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.isArchived = applicationInfo.isArchived; + activityInfo.applicationInfo = applicationInfo; + activityInfo.packageName = + archiveActivityInfo.getOriginalComponentName().getPackageName(); + activityInfo.name = archiveActivityInfo.getOriginalComponentName().getClassName(); + activityInfo.nonLocalizedLabel = archiveActivityInfo.getTitle(); + + return new LauncherActivityInfoInternal( + activityInfo, + new IncrementalStatesInfo( + false /* isLoading */, 1 /* progress */, 0 /* loadingCompletedTime */), + user); + } + + @NonNull + private List<ApplicationInfo> getApplicationInfoListForAllArchivedApps(UserHandle user) { + final int callingUid = injectBinderCallingUid(); + List<ApplicationInfo> installedApplicationInfoList = + mPackageManagerInternal.getInstalledApplications( + PackageManager.MATCH_ARCHIVED_PACKAGES, + user.getIdentifier(), + callingUid); + List<ApplicationInfo> archivedApplicationInfos = new ArrayList<>(); + for (int i = 0; i < installedApplicationInfoList.size(); i++) { + ApplicationInfo installedApplicationInfo = installedApplicationInfoList.get(i); + if (installedApplicationInfo != null && installedApplicationInfo.isArchived) { + archivedApplicationInfos.add(installedApplicationInfo); + } + } + return archivedApplicationInfos; + } + + @NonNull + private List<ApplicationInfo> getApplicationInfoForArchivedApp( + @NonNull String packageName, UserHandle user) { + final int callingUid = injectBinderCallingUid(); + ApplicationInfo applicationInfo = mPackageManagerInternal.getApplicationInfo( + packageName, + PackageManager.MATCH_ARCHIVED_PACKAGES, + callingUid, + user.getIdentifier()); + if (applicationInfo == null || !applicationInfo.isArchived) { + return Collections.EMPTY_LIST; + } + return List.of(applicationInfo); + } + private List<LauncherActivityInfoInternal> queryIntentLauncherActivities( Intent intent, int callingUid, UserHandle user) { final List<ResolveInfo> apps = mPackageManagerInternal.queryIntentActivities(intent, diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index 781e5f891a43d32a8bc1c026fa134831f4b0c962..49228513064d159832deab2814154450d5b8aba0 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -17,6 +17,8 @@ package com.android.server.pm; import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; +import static android.content.pm.ArchivedActivity.bytesFromBitmap; +import static android.content.pm.ArchivedActivity.drawableToBitmap; import static android.content.pm.PackageManager.DELETE_ARCHIVE; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE; @@ -43,9 +45,6 @@ import android.content.pm.ResolveInfo; import android.content.pm.VersionedPackage; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -64,7 +63,6 @@ import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.PackageUserState; import com.android.server.pm.pkg.PackageUserStateInternal; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -547,29 +545,6 @@ public class PackageArchiver { return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR); } - private static Bitmap drawableToBitmap(Drawable drawable) { - if (drawable instanceof BitmapDrawable) { - return ((BitmapDrawable) drawable).getBitmap(); - - } - - Bitmap bitmap; - if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { - // Needed for drawables that are just a single color. - bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - } else { - bitmap = - Bitmap.createBitmap( - drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight(), - Bitmap.Config.ARGB_8888); - } - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - return bitmap; - } - private static byte[] bytesFromBitmapFile(Path path) throws IOException { if (path == null) { return null; @@ -579,18 +554,6 @@ public class PackageArchiver { return bytesFromBitmap(BitmapFactory.decodeFile(path.toString())); } - private static byte[] bytesFromBitmap(Bitmap bitmap) throws IOException { - if (bitmap == null) { - return null; - } - - try (ByteArrayOutputStream baos = new ByteArrayOutputStream( - bitmap.getByteCount())) { - bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); - return baos.toByteArray(); - } - } - /** * Creates serializable archived activities from existing ArchiveState. */ @@ -642,9 +605,8 @@ public class PackageArchiver { var archivedActivity = new ArchivedActivityParcel(); archivedActivity.title = info.getLabel().toString(); archivedActivity.originalComponentName = info.getComponentName(); - archivedActivity.iconBitmap = - info.getActivityInfo().getIconResource() == 0 ? null : bytesFromBitmap( - drawableToBitmap(info.getIcon(/* density= */ 0))); + archivedActivity.iconBitmap = info.getActivityInfo().getIconResource() == 0 ? null : + bytesFromBitmap(drawableToBitmap(info.getIcon(/* density= */ 0))); // TODO(b/298452477) Handle monochrome icons. archivedActivity.monochromeIconBitmap = null; activities.add(archivedActivity); diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 1bb20b4769f5ffc2d118d30c1fdc872486fc4442..b9b590833f4ae74a133966e6be6b43a7f2671405 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -17,6 +17,7 @@ package com.android.server.pm; import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO; +import static android.content.pm.PackageInstaller.LOCATION_DATA_APP; import static android.os.Process.INVALID_UID; import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME; @@ -42,6 +43,7 @@ import android.content.Intent; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.pm.ApplicationInfo; +import android.content.pm.ArchivedPackageParcel; import android.content.pm.IPackageInstaller; import android.content.pm.IPackageInstallerCallback; import android.content.pm.IPackageInstallerSession; @@ -621,6 +623,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public int createSession(SessionParams params, String installerPackageName, String callingAttributionTag, int userId) { try { + if (params.dataLoaderParams != null + && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need the " + + "com.android.permission.USE_INSTALLER_V2 permission " + + "to use a data loader"); + } + return createSessionInternal(params, installerPackageName, callingAttributionTag, userId); } catch (IOException e) { @@ -639,14 +649,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements throw new SecurityException("User restriction prevents installing"); } - if (params.dataLoaderParams != null - && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("You need the " - + "com.android.permission.USE_INSTALLER_V2 permission " - + "to use a data loader"); - } - // INSTALL_REASON_ROLLBACK allows an app to be rolled back without requiring the ROLLBACK // capability; ensure if this is set as the install reason the app has one of the necessary // signature permissions to perform the rollback. @@ -1043,7 +1045,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements return false; } - private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException { + private PackageInstallerSession openSessionInternal(int sessionId) throws IOException { synchronized (mSessions) { final PackageInstallerSession session = mSessions.get(sessionId); if (!checkOpenSessionAccess(session)) { @@ -1523,6 +1525,61 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mPackageArchiver.requestUnarchive(packageName, callerPackageName, userHandle); } + @Override + public void installPackageArchived( + @NonNull ArchivedPackageParcel archivedPackageParcel, + @NonNull SessionParams params, + @NonNull IntentSender statusReceiver, + @NonNull String installerPackageName, + @NonNull UserHandle userHandle) { + Objects.requireNonNull(params); + Objects.requireNonNull(archivedPackageParcel); + Objects.requireNonNull(statusReceiver); + Objects.requireNonNull(installerPackageName); + Objects.requireNonNull(userHandle); + + final int callingUid = Binder.getCallingUid(); + final int userId = userHandle.getIdentifier(); + final Computer snapshot = mPm.snapshotComputer(); + snapshot.enforceCrossUserPermission(callingUid, userId, true, true, + "installPackageArchived"); + + if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need the " + + "com.android.permission.INSTALL_PACKAGES permission " + + "to request archived package install"); + } + + params.installFlags |= PackageManager.INSTALL_ARCHIVED; + if (params.dataLoaderParams != null) { + throw new IllegalArgumentException( + "Incompatible session param: dataLoaderParams has to be null"); + } + + params.setDataLoaderParams( + PackageManagerShellCommandDataLoader.getStreamingDataLoaderParams(null)); + var metadata = PackageManagerShellCommandDataLoader.Metadata.forArchived( + archivedPackageParcel); + + // Create and commit install archived session. + PackageInstallerSession session = null; + try { + var sessionId = createSessionInternal(params, installerPackageName, + null /*installerAttributionTag*/, userId); + session = openSessionInternal(sessionId); + session.addFile(LOCATION_DATA_APP, "base", 0 /*lengthBytes*/, metadata.toByteArray(), + null /*signature*/); + session.commit(statusReceiver, false /*forTransfer*/); + } catch (IOException e) { + throw ExceptionUtils.wrap(e); + } finally { + if (session != null) { + session.close(); + } + } + } + private static int getSessionCount(SparseArray<PackageInstallerSession> sessions, int installerUid) { int count = 0; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java index 9e7f04351327456dae73781ed88281af13bc75c2..a09e71311125e6bb3f2927c7a9df54a66708c624 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java @@ -304,10 +304,6 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { public boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles, @NonNull Collection<String> removedFiles) { ShellCommand shellCommand = lookupShellCommand(mParams.getArguments()); - if (shellCommand == null) { - Slog.e(TAG, "Missing shell command."); - return false; - } try { for (InstallationFile file : addedFiles) { Metadata metadata = Metadata.fromByteArray(file.getMetadata()); @@ -317,11 +313,19 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { } switch (metadata.getMode()) { case Metadata.STDIN: { + if (shellCommand == null) { + Slog.e(TAG, "Missing shell command for Metadata.STDIN."); + return false; + } final ParcelFileDescriptor inFd = getStdInPFD(shellCommand); mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd); break; } case Metadata.LOCAL_FILE: { + if (shellCommand == null) { + Slog.e(TAG, "Missing shell command for Metadata.LOCAL_FILE."); + return false; + } ParcelFileDescriptor incomingFd = null; try { final String filePath = new String(metadata.getData(), diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 42f4cfb8b4dfa084f4e65ce072a27e68250b27b2..ad26d1fa858728a4caaa9b29a54711e20583c07a 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; @@ -89,17 +90,14 @@ public class PackageSetting extends SettingBase implements PackageStateInternal private static class Booleans { @IntDef({ INSTALL_PERMISSION_FIXED, - DEFAULT_TO_DEVICE_PROTECTED_STORAGE, UPDATE_AVAILABLE, FORCE_QUERYABLE_OVERRIDE }) public @interface Flags { } private static final int INSTALL_PERMISSION_FIXED = 1; - private static final int DEFAULT_TO_DEVICE_PROTECTED_STORAGE = 1 << 1; - private static final int UPDATE_AVAILABLE = 1 << 2; - private static final int FORCE_QUERYABLE_OVERRIDE = 1 << 3; - private static final int PERSISTENT = 1 << 4; + private static final int UPDATE_AVAILABLE = 1 << 1; + private static final int FORCE_QUERYABLE_OVERRIDE = 1 << 2; } private int mBooleans; @@ -500,13 +498,6 @@ public class PackageSetting extends SettingBase implements PackageStateInternal return this; } - public PackageSetting setDefaultToDeviceProtectedStorage( - boolean defaultToDeviceProtectedStorage) { - setBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE, defaultToDeviceProtectedStorage); - onChanged(); - return this; - } - @Override public boolean isExternalStorage() { return (getFlags() & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; @@ -524,12 +515,6 @@ public class PackageSetting extends SettingBase implements PackageStateInternal return this; } - public PackageSetting setIsPersistent(boolean isPersistent) { - setBoolean(Booleans.PERSISTENT, isPersistent); - onChanged(); - return this; - } - public PackageSetting setTargetSdkVersion(int targetSdkVersion) { mTargetSdkVersion = targetSdkVersion; onChanged(); @@ -1585,12 +1570,12 @@ public class PackageSetting extends SettingBase implements PackageStateInternal */ @Override public boolean isDefaultToDeviceProtectedStorage() { - return getBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE); + return (getPrivateFlags() & PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) != 0; } @Override public boolean isPersistent() { - return getBoolean(Booleans.PERSISTENT); + return (getFlags() & ApplicationInfo.FLAG_PERSISTENT) != 0; } @@ -1746,10 +1731,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal } @DataClass.Generated( - time = 1696979728639L, + time = 1698097434269L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java", - inputSignatures = "private int mBooleans\nprivate int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic com.android.server.pm.PackageSetting setDefaultToDeviceProtectedStorage(boolean)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic void setSharedUserAppId(int)\npublic com.android.server.pm.PackageSetting setIsPersistent(boolean)\npublic com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic boolean isRequestLegacyExternalStorage()\npublic boolean isUserDataFragile()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n int[] queryInstalledUsers(int[],boolean)\n int[] queryUsersInstalledOrHasData(int[])\n long getCeDataInode(int)\n long getDeDataInode(int)\n void setCeDataInode(long,int)\n void setDeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isIncremental()\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final int INSTALL_PERMISSION_FIXED\nprivate static final int DEFAULT_TO_DEVICE_PROTECTED_STORAGE\nprivate static final int UPDATE_AVAILABLE\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int PERSISTENT\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)") + inputSignatures = "private int mBooleans\nprivate int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic boolean isRequestLegacyExternalStorage()\npublic boolean isUserDataFragile()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n int[] queryInstalledUsers(int[],boolean)\n int[] queryUsersInstalledOrHasData(int[])\n long getCeDataInode(int)\n long getDeDataInode(int)\n void setCeDataInode(long,int)\n void setDeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isIncremental()\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final int INSTALL_PERMISSION_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java index 8d8acfd421dee02a37b57515988c52c4abb9611e..7ea9e3f529d0f0079b78bec1d37d6474c4403e85 100644 --- a/services/core/java/com/android/server/pm/ScanPackageUtils.java +++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java @@ -220,7 +220,7 @@ final class ScanPackageUtils { UserManagerService.getInstance(), usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(), - newDomainSetId, parsedPackage.isPersistent(), + newDomainSetId, parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash()); } else { // make a deep copy to avoid modifying any existing system state. @@ -241,7 +241,7 @@ final class ScanPackageUtils { UserManagerService.getInstance(), usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(), - parsedPackage.getMimeGroups(), newDomainSetId, parsedPackage.isPersistent(), + parsedPackage.getMimeGroups(), newDomainSetId, parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash()); } @@ -464,8 +464,6 @@ final class ScanPackageUtils { + " to " + volumeUuid); pkgSetting.setVolumeUuid(volumeUuid); } - pkgSetting.setDefaultToDeviceProtectedStorage( - parsedPackage.isDefaultToDeviceProtectedStorage()); SharedLibraryInfo sdkLibraryInfo = null; if (!TextUtils.isEmpty(parsedPackage.getSdkLibraryName())) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index a39178ec4baa84a41664d5450c461e3adc6c8001..440823c436073e9e6bf4d22d4288a617dad9f08b 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -946,7 +946,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile ret.setMimeGroups(p.getMimeGroups()); ret.setAppMetadataFilePath(p.getAppMetadataFilePath()); ret.getPkgState().setUpdatedSystemApp(false); - ret.setIsPersistent(p.isPersistent()); ret.setTargetSdkVersion(p.getTargetSdkVersion()); ret.setRestrictUpdateHash(p.getRestrictUpdateHash()); } @@ -1061,7 +1060,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile boolean virtualPreload, boolean isStoppedSystemApp, UserManagerService userManager, String[] usesSdkLibraries, long[] usesSdkLibrariesVersions, String[] usesStaticLibraries, long[] usesStaticLibrariesVersions, - Set<String> mimeGroupNames, @NonNull UUID domainSetId, boolean isPersistent, + Set<String> mimeGroupNames, @NonNull UUID domainSetId, int targetSdkVersion, byte[] restrictUpdatedHash) { final PackageSetting pkgSetting; if (originalPkg != null) { @@ -1083,7 +1082,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile // Update new package state. .setLastModifiedTime(codePath.lastModified()) .setDomainSetId(domainSetId) - .setIsPersistent(isPersistent) .setTargetSdkVersion(targetSdkVersion) .setRestrictUpdateHash(restrictUpdatedHash); pkgSetting.setFlags(pkgFlags) @@ -1103,7 +1101,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile .setSecondaryCpuAbi(secondaryCpuAbi) .setLongVersionCode(versionCode) .setMimeGroups(createMimeGroups(mimeGroupNames)) - .setIsPersistent(isPersistent) .setTargetSdkVersion(targetSdkVersion) .setRestrictUpdateHash(restrictUpdatedHash) .setLastModifiedTime(codePath.lastModified()); @@ -1219,7 +1216,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile int pkgPrivateFlags, @NonNull UserManagerService userManager, @Nullable String[] usesSdkLibraries, @Nullable long[] usesSdkLibrariesVersions, @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions, - @Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId, boolean isPersistent, + @Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId, int targetSdkVersion, byte[] restrictUpdatedHash) throws PackageManagerException { final String pkgName = pkgSetting.getPackageName(); @@ -1273,7 +1270,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile .setSecondaryCpuAbi(secondaryCpuAbi) .updateMimeGroups(mimeGroupNames) .setDomainSetId(domainSetId) - .setIsPersistent(isPersistent) .setTargetSdkVersion(targetSdkVersion) .setRestrictUpdateHash(restrictUpdatedHash); // Update SDK library dependencies if needed. @@ -3071,7 +3067,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime()); serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime()); serializer.attributeLong(null, "version", pkg.getVersionCode()); - serializer.attributeBoolean(null, "isPersistent", pkg.isPersistent()); serializer.attributeInt(null, "targetSdkVersion", pkg.getTargetSdkVersion()); if (pkg.getRestrictUpdateHash() != null) { serializer.attributeBytesBase64(null, "restrictUpdateHash", @@ -3140,7 +3135,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime()); serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime()); serializer.attributeLong(null, "version", pkg.getVersionCode()); - serializer.attributeBoolean(null, "isPersistent", pkg.isPersistent()); serializer.attributeInt(null, "targetSdkVersion", pkg.getTargetSdkVersion()); if (pkg.getRestrictUpdateHash() != null) { serializer.attributeBytesBase64(null, "restrictUpdateHash", @@ -3182,8 +3176,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile if (pkg.getVolumeUuid() != null) { serializer.attribute(null, "volumeUuid", pkg.getVolumeUuid()); } - serializer.attributeBoolean(null, "defaultToDeviceProtectedStorage", - pkg.isDefaultToDeviceProtectedStorage()); if (pkg.getCategoryOverride() != ApplicationInfo.CATEGORY_UNDEFINED) { serializer.attributeInt(null, "categoryHint", pkg.getCategoryOverride()); } @@ -3878,7 +3870,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } long versionCode = parser.getAttributeLong(null, "version", 0); - boolean isPersistent = parser.getAttributeBoolean(null, "isPersistent", false); int targetSdkVersion = parser.getAttributeInt(null, "targetSdkVersion", 0); byte[] restrictUpdateHash = parser.getAttributeBytesBase64(null, "restrictUpdateHash", null); @@ -3901,7 +3892,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile .setSecondaryCpuAbi(secondaryCpuAbiStr) .setCpuAbiOverride(cpuAbiOverrideStr) .setLongVersionCode(versionCode) - .setIsPersistent(isPersistent) .setTargetSdkVersion(targetSdkVersion) .setRestrictUpdateHash(restrictUpdateHash); long timeStamp = parser.getAttributeLongHex(null, "ft", 0); @@ -3982,7 +3972,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile String installInitiatingPackageName = null; boolean installInitiatorUninstalled = false; String volumeUuid = null; - boolean defaultToDeviceProtectedStorage = false; boolean updateAvailable = false; int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; int pkgFlags = 0; @@ -3997,7 +3986,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile long loadingCompletedTime = 0; UUID domainSetId; String appMetadataFilePath = null; - boolean isPersistent = false; int targetSdkVersion = 0; byte[] restrictUpdateHash = null; try { @@ -4023,7 +4011,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } versionCode = parser.getAttributeLong(null, "version", 0); - isPersistent = parser.getAttributeBoolean(null, "isPersistent", false); targetSdkVersion = parser.getAttributeInt(null, "targetSdkVersion", 0); restrictUpdateHash = parser.getAttributeBytesBase64(null, "restrictUpdateHash", null); installerPackageName = parser.getAttributeValue(null, "installer"); @@ -4038,8 +4025,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile installInitiatorUninstalled = parser.getAttributeBoolean(null, "installInitiatorUninstalled", false); volumeUuid = parser.getAttributeValue(null, "volumeUuid"); - defaultToDeviceProtectedStorage = parser.getAttributeBoolean( - null, "defaultToDeviceProtectedStorage", false); categoryHint = parser.getAttributeInt(null, "categoryHint", ApplicationInfo.CATEGORY_UNDEFINED); appMetadataFilePath = parser.getAttributeValue(null, "appMetadataFilePath"); @@ -4179,7 +4164,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile installInitiatorUninstalled); packageSetting.setInstallSource(installSource) .setVolumeUuid(volumeUuid) - .setDefaultToDeviceProtectedStorage(defaultToDeviceProtectedStorage) .setCategoryOverride(categoryHint) .setLegacyNativeLibraryPath(legacyNativeLibraryPathStr) .setPrimaryCpuAbi(primaryCpuAbiString) @@ -4189,7 +4173,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile .setLoadingProgress(loadingProgress) .setLoadingCompletedTime(loadingCompletedTime) .setAppMetadataFilePath(appMetadataFilePath) - .setIsPersistent(isPersistent) .setTargetSdkVersion(targetSdkVersion) .setRestrictUpdateHash(restrictUpdateHash); // Handle legacy string here for single-user mode diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index 61e96ca3dd59cccf8aa15e8052bc45411a00b6a5..2ad8bcf4739e9cb88c14075db2ef3f21a98b92c6 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -1016,8 +1016,8 @@ public class PackageInfoUtils { return; } - if (android.content.pm.Flags.nullableDataDir() - && !state.isInstalled() && !state.dataExists()) { + if (!state.isInstalled() && !state.dataExists() + && android.content.pm.Flags.nullableDataDir()) { // The data dir has been deleted output.dataDir = null; return; @@ -1065,8 +1065,8 @@ public class PackageInfoUtils { return; } - if (android.content.pm.Flags.nullableDataDir() - && !state.isInstalled() && !state.dataExists()) { + if (!state.isInstalled() && !state.dataExists() + && android.content.pm.Flags.nullableDataDir()) { // The data dir has been deleted output.dataDir = null; return; @@ -1113,9 +1113,9 @@ public class PackageInfoUtils { return Environment.getDataSystemDirectory(); } - if (android.content.pm.Flags.nullableDataDir() - && !ps.getUserStateOrDefault(userId).isInstalled() - && !ps.getUserStateOrDefault(userId).dataExists()) { + if (!ps.getUserStateOrDefault(userId).isInstalled() + && !ps.getUserStateOrDefault(userId).dataExists() + && android.content.pm.Flags.nullableDataDir()) { // The app has been uninstalled for the user and the data dir has been deleted return null; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 7c0fc998abcfff7fd42e19b922af8a145165ba5a..b4396818c43ce94553b4a4c4a03aff460d75aa68 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2478,7 +2478,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.integer.config_keyguardDrawnTimeout); mKeyguardDelegate = injector.getKeyguardServiceDelegate(); initKeyCombinationRules(); - initSingleKeyGestureRules(); + initSingleKeyGestureRules(injector.getLooper()); mSideFpsEventHandler = new SideFpsEventHandler(mContext, mHandler, mPowerManager); } @@ -2749,8 +2749,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void initSingleKeyGestureRules() { - mSingleKeyGestureDetector = SingleKeyGestureDetector.get(mContext); + private void initSingleKeyGestureRules(Looper looper) { + mSingleKeyGestureDetector = SingleKeyGestureDetector.get(mContext, looper); mSingleKeyGestureDetector.addRule(new PowerKeyRule()); if (hasLongPressOnBackBehavior()) { mSingleKeyGestureDetector.addRule(new BackKeyRule()); diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java index c5a9cdf4bacb8167c2a8ececb03a5d069317bbbd..047555ae491c3cc2cf5afdfd64b0f8f294a3a091 100644 --- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java +++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java @@ -179,8 +179,8 @@ public final class SingleKeyGestureDetector { } } - static SingleKeyGestureDetector get(Context context) { - SingleKeyGestureDetector detector = new SingleKeyGestureDetector(); + static SingleKeyGestureDetector get(Context context, Looper looper) { + SingleKeyGestureDetector detector = new SingleKeyGestureDetector(looper); sDefaultLongPressTimeout = context.getResources().getInteger( com.android.internal.R.integer.config_globalActionsKeyTimeout); sDefaultVeryLongPressTimeout = context.getResources().getInteger( @@ -188,8 +188,8 @@ public final class SingleKeyGestureDetector { return detector; } - private SingleKeyGestureDetector() { - mHandler = new KeyHandler(); + private SingleKeyGestureDetector(Looper looper) { + mHandler = new KeyHandler(looper); } void addRule(SingleKeyRule rule) { @@ -417,8 +417,8 @@ public final class SingleKeyGestureDetector { } private class KeyHandler extends Handler { - KeyHandler() { - super(Looper.myLooper()); + KeyHandler(Looper looper) { + super(looper); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 9fa5ed2cfde97a45bc2716c810739b94d03df752..fd8f6bfd1cc881dac8f499d56da1589ca74f34d3 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3055,7 +3055,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override boolean providesOrientation() { - return mStyleFillsParent; + return mStyleFillsParent || mOccludesParent; } @Override diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index 823fbc9b0f3ec1781df7d249d4327de08cb17ca2..2fabb0ea686abe6ed38c8b1915217586ac322f7e 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -29,7 +29,7 @@ import com.android.window.flags.Flags; */ public abstract class Dimmer { - static final boolean DIMMER_REFACTOR = Flags.dimmerRefactor(); + static final boolean DIMMER_REFACTOR = Flags.introduceSmootherDimmer(); /** * The {@link WindowContainer} that our Dims are bounded to. We may be dimming on behalf of the diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index f9fa9e6d4a7bdc79cc0b49f85cb67ed48c82ca61..f6aad4c86220b29075baf51b57ea7873da42f987 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -36,7 +36,10 @@ import android.view.WindowManager; import com.android.server.UiThread; +import java.util.function.BooleanSupplier; +import java.util.function.DoubleSupplier; import java.util.function.IntConsumer; +import java.util.function.IntSupplier; import java.util.function.Supplier; /** @@ -50,12 +53,12 @@ public class Letterbox { private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory; private final Supplier<SurfaceControl.Transaction> mTransactionFactory; - private final Supplier<Boolean> mAreCornersRounded; + private final BooleanSupplier mAreCornersRounded; private final Supplier<Color> mColorSupplier; // Parameters for "blurred wallpaper" letterbox background. - private final Supplier<Boolean> mHasWallpaperBackgroundSupplier; - private final Supplier<Integer> mBlurRadiusSupplier; - private final Supplier<Float> mDarkScrimAlphaSupplier; + private final BooleanSupplier mHasWallpaperBackgroundSupplier; + private final IntSupplier mBlurRadiusSupplier; + private final DoubleSupplier mDarkScrimAlphaSupplier; private final Supplier<SurfaceControl> mParentSurfaceSupplier; private final Rect mOuter = new Rect(); @@ -82,11 +85,11 @@ public class Letterbox { */ public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory, Supplier<SurfaceControl.Transaction> transactionFactory, - Supplier<Boolean> areCornersRounded, + BooleanSupplier areCornersRounded, Supplier<Color> colorSupplier, - Supplier<Boolean> hasWallpaperBackgroundSupplier, - Supplier<Integer> blurRadiusSupplier, - Supplier<Float> darkScrimAlphaSupplier, + BooleanSupplier hasWallpaperBackgroundSupplier, + IntSupplier blurRadiusSupplier, + DoubleSupplier darkScrimAlphaSupplier, IntConsumer doubleTapCallbackX, IntConsumer doubleTapCallbackY, Supplier<SurfaceControl> parentSurface) { @@ -247,7 +250,7 @@ public class Letterbox { * Returns {@code true} when using {@link #mFullWindowSurface} instead of {@link mSurfaces}. */ private boolean useFullWindowSurface() { - return mAreCornersRounded.get() || mHasWallpaperBackgroundSupplier.get(); + return mAreCornersRounded.getAsBoolean() || mHasWallpaperBackgroundSupplier.getAsBoolean(); } private final class TapEventReceiver extends InputEventReceiver { @@ -424,7 +427,7 @@ public class Letterbox { mSurfaceFrameRelative.height()); t.reparent(mSurface, mParentSurface); - mHasWallpaperBackground = mHasWallpaperBackgroundSupplier.get(); + mHasWallpaperBackground = mHasWallpaperBackgroundSupplier.getAsBoolean(); updateAlphaAndBlur(t); t.show(mSurface); @@ -445,17 +448,17 @@ public class Letterbox { t.setBackgroundBlurRadius(mSurface, 0); return; } - final float alpha = mDarkScrimAlphaSupplier.get(); + final float alpha = (float) mDarkScrimAlphaSupplier.getAsDouble(); t.setAlpha(mSurface, alpha); // Translucent dark scrim can be shown without blur. - if (mBlurRadiusSupplier.get() <= 0) { + if (mBlurRadiusSupplier.getAsInt() <= 0) { // Removing pre-exesting blur t.setBackgroundBlurRadius(mSurface, 0); return; } - t.setBackgroundBlurRadius(mSurface, mBlurRadiusSupplier.get()); + t.setBackgroundBlurRadius(mSurface, mBlurRadiusSupplier.getAsInt()); } private float[] getRgbColorArray() { @@ -472,7 +475,7 @@ public class Letterbox { // and mParentSurface may never be updated in applySurfaceChanges but this // doesn't mean that update is needed. || !mSurfaceFrameRelative.isEmpty() - && (mHasWallpaperBackgroundSupplier.get() != mHasWallpaperBackground + && (mHasWallpaperBackgroundSupplier.getAsBoolean() != mHasWallpaperBackground || !mColorSupplier.get().equals(mColor) || mParentSurfaceSupplier.get() != mParentSurface); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 71dbd29a0410f61e8cecef73110cc721bbadc5b7..6c31e3e6935067f497336ae52c27e9bdc2f3cc90 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -199,7 +199,6 @@ import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService; import com.android.server.am.AppTimeTracker; import com.android.server.uri.NeededUriGrants; -import com.android.window.flags.Flags; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -241,7 +240,6 @@ class Task extends TaskFragment { private static final String ATTR_ROOT_AFFINITY = "root_affinity"; private static final String ATTR_ROOTHASRESET = "root_has_reset"; private static final String ATTR_AUTOREMOVERECENTS = "auto_remove_recents"; - private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode"; private static final String ATTR_USERID = "user_id"; private static final String ATTR_USER_SETUP_COMPLETE = "user_setup_complete"; private static final String ATTR_EFFECTIVE_UID = "effective_uid"; @@ -344,7 +342,6 @@ class Task extends TaskFragment { // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag. boolean autoRemoveRecents; // If true, we should automatically remove the task from // recents when activity finishes - boolean askedCompatMode;// Have asked the user about compat mode for this task. private boolean mHasBeenVisible; // Set if any activities in the task have been visible String stringName; // caching of toString() result. @@ -617,7 +614,7 @@ class Task extends TaskFragment { private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent, String _affinity, String _rootAffinity, ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, - boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId, int _effectiveUid, + boolean _autoRemoveRecents, int _userId, int _effectiveUid, String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription, PersistedTaskSnapshotData _lastSnapshotData, int taskAffiliation, int prevTaskId, int nextTaskId, int callingUid, @@ -652,7 +649,6 @@ class Task extends TaskFragment { rootWasReset = _rootWasReset; isAvailable = true; autoRemoveRecents = _autoRemoveRecents; - askedCompatMode = _askedCompatMode; mUserSetupComplete = userSetupComplete; effectiveUid = _effectiveUid; touchActiveTime(); @@ -3304,7 +3300,7 @@ class Task extends TaskFragment { // Once at the root task level, we want to check {@link #isTranslucent(ActivityRecord)}. // If true, we want to get the Dimmer from the level above since we don't want to animate // the dim with the Task. - if (!isRootTask() || (Flags.dimmerRefactor() && isTranslucentAndVisible()) + if (!isRootTask() || (Dimmer.DIMMER_REFACTOR && isTranslucentAndVisible()) || isTranslucent(null)) { return super.getDimmer(); } @@ -3768,8 +3764,8 @@ class Task extends TaskFragment { pw.println(")"); } pw.print(prefix); pw.print("Activities="); pw.println(mChildren); - if (!askedCompatMode || !inRecents || !isAvailable) { - pw.print(prefix); pw.print("askedCompatMode="); pw.print(askedCompatMode); + if (!inRecents || !isAvailable) { + pw.print(prefix); pw.print(" inRecents="); pw.print(inRecents); pw.print(" isAvailable="); pw.println(isAvailable); } @@ -3877,7 +3873,6 @@ class Task extends TaskFragment { } out.attributeBoolean(null, ATTR_ROOTHASRESET, rootWasReset); out.attributeBoolean(null, ATTR_AUTOREMOVERECENTS, autoRemoveRecents); - out.attributeBoolean(null, ATTR_ASKEDCOMPATMODE, askedCompatMode); out.attributeInt(null, ATTR_USERID, mUserId); out.attributeBoolean(null, ATTR_USER_SETUP_COMPLETE, mUserSetupComplete); out.attributeInt(null, ATTR_EFFECTIVE_UID, effectiveUid); @@ -3975,7 +3970,6 @@ class Task extends TaskFragment { String windowLayoutAffinity = null; boolean rootHasReset = false; boolean autoRemoveRecents = false; - boolean askedCompatMode = false; int taskType = 0; int userId = 0; boolean userSetupComplete = true; @@ -4036,9 +4030,6 @@ class Task extends TaskFragment { case ATTR_AUTOREMOVERECENTS: autoRemoveRecents = Boolean.parseBoolean(attrValue); break; - case ATTR_ASKEDCOMPATMODE: - askedCompatMode = Boolean.parseBoolean(attrValue); - break; case ATTR_USERID: userId = Integer.parseInt(attrValue); break; @@ -4192,7 +4183,6 @@ class Task extends TaskFragment { .setOrigActivity(origActivity) .setRootWasReset(rootHasReset) .setAutoRemoveRecents(autoRemoveRecents) - .setAskedCompatMode(askedCompatMode) .setUserId(userId) .setEffectiveUid(effectiveUid) .setLastDescription(lastDescription) @@ -6269,7 +6259,6 @@ class Task extends TaskFragment { private ComponentName mOrigActivity; private boolean mRootWasReset; private boolean mAutoRemoveRecents; - private boolean mAskedCompatMode; private int mUserId; private int mEffectiveUid; private String mLastDescription; @@ -6524,11 +6513,6 @@ class Task extends TaskFragment { return this; } - private Builder setAskedCompatMode(boolean askedCompatMode) { - mAskedCompatMode = askedCompatMode; - return this; - } - private Builder setAffinityIntent(Intent affinityIntent) { mAffinityIntent = affinityIntent; return this; @@ -6661,7 +6645,7 @@ class Task extends TaskFragment { Task buildInner() { return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity, mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents, - mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved, + mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved, mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData, mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid, mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture, diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 906b3b55e015361f9b69e58896dacc24fab68de4..82d34246f8571f33bca35d6f0a117acbcb9f1e34 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -103,7 +103,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.server.am.HostingRecord; import com.android.server.pm.pkg.AndroidPackage; -import com.android.window.flags.Flags; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -210,7 +209,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { */ int mMinHeight; - Dimmer mDimmer = Flags.dimmerRefactor() + Dimmer mDimmer = Dimmer.DIMMER_REFACTOR ? new SmoothDimmer(this) : new LegacyDimmer(this); /** Apply the dim layer on the embedded TaskFragment. */ @@ -391,41 +390,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper = new EnsureActivitiesVisibleHelper(this); - private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper = - new EnsureVisibleActivitiesConfigHelper(); - private class EnsureVisibleActivitiesConfigHelper implements Predicate<ActivityRecord> { - private boolean mUpdateConfig; - private boolean mPreserveWindow; - private boolean mBehindFullscreen; - - void reset(boolean preserveWindow) { - mPreserveWindow = preserveWindow; - mUpdateConfig = false; - mBehindFullscreen = false; - } - - void process(ActivityRecord start, boolean preserveWindow) { - if (start == null || !start.isVisibleRequested()) { - return; - } - reset(preserveWindow); - forAllActivities(this, start, true /* includeBoundary */, - true /* traverseTopToBottom */); - - if (mUpdateConfig) { - // Ensure the resumed state of the focus activity if we updated the configuration of - // any activity. - mRootWindowContainer.resumeFocusedTasksTopActivities(); - } - } - - @Override - public boolean test(ActivityRecord r) { - mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow); - mBehindFullscreen |= r.occludesParent(); - return mBehindFullscreen; - } - } /** Creates an embedded task fragment. */ TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken, @@ -2163,13 +2127,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { return getTask() != null ? getTask().mTaskId : INVALID_TASK_ID; } - /** - * Ensures all visible activities at or below the input activity have the right configuration. - */ - void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) { - mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow); - } - void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig) { computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 924e2f8ec65481b5bf7d8322e657ae689a90e2e2..0d024d6a4c587e8043ce20a2696969eebd5f999a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -2083,17 +2083,14 @@ public final class SystemServer implements Dumpable { t.traceEnd(); } - // Devices without WebView/JavaScript cannot support PAC proxies. - if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { - t.traceBegin("StartPacProxyService"); - try { - pacProxyService = new PacProxyService(context); - ServiceManager.addService(Context.PAC_PROXY_SERVICE, pacProxyService); - } catch (Throwable e) { - reportWtf("starting PacProxyService", e); - } - t.traceEnd(); + t.traceBegin("StartPacProxyService"); + try { + pacProxyService = new PacProxyService(context); + ServiceManager.addService(Context.PAC_PROXY_SERVICE, pacProxyService); + } catch (Throwable e) { + reportWtf("starting PacProxyService", e); } + t.traceEnd(); t.traceBegin("StartConnectivityService"); // This has to be called after NetworkManagementService, NetworkStatsService diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java index aaad669d5b8daafa1ff754579e68ea640a386096..2810145c08a9fb5e9cc1322f2d29fb5341802b9e 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -1051,7 +1051,6 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, UUID.randomUUID(), - false /*isPersistent*/, 34 /*targetSdkVersion*/, null /*restrictUpdateHash*/); assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a")); @@ -1092,7 +1091,6 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, UUID.randomUUID(), - false /*isPersistent*/, 34 /*targetSdkVersion*/, null /*restrictUpdateHash*/); assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a")); @@ -1135,7 +1133,6 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, UUID.randomUUID(), - false /*isPersistent*/, 34 /*targetSdkVersion*/, null /*restrictUpdateHash*/); fail("Expected a PackageManagerException"); @@ -1174,7 +1171,6 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, UUID.randomUUID(), - false /*isPersistent*/, 34 /*targetSdkVersion*/, null /*restrictUpdateHash*/); assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH)); @@ -1222,7 +1218,6 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, UUID.randomUUID(), - false /*isPersistent*/, 34 /*targetSdkVersion*/, null /*restrictUpdateHash*/); assertThat(testPkgSetting01.getAppId(), is(0)); @@ -1270,7 +1265,6 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, UUID.randomUUID(), - false /*isPersistent*/, 34 /*targetSdkVersion*/, null /*restrictUpdateHash*/); assertThat(testPkgSetting01.getAppId(), is(10064)); @@ -1319,7 +1313,6 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, UUID.randomUUID(), - false /*isPersistent*/, 34 /*targetSdkVersion*/, null /*restrictUpdateHash*/); assertThat(testPkgSetting01.getAppId(), is(10064)); @@ -1365,7 +1358,6 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, UUID.randomUUID(), - false /*isPersistent*/, 34 /*targetSdkVersion*/, null /*restrictUpdateHash*/); assertThat(testPkgSetting01.getAppId(), is(0)); diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java index 5cc84b197e0327346a0ca863bd785db4ab1c3629..78bf9b0bc828f43c3b1adada86e44483a23e7c19 100644 --- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java @@ -24,7 +24,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @@ -44,7 +43,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.server.LocalServices; -import com.android.server.contentprotection.ContentProtectionBlocklistManager; +import com.android.server.contentprotection.ContentProtectionAllowlistManager; import com.android.server.contentprotection.ContentProtectionConsentManager; import com.android.server.contentprotection.RemoteContentProtectionService; import com.android.server.pm.UserManagerInternal; @@ -94,7 +93,7 @@ public class ContentCaptureManagerServiceTest { @Mock private UserManagerInternal mMockUserManagerInternal; - @Mock private ContentProtectionBlocklistManager mMockContentProtectionBlocklistManager; + @Mock private ContentProtectionAllowlistManager mMockContentProtectionAllowlistManager; @Mock private ContentCaptureServiceInfo mMockContentCaptureServiceInfo; @@ -108,7 +107,7 @@ public class ContentCaptureManagerServiceTest { private List<List<String>> mDevCfgContentProtectionOptionalGroups = Collections.emptyList(); - private int mContentProtectionBlocklistManagersCreated; + private int mContentProtectionAllowlistManagersCreated; private int mContentProtectionServiceInfosCreated; @@ -132,10 +131,10 @@ public class ContentCaptureManagerServiceTest { @Test public void constructor_contentProtection_flagDisabled_noManagers() { - assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0); + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0); assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0); - verifyZeroInteractions(mMockContentProtectionBlocklistManager); + verifyZeroInteractions(mMockContentProtectionAllowlistManager); verifyZeroInteractions(mMockContentProtectionConsentManager); } @@ -145,10 +144,10 @@ public class ContentCaptureManagerServiceTest { mContentCaptureManagerService = new TestContentCaptureManagerService(); - assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0); + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0); assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0); - verifyZeroInteractions(mMockContentProtectionBlocklistManager); + verifyZeroInteractions(mMockContentProtectionAllowlistManager); verifyZeroInteractions(mMockContentProtectionConsentManager); } @@ -158,10 +157,10 @@ public class ContentCaptureManagerServiceTest { mContentCaptureManagerService = new TestContentCaptureManagerService(); - assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0); + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0); assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0); - verifyZeroInteractions(mMockContentProtectionBlocklistManager); + verifyZeroInteractions(mMockContentProtectionAllowlistManager); verifyZeroInteractions(mMockContentProtectionConsentManager); } @@ -171,25 +170,12 @@ public class ContentCaptureManagerServiceTest { mContentCaptureManagerService = new TestContentCaptureManagerService(); - assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(1); + assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(1); assertThat(mContentProtectionConsentManagersCreated).isEqualTo(1); assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); - verify(mMockContentProtectionBlocklistManager).updateBlocklist(anyInt()); verifyZeroInteractions(mMockContentProtectionConsentManager); } - @Test - public void setFineTuneParamsFromDeviceConfig_doesNotUpdateContentProtectionBlocklist() { - mDevCfgEnableContentProtectionReceiver = true; - mContentCaptureManagerService = new TestContentCaptureManagerService(); - mContentCaptureManagerService.mDevCfgContentProtectionAppsBlocklistSize += 100; - verify(mMockContentProtectionBlocklistManager).updateBlocklist(anyInt()); - - mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); - - verifyNoMoreInteractions(mMockContentProtectionBlocklistManager); - } - @Test public void getOptions_contentCaptureDisabled_contentProtectionDisabled() { mDevCfgEnableContentProtectionReceiver = true; @@ -201,13 +187,13 @@ public class ContentCaptureManagerServiceTest { assertThat(actual).isNull(); verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID); - verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test public void getOptions_contentCaptureDisabled_contentProtectionEnabled() { when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true); - when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); + when(mMockContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); mDevCfgEnableContentProtectionReceiver = true; mContentCaptureManagerService = new TestContentCaptureManagerService(); @@ -239,13 +225,13 @@ public class ContentCaptureManagerServiceTest { assertThat(actual.contentProtectionOptions.enableReceiver).isFalse(); assertThat(actual.whitelistedComponents).isNull(); verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID); - verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test public void getOptions_contentCaptureEnabled_contentProtectionEnabled() { when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true); - when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); + when(mMockContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); mDevCfgEnableContentProtectionReceiver = true; mContentCaptureManagerService = new TestContentCaptureManagerService(); mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( @@ -273,7 +259,7 @@ public class ContentCaptureManagerServiceTest { assertThat(actual).isFalse(); verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID); - verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test @@ -287,13 +273,13 @@ public class ContentCaptureManagerServiceTest { USER_ID, PACKAGE_NAME); assertThat(actual).isFalse(); - verify(mMockContentProtectionBlocklistManager).isAllowed(PACKAGE_NAME); + verify(mMockContentProtectionAllowlistManager).isAllowed(PACKAGE_NAME); } @Test public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionEnabled() { when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true); - when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); + when(mMockContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); mDevCfgEnableContentProtectionReceiver = true; mContentCaptureManagerService = new TestContentCaptureManagerService(); @@ -317,7 +303,7 @@ public class ContentCaptureManagerServiceTest { assertThat(actual).isTrue(); verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); - verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test @@ -331,7 +317,7 @@ public class ContentCaptureManagerServiceTest { assertThat(actual).isFalse(); verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID); - verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test @@ -345,13 +331,13 @@ public class ContentCaptureManagerServiceTest { USER_ID, COMPONENT_NAME); assertThat(actual).isFalse(); - verify(mMockContentProtectionBlocklistManager).isAllowed(PACKAGE_NAME); + verify(mMockContentProtectionAllowlistManager).isAllowed(PACKAGE_NAME); } @Test public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionEnabled() { when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true); - when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); + when(mMockContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); mDevCfgEnableContentProtectionReceiver = true; mContentCaptureManagerService = new TestContentCaptureManagerService(); @@ -375,13 +361,13 @@ public class ContentCaptureManagerServiceTest { assertThat(actual).isTrue(); verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); - verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test public void isContentProtectionReceiverEnabled_true() { when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true); - when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); + when(mMockContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); mDevCfgEnableContentProtectionReceiver = true; mContentCaptureManagerService = new TestContentCaptureManagerService(); @@ -400,7 +386,7 @@ public class ContentCaptureManagerServiceTest { assertThat(actual).isFalse(); verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); - verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test @@ -415,7 +401,7 @@ public class ContentCaptureManagerServiceTest { assertThat(actual).isFalse(); verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); - verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test @@ -431,7 +417,7 @@ public class ContentCaptureManagerServiceTest { assertThat(actual).isFalse(); verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt()); - verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + verify(mMockContentProtectionAllowlistManager, never()).isAllowed(anyString()); } @Test @@ -572,9 +558,9 @@ public class ContentCaptureManagerServiceTest { } @Override - protected ContentProtectionBlocklistManager createContentProtectionBlocklistManager() { - mContentProtectionBlocklistManagersCreated++; - return mMockContentProtectionBlocklistManager; + protected ContentProtectionAllowlistManager createContentProtectionAllowlistManager() { + mContentProtectionAllowlistManagersCreated++; + return mMockContentProtectionAllowlistManager; } @Override diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6767a85035feaab1e2415547f2a505d7a911239f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.contentprotection; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** + * Test for {@link ContentProtectionAllowlistManager}. + * + * <p>Run with: {@code atest FrameworksServicesTests: + * com.android.server.contentprotection.ContentProtectionAllowlistManagerTest} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ContentProtectionAllowlistManagerTest { + + private static final String PACKAGE_NAME = "com.test.package.name"; + + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + private ContentProtectionAllowlistManager mContentProtectionAllowlistManager; + + @Before + public void setup() { + mContentProtectionAllowlistManager = new ContentProtectionAllowlistManager(); + } + + @Test + public void isAllowed() { + boolean actual = mContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME); + + assertThat(actual).isFalse(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionBlocklistManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionBlocklistManagerTest.java deleted file mode 100644 index ba9956a63dca829115678f491fcb00cfc4e81cd6..0000000000000000000000000000000000000000 --- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionBlocklistManagerTest.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.contentprotection; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.content.pm.PackageInfo; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.google.common.collect.ImmutableList; - -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 java.util.ArrayList; -import java.util.List; - -/** - * Test for {@link ContentProtectionBlocklistManager}. - * - * <p>Run with: {@code atest - * FrameworksServicesTests: - * com.android.server.contentprotection.ContentProtectionBlocklistManagerTest} - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ContentProtectionBlocklistManagerTest { - - private static final String FIRST_PACKAGE_NAME = "com.test.first.package.name"; - - private static final String SECOND_PACKAGE_NAME = "com.test.second.package.name"; - - private static final String UNLISTED_PACKAGE_NAME = "com.test.unlisted.package.name"; - - private static final String PACKAGE_NAME_BLOCKLIST_FILENAME = - "/product/etc/res/raw/content_protection/package_name_blocklist.txt"; - - private static final PackageInfo PACKAGE_INFO = new PackageInfo(); - - private static final List<String> LINES = - ImmutableList.of(FIRST_PACKAGE_NAME, SECOND_PACKAGE_NAME); - - @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); - - @Mock private ContentProtectionPackageManager mMockContentProtectionPackageManager; - - private final List<String> mReadRawFiles = new ArrayList<>(); - - private ContentProtectionBlocklistManager mContentProtectionBlocklistManager; - - @Before - public void setup() { - mContentProtectionBlocklistManager = new TestContentProtectionBlocklistManager(); - } - - @Test - public void isAllowed_blocklistNotLoaded() { - boolean actual = mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); - - assertThat(actual).isFalse(); - assertThat(mReadRawFiles).isEmpty(); - verifyZeroInteractions(mMockContentProtectionPackageManager); - } - - @Test - public void isAllowed_inBlocklist() { - mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); - - boolean actual = mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); - - assertThat(actual).isFalse(); - verifyZeroInteractions(mMockContentProtectionPackageManager); - } - - @Test - public void isAllowed_packageInfoNotFound() { - mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); - when(mMockContentProtectionPackageManager.getPackageInfo(UNLISTED_PACKAGE_NAME)) - .thenReturn(null); - - boolean actual = mContentProtectionBlocklistManager.isAllowed(UNLISTED_PACKAGE_NAME); - - assertThat(actual).isFalse(); - verify(mMockContentProtectionPackageManager, never()) - .hasRequestedInternetPermissions(any()); - verify(mMockContentProtectionPackageManager, never()).isSystemApp(any()); - verify(mMockContentProtectionPackageManager, never()).isUpdatedSystemApp(any()); - } - - @Test - public void isAllowed_notRequestedInternet() { - mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); - when(mMockContentProtectionPackageManager.getPackageInfo(UNLISTED_PACKAGE_NAME)) - .thenReturn(PACKAGE_INFO); - when(mMockContentProtectionPackageManager.hasRequestedInternetPermissions(PACKAGE_INFO)) - .thenReturn(false); - - boolean actual = mContentProtectionBlocklistManager.isAllowed(UNLISTED_PACKAGE_NAME); - - assertThat(actual).isFalse(); - verify(mMockContentProtectionPackageManager, never()).isSystemApp(any()); - verify(mMockContentProtectionPackageManager, never()).isUpdatedSystemApp(any()); - } - - @Test - public void isAllowed_systemApp() { - mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); - when(mMockContentProtectionPackageManager.getPackageInfo(UNLISTED_PACKAGE_NAME)) - .thenReturn(PACKAGE_INFO); - when(mMockContentProtectionPackageManager.hasRequestedInternetPermissions(PACKAGE_INFO)) - .thenReturn(true); - when(mMockContentProtectionPackageManager.isSystemApp(PACKAGE_INFO)).thenReturn(true); - - boolean actual = mContentProtectionBlocklistManager.isAllowed(UNLISTED_PACKAGE_NAME); - - assertThat(actual).isFalse(); - verify(mMockContentProtectionPackageManager, never()).isUpdatedSystemApp(any()); - } - - @Test - public void isAllowed_updatedSystemApp() { - mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); - when(mMockContentProtectionPackageManager.getPackageInfo(UNLISTED_PACKAGE_NAME)) - .thenReturn(PACKAGE_INFO); - when(mMockContentProtectionPackageManager.hasRequestedInternetPermissions(PACKAGE_INFO)) - .thenReturn(true); - when(mMockContentProtectionPackageManager.isSystemApp(PACKAGE_INFO)).thenReturn(true); - when(mMockContentProtectionPackageManager.isUpdatedSystemApp(PACKAGE_INFO)) - .thenReturn(true); - - boolean actual = mContentProtectionBlocklistManager.isAllowed(UNLISTED_PACKAGE_NAME); - - assertThat(actual).isFalse(); - } - - @Test - public void isAllowed_allowed() { - mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); - when(mMockContentProtectionPackageManager.getPackageInfo(UNLISTED_PACKAGE_NAME)) - .thenReturn(PACKAGE_INFO); - when(mMockContentProtectionPackageManager.hasRequestedInternetPermissions(PACKAGE_INFO)) - .thenReturn(true); - when(mMockContentProtectionPackageManager.isSystemApp(PACKAGE_INFO)).thenReturn(false); - when(mMockContentProtectionPackageManager.isUpdatedSystemApp(PACKAGE_INFO)) - .thenReturn(false); - - boolean actual = mContentProtectionBlocklistManager.isAllowed(UNLISTED_PACKAGE_NAME); - - assertThat(actual).isTrue(); - } - - @Test - public void updateBlocklist_negativeSize() { - mContentProtectionBlocklistManager.updateBlocklist(/* blocklistSize= */ -1); - assertThat(mReadRawFiles).isEmpty(); - - mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); - verify(mMockContentProtectionPackageManager).getPackageInfo(FIRST_PACKAGE_NAME); - } - - @Test - public void updateBlocklist_zeroSize() { - mContentProtectionBlocklistManager.updateBlocklist(/* blocklistSize= */ 0); - assertThat(mReadRawFiles).isEmpty(); - - mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); - verify(mMockContentProtectionPackageManager).getPackageInfo(FIRST_PACKAGE_NAME); - } - - @Test - public void updateBlocklist_positiveSize_belowTotal() { - mContentProtectionBlocklistManager.updateBlocklist(/* blocklistSize= */ 1); - assertThat(mReadRawFiles).containsExactly(PACKAGE_NAME_BLOCKLIST_FILENAME); - - mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); - mContentProtectionBlocklistManager.isAllowed(SECOND_PACKAGE_NAME); - - verify(mMockContentProtectionPackageManager, never()).getPackageInfo(FIRST_PACKAGE_NAME); - verify(mMockContentProtectionPackageManager).getPackageInfo(SECOND_PACKAGE_NAME); - } - - @Test - public void updateBlocklist_positiveSize_aboveTotal() { - mContentProtectionBlocklistManager.updateBlocklist(LINES.size() + 1); - assertThat(mReadRawFiles).containsExactly(PACKAGE_NAME_BLOCKLIST_FILENAME); - - mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); - mContentProtectionBlocklistManager.isAllowed(SECOND_PACKAGE_NAME); - - verify(mMockContentProtectionPackageManager, never()).getPackageInfo(FIRST_PACKAGE_NAME); - verify(mMockContentProtectionPackageManager, never()).getPackageInfo(SECOND_PACKAGE_NAME); - } - - private final class TestContentProtectionBlocklistManager - extends ContentProtectionBlocklistManager { - - TestContentProtectionBlocklistManager() { - super(mMockContentProtectionPackageManager); - } - - @Override - protected List<String> readLinesFromRawFile(@NonNull String filename) { - mReadRawFiles.add(filename); - return LINES; - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionPackageManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionPackageManagerTest.java deleted file mode 100644 index 7d45ea4ce39aa8a1baa248c0eaeedc95445da4bc..0000000000000000000000000000000000000000 --- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionPackageManagerTest.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.contentprotection; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import android.Manifest.permission; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.PackageManager.PackageInfoFlags; -import android.testing.TestableContext; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -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; - -/** - * Test for {@link ContentProtectionPackageManager}. - * - * <p>Run with: {@code atest - * FrameworksServicesTests:com.android.server.contentprotection.ContentProtectionPackageManagerTest} - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ContentProtectionPackageManagerTest { - private static final String PACKAGE_NAME = "PACKAGE_NAME"; - - private static final PackageInfo EMPTY_PACKAGE_INFO = new PackageInfo(); - - private static final PackageInfo SYSTEM_APP_PACKAGE_INFO = createSystemAppPackageInfo(); - - private static final PackageInfo UPDATED_SYSTEM_APP_PACKAGE_INFO = - createUpdatedSystemAppPackageInfo(); - - @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); - - @Rule - public final TestableContext mContext = - new TestableContext(ApplicationProvider.getApplicationContext()); - - @Mock private PackageManager mMockPackageManager; - - private ContentProtectionPackageManager mContentProtectionPackageManager; - - @Before - public void setup() { - mContext.setMockPackageManager(mMockPackageManager); - mContentProtectionPackageManager = new ContentProtectionPackageManager(mContext); - } - - @Test - public void getPackageInfo_found() throws Exception { - PackageInfo expected = createPackageInfo(/* flags= */ 0); - when(mMockPackageManager.getPackageInfo(eq(PACKAGE_NAME), any(PackageInfoFlags.class))) - .thenReturn(expected); - - PackageInfo actual = mContentProtectionPackageManager.getPackageInfo(PACKAGE_NAME); - - assertThat(actual).isEqualTo(expected); - } - - @Test - public void getPackageInfo_notFound() throws Exception { - when(mMockPackageManager.getPackageInfo(eq(PACKAGE_NAME), any(PackageInfoFlags.class))) - .thenThrow(new NameNotFoundException()); - - PackageInfo actual = mContentProtectionPackageManager.getPackageInfo(PACKAGE_NAME); - - assertThat(actual).isNull(); - } - - @Test - public void getPackageInfo_null() { - PackageInfo actual = mContentProtectionPackageManager.getPackageInfo(PACKAGE_NAME); - - assertThat(actual).isNull(); - } - - @Test - public void isSystemApp_true() { - boolean actual = mContentProtectionPackageManager.isSystemApp(SYSTEM_APP_PACKAGE_INFO); - - assertThat(actual).isTrue(); - } - - @Test - public void isSystemApp_false() { - boolean actual = - mContentProtectionPackageManager.isSystemApp(UPDATED_SYSTEM_APP_PACKAGE_INFO); - - assertThat(actual).isFalse(); - } - - @Test - public void isSystemApp_noApplicationInfo() { - boolean actual = mContentProtectionPackageManager.isSystemApp(EMPTY_PACKAGE_INFO); - - assertThat(actual).isFalse(); - } - - @Test - public void isUpdatedSystemApp_true() { - boolean actual = - mContentProtectionPackageManager.isUpdatedSystemApp( - UPDATED_SYSTEM_APP_PACKAGE_INFO); - - assertThat(actual).isTrue(); - } - - @Test - public void isUpdatedSystemApp_false() { - boolean actual = - mContentProtectionPackageManager.isUpdatedSystemApp(SYSTEM_APP_PACKAGE_INFO); - - assertThat(actual).isFalse(); - } - - @Test - public void isUpdatedSystemApp_noApplicationInfo() { - boolean actual = mContentProtectionPackageManager.isUpdatedSystemApp(EMPTY_PACKAGE_INFO); - - assertThat(actual).isFalse(); - } - - @Test - public void hasRequestedInternetPermissions_true() { - PackageInfo packageInfo = createPackageInfo(new String[] {permission.INTERNET}); - - boolean actual = - mContentProtectionPackageManager.hasRequestedInternetPermissions(packageInfo); - - assertThat(actual).isTrue(); - } - - @Test - public void hasRequestedInternetPermissions_false() { - PackageInfo packageInfo = createPackageInfo(new String[] {permission.ACCESS_FINE_LOCATION}); - - boolean actual = - mContentProtectionPackageManager.hasRequestedInternetPermissions(packageInfo); - - assertThat(actual).isFalse(); - } - - @Test - public void hasRequestedInternetPermissions_noRequestedPermissions() { - boolean actual = - mContentProtectionPackageManager.hasRequestedInternetPermissions( - EMPTY_PACKAGE_INFO); - - assertThat(actual).isFalse(); - } - - private static PackageInfo createSystemAppPackageInfo() { - return createPackageInfo(ApplicationInfo.FLAG_SYSTEM); - } - - private static PackageInfo createUpdatedSystemAppPackageInfo() { - return createPackageInfo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); - } - - private static PackageInfo createPackageInfo(int flags) { - return createPackageInfo(flags, /* requestedPermissions= */ new String[0]); - } - - private static PackageInfo createPackageInfo(String[] requestedPermissions) { - return createPackageInfo(/* flags= */ 0, requestedPermissions); - } - - private static PackageInfo createPackageInfo(int flags, String[] requestedPermissions) { - PackageInfo packageInfo = new PackageInfo(); - packageInfo.packageName = PACKAGE_NAME; - packageInfo.applicationInfo = new ApplicationInfo(); - packageInfo.applicationInfo.packageName = PACKAGE_NAME; - packageInfo.applicationInfo.flags = flags; - packageInfo.requestedPermissions = requestedPermissions; - return packageInfo; - } -} diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java index 61c4d06131e16bf489dcddce85ac6a8430ccf0d9..270d5df5e702b8df64dce30d6339c52ddb550ede 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java @@ -203,5 +203,6 @@ class ShortcutKeyTestBase { mPhoneWindowManager.dispatchUnhandledKey(keyEvent); } } + mPhoneWindowManager.dispatchAllPendingEvents(); } } diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java index 8e870681bb83c2f6dd57eeb053b41b573ce604ae..f2721a556454d1fca3031096f579070d18341b07 100644 --- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java @@ -30,6 +30,7 @@ import static org.junit.Assert.assertTrue; import android.app.Instrumentation; import android.content.Context; +import android.os.Looper; import android.os.Handler; import android.os.HandlerThread; import android.os.Process; @@ -80,7 +81,7 @@ public class SingleKeyGestureTests { public void setUp() { mInstrumentation.runOnMainSync( () -> { - mDetector = SingleKeyGestureDetector.get(mContext); + mDetector = SingleKeyGestureDetector.get(mContext, Looper.myLooper()); initSingleKeyGestureRules(); }); diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index 261d3cc3c8d9606ca80161fbad82753964c525de..e26260a6836cceaf1b9e00dc9c170125ce917c57 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -118,7 +118,6 @@ import org.mockito.quality.Strictness; import java.util.function.Supplier; class TestPhoneWindowManager { - private static final long SHORTCUT_KEY_DELAY_MILLIS = 150; private static final long TEST_SINGLE_KEY_DELAY_MILLIS = SingleKeyGestureDetector.MULTI_PRESS_TIMEOUT + 1000L * HW_TIMEOUT_MULTIPLIER; @@ -188,7 +187,7 @@ class TestPhoneWindowManager { MockitoAnnotations.initMocks(this); mHandler = new Handler(mTestLooper.getLooper()); mContext = mockingDetails(context).isSpy() ? context : spy(context); - mHandler.post(() -> setUp(supportSettingsUpdate)); + setUp(supportSettingsUpdate); mTestLooper.dispatchAll(); } @@ -306,6 +305,10 @@ class TestPhoneWindowManager { mMockitoSession.finishMocking(); } + void dispatchAllPendingEvents() { + mTestLooper.dispatchAll(); + } + // Override accessibility setting and perform function. private void overrideLaunchAccessibility() { doReturn(true).when(mAccessibilityShortcutController) @@ -446,6 +449,7 @@ class TestPhoneWindowManager { doNothing().when(mPhoneWindowManager).sendCloseSystemWindows(); doReturn(true).when(mPhoneWindowManager).isUserSetupComplete(); doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt()); + doReturn(mSearchManager).when(mContext).getSystemService(eq(SearchManager.class)); } void overrideSearchManager(SearchManager searchManager) { @@ -500,29 +504,24 @@ class TestPhoneWindowManager { */ void assertTakeScreenshotCalled() { mTestLooper.dispatchAll(); - verify(mDisplayPolicy, timeout(SHORTCUT_KEY_DELAY_MILLIS)) - .takeScreenshot(anyInt(), anyInt()); + verify(mDisplayPolicy).takeScreenshot(anyInt(), anyInt()); } void assertShowGlobalActionsCalled() { mTestLooper.dispatchAll(); verify(mPhoneWindowManager).showGlobalActions(); - verify(mGlobalActions, timeout(SHORTCUT_KEY_DELAY_MILLIS)) - .showDialog(anyBoolean(), anyBoolean()); - verify(mPowerManager, timeout(SHORTCUT_KEY_DELAY_MILLIS)) - .userActivity(anyLong(), anyBoolean()); + verify(mGlobalActions).showDialog(anyBoolean(), anyBoolean()); + verify(mPowerManager).userActivity(anyLong(), anyBoolean()); } void assertVolumeMute() { mTestLooper.dispatchAll(); - verify(mAudioManagerInternal, timeout(SHORTCUT_KEY_DELAY_MILLIS)) - .silenceRingerModeInternal(eq("volume_hush")); + verify(mAudioManagerInternal).silenceRingerModeInternal(eq("volume_hush")); } void assertAccessibilityKeychordCalled() { mTestLooper.dispatchAll(); - verify(mAccessibilityShortcutController, - timeout(SHORTCUT_KEY_DELAY_MILLIS)).performAccessibilityShortcut(); + verify(mAccessibilityShortcutController).performAccessibilityShortcut(); } void assertDreamRequest() { @@ -532,14 +531,12 @@ class TestPhoneWindowManager { void assertPowerSleep() { mTestLooper.dispatchAll(); - verify(mPowerManager, - timeout(SHORTCUT_KEY_DELAY_MILLIS)).goToSleep(anyLong(), anyInt(), anyInt()); + verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt()); } void assertPowerWakeUp() { mTestLooper.dispatchAll(); - verify(mPowerManager, - timeout(SHORTCUT_KEY_DELAY_MILLIS)).wakeUp(anyLong(), anyInt(), anyString()); + verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString()); } void assertNoPowerSleep() { @@ -556,7 +553,7 @@ class TestPhoneWindowManager { void assertSearchManagerLaunchAssist() { mTestLooper.dispatchAll(); - verify(mSearchManager, timeout(SHORTCUT_KEY_DELAY_MILLIS)).launchAssist(any()); + verify(mSearchManager).launchAssist(any()); } void assertLaunchCategory(String category) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 40ac7b1ccdca2b143b7fa0927d7df24b134d6b9f..bdbfb7ad80df3c105eaee6fc21cb50fd3200ad2b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -2627,6 +2627,13 @@ public class ActivityRecordTests extends WindowTestsBase { // Can specify orientation if the current orientation candidate is orientation behind. assertEquals(SCREEN_ORIENTATION_LANDSCAPE, activity.getOrientation(SCREEN_ORIENTATION_BEHIND)); + + final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) + .setActivityTheme(android.R.style.Theme_Translucent) + .setCreateTask(true).build(); + assertFalse(translucentActivity.providesOrientation()); + translucentActivity.setOccludesParent(true); + assertTrue(translucentActivity.providesOrientation()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java index 6a738befba9a1cf1da9bca6fd73aa491cfdaba9f..9f584911aed70ca2bb06851d2bdf4f019b498a3b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java @@ -41,7 +41,6 @@ import android.view.SurfaceSession; import com.android.server.testutils.StubTransaction; import com.android.server.wm.utils.MockAnimationAdapter; -import com.android.window.flags.Flags; import org.junit.Before; import org.junit.Test; @@ -150,7 +149,7 @@ public class DimmerTests extends WindowTestsBase { mHost = new MockSurfaceBuildingContainer(mWm); mTransaction = spy(StubTransaction.class); mChild = new TestWindowContainer(mWm); - if (Flags.dimmerRefactor()) { + if (Dimmer.DIMMER_REFACTOR) { sTestAnimation = spy(new MockAnimationAdapter()); mDimmer = new SmoothDimmer(mHost, new MockAnimationAdapterFactory()); } else { @@ -177,7 +176,7 @@ public class DimmerTests extends WindowTestsBase { @Test public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Smooth() { - assumeTrue(Flags.dimmerRefactor()); + assumeTrue(Dimmer.DIMMER_REFACTOR); final float alpha = 0.7f; final int blur = 50; mHost.addChild(mChild, 0); @@ -197,7 +196,7 @@ public class DimmerTests extends WindowTestsBase { @Test public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Legacy() { - assumeFalse(Flags.dimmerRefactor()); + assumeFalse(Dimmer.DIMMER_REFACTOR); final float alpha = 0.7f; mHost.addChild(mChild, 0); mDimmer.adjustAppearance(mChild, alpha, 20); @@ -212,7 +211,7 @@ public class DimmerTests extends WindowTestsBase { @Test public void testDimBelowWithChildSurfaceDestroyedWhenReset_Smooth() { - assumeTrue(Flags.dimmerRefactor()); + assumeTrue(Dimmer.DIMMER_REFACTOR); mHost.addChild(mChild, 0); final float alpha = 0.8f; @@ -232,7 +231,7 @@ public class DimmerTests extends WindowTestsBase { @Test public void testDimBelowWithChildSurfaceDestroyedWhenReset_Legacy() { - assumeFalse(Flags.dimmerRefactor()); + assumeFalse(Dimmer.DIMMER_REFACTOR); mHost.addChild(mChild, 0); final float alpha = 0.8f; @@ -292,7 +291,7 @@ public class DimmerTests extends WindowTestsBase { @Test public void testRemoveDimImmediately_Smooth() { - assumeTrue(Flags.dimmerRefactor()); + assumeTrue(Dimmer.DIMMER_REFACTOR); mHost.addChild(mChild, 0); mDimmer.adjustAppearance(mChild, 1, 2); mDimmer.adjustRelativeLayer(mChild, -1); @@ -312,7 +311,7 @@ public class DimmerTests extends WindowTestsBase { @Test public void testRemoveDimImmediately_Legacy() { - assumeFalse(Flags.dimmerRefactor()); + assumeFalse(Dimmer.DIMMER_REFACTOR); mHost.addChild(mChild, 0); mDimmer.adjustAppearance(mChild, 1, 0); mDimmer.adjustRelativeLayer(mChild, -1); @@ -332,7 +331,7 @@ public class DimmerTests extends WindowTestsBase { @Test public void testDimmerWithBlurUpdatesTransaction_Legacy() { - assumeFalse(Flags.dimmerRefactor()); + assumeFalse(Dimmer.DIMMER_REFACTOR); mHost.addChild(mChild, 0); final int blurRadius = 50; diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index 5f92fd5f12e73687b20cbadc37b9d1e8ddf63a40..0d4c443ce1b088534e5c5138c5f7cb885cbacbe6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -676,60 +676,59 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT}) public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsPortrait() throws Exception { - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED)); } @Test @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR}) public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsNosensor() { - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_NOSENSOR); + assertEquals(SCREEN_ORIENTATION_NOSENSOR, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED)); } @Test @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR}) public void testOverrideOrientationIfNeeded_nosensorOverride_orientationFixed_returnsUnchanged() { - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_PORTRAIT)); } @Test @EnableCompatChanges({OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE}) public void testOverrideOrientationIfNeeded_reverseLandscapeOverride_orientationPortraitOrUndefined_returnsUnchanged() { - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT); - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_PORTRAIT)); + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED)); } @Test @EnableCompatChanges({OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE}) public void testOverrideOrientationIfNeeded_reverseLandscapeOverride_orientationLandscape_returnsReverseLandscape() { - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_LANDSCAPE), - SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_LANDSCAPE)); } @Test @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT}) public void testOverrideOrientationIfNeeded_portraitOverride_orientationFixed_returnsUnchanged() { - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_NOSENSOR), SCREEN_ORIENTATION_NOSENSOR); + assertEquals(SCREEN_ORIENTATION_NOSENSOR, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_NOSENSOR)); } @Test @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION}) public void testOverrideOrientationIfNeeded_portraitAndIgnoreFixedOverrides_returnsPortrait() { - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_NOSENSOR), SCREEN_ORIENTATION_PORTRAIT); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_NOSENSOR)); } @Test @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR, OVERRIDE_ANY_ORIENTATION}) public void testOverrideOrientationIfNeeded_noSensorAndIgnoreFixedOverrides_returnsNosensor() { - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_NOSENSOR); + assertEquals(SCREEN_ORIENTATION_NOSENSOR, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_PORTRAIT)); } @Test @@ -740,8 +739,8 @@ public class LetterboxUiControllerTest extends WindowTestsBase { mController = new LetterboxUiController(mWm, mActivity); - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED); + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED)); } @Test @@ -760,8 +759,8 @@ public class LetterboxUiControllerTest extends WindowTestsBase { doReturn(false).when(mDisplayContent.mDisplayRotationCompatPolicy) .isActivityEligibleForOrientationOverride(eq(mActivity)); - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED); + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED)); } @Test @@ -780,8 +779,8 @@ public class LetterboxUiControllerTest extends WindowTestsBase { doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy) .isActivityEligibleForOrientationOverride(eq(mActivity)); - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED)); } @Test @@ -789,8 +788,8 @@ public class LetterboxUiControllerTest extends WindowTestsBase { spyOn(mController); doReturn(true).when(mController).shouldApplyUserFullscreenOverride(); - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_USER); + assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED)); } @Test @@ -799,8 +798,8 @@ public class LetterboxUiControllerTest extends WindowTestsBase { spyOn(mController); doReturn(true).when(mController).shouldApplyUserFullscreenOverride(); - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_USER); + assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_PORTRAIT)); } @Test @@ -808,8 +807,8 @@ public class LetterboxUiControllerTest extends WindowTestsBase { spyOn(mController); doReturn(false).when(mController).shouldApplyUserFullscreenOverride(); - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_PORTRAIT), SCREEN_ORIENTATION_PORTRAIT); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_PORTRAIT)); } @Test @@ -817,15 +816,15 @@ public class LetterboxUiControllerTest extends WindowTestsBase { spyOn(mController); doReturn(true).when(mController).shouldApplyUserMinAspectRatioOverride(); - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED)); - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_LOCKED), SCREEN_ORIENTATION_PORTRAIT); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_LOCKED)); // unchanged if orientation is specified - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_LANDSCAPE), SCREEN_ORIENTATION_LANDSCAPE); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_LANDSCAPE)); } @Test @@ -833,8 +832,8 @@ public class LetterboxUiControllerTest extends WindowTestsBase { spyOn(mController); doReturn(false).when(mController).shouldApplyUserMinAspectRatioOverride(); - assertEquals(mController.overrideOrientationIfNeeded( - /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_UNSPECIFIED); + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mController.overrideOrientationIfNeeded( + /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED)); } // shouldApplyUser...Override @@ -1286,16 +1285,16 @@ public class LetterboxUiControllerTest extends WindowTestsBase { mActivity = setUpActivityWithComponent(); mController = new LetterboxUiController(mWm, mActivity); - assertEquals(mController.getFixedOrientationLetterboxAspectRatio( - mActivity.getParent().getConfiguration()), 1.5f, /* delta */ 0.01); + assertEquals(1.5f, mController.getFixedOrientationLetterboxAspectRatio( + mActivity.getParent().getConfiguration()), /* delta */ 0.01); spyOn(mDisplayContent.mDisplayRotationCompatPolicy); doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy) .isTreatmentEnabledForActivity(eq(mActivity)); - assertEquals(mController.getFixedOrientationLetterboxAspectRatio( - mActivity.getParent().getConfiguration()), mController.getSplitScreenAspectRatio(), - /* delta */ 0.01); + assertEquals(mController.getSplitScreenAspectRatio(), + mController.getFixedOrientationLetterboxAspectRatio( + mActivity.getParent().getConfiguration()), /* delta */ 0.01); } private void mockThatProperty(String propertyName, boolean value) throws Exception { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 9146889e37d9d042b980f39f9342303efec75077..e0ed642d3130bccfec4441d5c229bd10afa76f72 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1184,6 +1184,7 @@ class WindowTestsBase extends SystemServiceTestsBase { private boolean mCreateTask = false; private Task mParentTask; private int mActivityFlags; + private int mActivityTheme; private int mLaunchMode; private int mResizeMode = RESIZE_MODE_RESIZEABLE; private float mMaxAspectRatio; @@ -1232,6 +1233,14 @@ class WindowTestsBase extends SystemServiceTestsBase { return this; } + ActivityBuilder setActivityTheme(int theme) { + mActivityTheme = theme; + // Use the real package of test so it can get a valid context for theme. + mComponent = ComponentName.createRelative(mService.mContext.getPackageName(), + DEFAULT_COMPONENT_CLASS_NAME + sCurrentActivityId++); + return this; + } + ActivityBuilder setActivityFlags(int flags) { mActivityFlags = flags; return this; @@ -1381,6 +1390,9 @@ class WindowTestsBase extends SystemServiceTestsBase { if (mTargetActivity != null) { aInfo.targetActivity = mTargetActivity; } + if (mActivityTheme != 0) { + aInfo.theme = mActivityTheme; + } aInfo.flags |= mActivityFlags; aInfo.launchMode = mLaunchMode; aInfo.resizeMode = mResizeMode; diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 038b93fc2a43a97a2a1445ae4248288db5a4c735..0b70b40e25567f05d31f37e2dbbe13e18902e661 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3147,6 +3147,14 @@ public class CarrierConfigManager { */ public static final String KEY_ROAMING_OPERATOR_STRING_ARRAY = "roaming_operator_string_array"; + /** + * Config to show the roaming indicator (i.e. the "R" icon) from the status bar when roaming. + * The roaming indicator will be shown if this is {@code true} and will not be shown if this is + * {@code false}. + */ + @FlaggedApi(Flags.FLAG_HIDE_ROAMING_ICON) + public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool"; + /** * URL from which the proto containing the public key of the Carrier used for * IMSI encryption will be downloaded. @@ -3313,11 +3321,11 @@ public class CarrierConfigManager { * If {@code false} the SPN display checks if the current MCC/MNC is different from the * SIM card's MCC/MNC. * - * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY - * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY - * @see KEY_NON_ROAMING_OPERATOR_STRING_ARRAY - * @see KEY_ROAMING_OPERATOR_STRING_ARRAY - * @see KEY_FORCE_HOME_NETWORK_BOOL + * @see #KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY + * @see #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY + * @see #KEY_NON_ROAMING_OPERATOR_STRING_ARRAY + * @see #KEY_ROAMING_OPERATOR_STRING_ARRAY + * @see #KEY_FORCE_HOME_NETWORK_BOOL * * @hide */ @@ -10193,6 +10201,7 @@ public class CarrierConfigManager { false); sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null); sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null); + sDefaults.putBoolean(KEY_SHOW_ROAMING_INDICATOR_BOOL, true); sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false); sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false); sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true); diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 158e0656b574bc87b257f7cfce596c46f7dce515..dc55dd240be0eb4ecf87c6a78998808d231060e2 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -236,6 +236,12 @@ public final class SampleDataClass implements Parcelable { * Transient fields are completely ignored and can be used for caching. */ private transient LinkAddress[] mLinkAddresses6; + /** + * For hidden lists, getters, setters and adders will be hidden. + * @hide + */ + private @NonNull List<LinkAddress> mLinkAddresses7 = new ArrayList<>(); + /** * When using transient fields for caching it's often also a good idea to initialize them * lazily. @@ -486,6 +492,8 @@ public final class SampleDataClass implements Parcelable { * Making a field public will suppress getter generation in favor of accessing it directly. * @param linkAddresses5 * Final fields suppress generating a setter (when setters are requested). + * @param linkAddresses7 + * For hidden lists, getters, setters and adders will be hidden. * @param stringRes * Fields with certain annotations are automatically validated in constructor * @@ -529,9 +537,10 @@ public final class SampleDataClass implements Parcelable { @State int state, @NonNull CharSequence charSeq, @Nullable LinkAddress[] linkAddresses5, + @NonNull List<LinkAddress> linkAddresses7, @StringRes int stringRes, @android.annotation.IntRange(from = 0, to = 6) int dayOfWeek, - @Size(2) @NonNull @FloatRange(from = 0f) float[] coords, + @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] coords, @NonNull IBinder token, @Nullable ICompanionDeviceManager iPCInterface) { this.mNum = num; @@ -595,6 +604,9 @@ public final class SampleDataClass implements Parcelable { AnnotationValidations.validate( NonNull.class, null, charSeq); this.mLinkAddresses5 = linkAddresses5; + this.mLinkAddresses7 = linkAddresses7; + AnnotationValidations.validate( + NonNull.class, null, mLinkAddresses7); this.mStringRes = stringRes; AnnotationValidations.validate( StringRes.class, null, mStringRes); @@ -609,13 +621,11 @@ public final class SampleDataClass implements Parcelable { "value", 2); AnnotationValidations.validate( NonNull.class, null, mCoords); - int coordsSize = mCoords.length; - for (int i = 0; i < coordsSize; i++) { - AnnotationValidations.validate( - FloatRange.class, null, mCoords[i], - "from", 0f); - } - + AnnotationValidations.validate( + Each.class, null, mCoords); + AnnotationValidations.validate( + FloatRange.class, null, mCoords, + "from", 0f); this.mToken = token; AnnotationValidations.validate( NonNull.class, null, mToken); @@ -776,6 +786,16 @@ public final class SampleDataClass implements Parcelable { return mLinkAddresses5; } + /** + * For hidden lists, getters, setters and adders will be hidden. + * + * @hide + */ + @DataClass.Generated.Member + public @NonNull List<LinkAddress> getLinkAddresses7() { + return mLinkAddresses7; + } + /** * Fields with certain annotations are automatically validated in constructor * @@ -815,7 +835,7 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int) */ @DataClass.Generated.Member - public @Size(2) @NonNull @FloatRange(from = 0f) float[] getCoords() { + public @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] getCoords() { return mCoords; } @@ -1064,6 +1084,19 @@ public final class SampleDataClass implements Parcelable { return this; } + /** + * For hidden lists, getters, setters and adders will be hidden. + * + * @hide + */ + @DataClass.Generated.Member + public @NonNull SampleDataClass setLinkAddresses7(@NonNull List<LinkAddress> value) { + mLinkAddresses7 = value; + AnnotationValidations.validate( + NonNull.class, null, mLinkAddresses7); + return this; + } + /** * Fields with certain annotations are automatically validated in constructor * @@ -1111,20 +1144,18 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int) */ @DataClass.Generated.Member - public @NonNull SampleDataClass setCoords(@Size(2) @NonNull @FloatRange(from = 0f) float... value) { + public @NonNull SampleDataClass setCoords(@Size(2) @NonNull @Each @FloatRange(from = 0f) float... value) { mCoords = value; AnnotationValidations.validate( Size.class, null, mCoords.length, "value", 2); AnnotationValidations.validate( NonNull.class, null, mCoords); - int coordsSize = mCoords.length; - for (int i = 0; i < coordsSize; i++) { - AnnotationValidations.validate( - FloatRange.class, null, mCoords[i], - "from", 0f); - } - + AnnotationValidations.validate( + Each.class, null, mCoords); + AnnotationValidations.validate( + FloatRange.class, null, mCoords, + "from", 0f); return this; } @@ -1172,6 +1203,7 @@ public final class SampleDataClass implements Parcelable { "state = " + stateToString(mState) + ", " + "charSeq = " + charSeq + ", " + "linkAddresses5 = " + java.util.Arrays.toString(mLinkAddresses5) + ", " + + "linkAddresses7 = " + mLinkAddresses7 + ", " + "stringRes = " + mStringRes + ", " + "dayOfWeek = " + mDayOfWeek + ", " + "coords = " + java.util.Arrays.toString(mCoords) + ", " + @@ -1210,6 +1242,7 @@ public final class SampleDataClass implements Parcelable { && mState == that.mState && Objects.equals(charSeq, that.charSeq) && java.util.Arrays.equals(mLinkAddresses5, that.mLinkAddresses5) + && Objects.equals(mLinkAddresses7, that.mLinkAddresses7) && mStringRes == that.mStringRes && mDayOfWeek == that.mDayOfWeek && java.util.Arrays.equals(mCoords, that.mCoords) @@ -1241,6 +1274,7 @@ public final class SampleDataClass implements Parcelable { _hash = 31 * _hash + mState; _hash = 31 * _hash + Objects.hashCode(charSeq); _hash = 31 * _hash + java.util.Arrays.hashCode(mLinkAddresses5); + _hash = 31 * _hash + Objects.hashCode(mLinkAddresses7); _hash = 31 * _hash + mStringRes; _hash = 31 * _hash + mDayOfWeek; _hash = 31 * _hash + java.util.Arrays.hashCode(mCoords); @@ -1270,6 +1304,7 @@ public final class SampleDataClass implements Parcelable { actionInt.acceptInt(this, "state", mState); actionObject.acceptObject(this, "charSeq", charSeq); actionObject.acceptObject(this, "linkAddresses5", mLinkAddresses5); + actionObject.acceptObject(this, "linkAddresses7", mLinkAddresses7); actionInt.acceptInt(this, "stringRes", mStringRes); actionInt.acceptInt(this, "dayOfWeek", mDayOfWeek); actionObject.acceptObject(this, "coords", mCoords); @@ -1298,6 +1333,7 @@ public final class SampleDataClass implements Parcelable { action.acceptObject(this, "state", mState); action.acceptObject(this, "charSeq", charSeq); action.acceptObject(this, "linkAddresses5", mLinkAddresses5); + action.acceptObject(this, "linkAddresses7", mLinkAddresses7); action.acceptObject(this, "stringRes", mStringRes); action.acceptObject(this, "dayOfWeek", mDayOfWeek); action.acceptObject(this, "coords", mCoords); @@ -1338,7 +1374,7 @@ public final class SampleDataClass implements Parcelable { if (mOtherParcelable != null) flg |= 0x40; if (mLinkAddresses4 != null) flg |= 0x800; if (mLinkAddresses5 != null) flg |= 0x10000; - if (mIPCInterface != null) flg |= 0x200000; + if (mIPCInterface != null) flg |= 0x400000; dest.writeLong(flg); dest.writeInt(mNum); dest.writeInt(mNum2); @@ -1357,6 +1393,7 @@ public final class SampleDataClass implements Parcelable { dest.writeInt(mState); dest.writeCharSequence(charSeq); if (mLinkAddresses5 != null) dest.writeTypedArray(mLinkAddresses5, flags); + dest.writeParcelableList(mLinkAddresses7, flags); dest.writeInt(mStringRes); dest.writeInt(mDayOfWeek); dest.writeFloatArray(mCoords); @@ -1395,11 +1432,13 @@ public final class SampleDataClass implements Parcelable { int state = in.readInt(); CharSequence _charSeq = (CharSequence) in.readCharSequence(); LinkAddress[] linkAddresses5 = (flg & 0x10000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR); + List<LinkAddress> linkAddresses7 = new ArrayList<>(); + in.readParcelableList(linkAddresses7, LinkAddress.class.getClassLoader()); int stringRes = in.readInt(); int dayOfWeek = in.readInt(); float[] coords = in.createFloatArray(); IBinder token = (IBinder) in.readStrongBinder(); - ICompanionDeviceManager iPCInterface = (flg & 0x200000) == 0 ? null : ICompanionDeviceManager.Stub.asInterface(in.readStrongBinder()); + ICompanionDeviceManager iPCInterface = (flg & 0x400000) == 0 ? null : ICompanionDeviceManager.Stub.asInterface(in.readStrongBinder()); this.mNum = num; this.mNum2 = num2; @@ -1462,6 +1501,9 @@ public final class SampleDataClass implements Parcelable { AnnotationValidations.validate( NonNull.class, null, charSeq); this.mLinkAddresses5 = linkAddresses5; + this.mLinkAddresses7 = linkAddresses7; + AnnotationValidations.validate( + NonNull.class, null, mLinkAddresses7); this.mStringRes = stringRes; AnnotationValidations.validate( StringRes.class, null, mStringRes); @@ -1476,13 +1518,11 @@ public final class SampleDataClass implements Parcelable { "value", 2); AnnotationValidations.validate( NonNull.class, null, mCoords); - int coordsSize = mCoords.length; - for (int i = 0; i < coordsSize; i++) { - AnnotationValidations.validate( - FloatRange.class, null, mCoords[i], - "from", 0f); - } - + AnnotationValidations.validate( + Each.class, null, mCoords); + AnnotationValidations.validate( + FloatRange.class, null, mCoords, + "from", 0f); this.mToken = token; AnnotationValidations.validate( NonNull.class, null, mToken); @@ -1529,9 +1569,10 @@ public final class SampleDataClass implements Parcelable { private @State int mState; private @NonNull CharSequence charSeq; private @Nullable LinkAddress[] mLinkAddresses5; + private @NonNull List<LinkAddress> mLinkAddresses7; private @StringRes int mStringRes; private @android.annotation.IntRange(from = 0, to = 6) int mDayOfWeek; - private @Size(2) @NonNull @FloatRange(from = 0f) float[] mCoords; + private @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] mCoords; private @NonNull IBinder mToken; private @Nullable ICompanionDeviceManager mIPCInterface; @@ -1822,6 +1863,30 @@ public final class SampleDataClass implements Parcelable { return this; } + /** + * For hidden lists, getters, setters and adders will be hidden. + * + * @hide + */ + @DataClass.Generated.Member + public @NonNull Builder setLinkAddresses7(@NonNull List<LinkAddress> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20000; + mLinkAddresses7 = value; + return this; + } + + /** @see #setLinkAddresses7 @hide */ + @DataClass.Generated.Member + public @NonNull Builder addLinkAddresses7(@NonNull LinkAddress value) { + // You can refine this method's name by providing item's singular name, e.g.: + // @DataClass.PluralOf("item")) mItems = ... + + if (mLinkAddresses7 == null) setLinkAddresses7(new ArrayList<>()); + mLinkAddresses7.add(value); + return this; + } + /** * Fields with certain annotations are automatically validated in constructor * @@ -1837,7 +1902,7 @@ public final class SampleDataClass implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setStringRes(@StringRes int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x20000; + mBuilderFieldsSet |= 0x40000; mStringRes = value; return this; } @@ -1852,7 +1917,7 @@ public final class SampleDataClass implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setDayOfWeek(@android.annotation.IntRange(from = 0, to = 6) int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x40000; + mBuilderFieldsSet |= 0x80000; mDayOfWeek = value; return this; } @@ -1867,9 +1932,9 @@ public final class SampleDataClass implements Parcelable { * @see AnnotationValidations#validate(Class, Size, int, String, int) */ @DataClass.Generated.Member - public @NonNull Builder setCoords(@Size(2) @NonNull @FloatRange(from = 0f) float... value) { + public @NonNull Builder setCoords(@Size(2) @NonNull @Each @FloatRange(from = 0f) float... value) { checkNotUsed(); - mBuilderFieldsSet |= 0x80000; + mBuilderFieldsSet |= 0x100000; mCoords = value; return this; } @@ -1880,7 +1945,7 @@ public final class SampleDataClass implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setToken(@NonNull IBinder value) { checkNotUsed(); - mBuilderFieldsSet |= 0x100000; + mBuilderFieldsSet |= 0x200000; mToken = value; return this; } @@ -1891,7 +1956,7 @@ public final class SampleDataClass implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setIPCInterface(@NonNull ICompanionDeviceManager value) { checkNotUsed(); - mBuilderFieldsSet |= 0x200000; + mBuilderFieldsSet |= 0x400000; mIPCInterface = value; return this; } @@ -1899,7 +1964,7 @@ public final class SampleDataClass implements Parcelable { /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull SampleDataClass build() { checkNotUsed(); - mBuilderFieldsSet |= 0x400000; // Mark builder used + mBuilderFieldsSet |= 0x800000; // Mark builder used if ((mBuilderFieldsSet & 0x10) == 0) { mName2 = "Bob"; @@ -1935,18 +2000,21 @@ public final class SampleDataClass implements Parcelable { charSeq = ""; } if ((mBuilderFieldsSet & 0x20000) == 0) { - mStringRes = 0; + mLinkAddresses7 = new ArrayList<>(); } if ((mBuilderFieldsSet & 0x40000) == 0) { - mDayOfWeek = 3; + mStringRes = 0; } if ((mBuilderFieldsSet & 0x80000) == 0) { - mCoords = new float[] { 0f, 0f }; + mDayOfWeek = 3; } if ((mBuilderFieldsSet & 0x100000) == 0) { - mToken = new Binder(); + mCoords = new float[] { 0f, 0f }; } if ((mBuilderFieldsSet & 0x200000) == 0) { + mToken = new Binder(); + } + if ((mBuilderFieldsSet & 0x400000) == 0) { mIPCInterface = null; } SampleDataClass o = new SampleDataClass( @@ -1967,6 +2035,7 @@ public final class SampleDataClass implements Parcelable { mState, charSeq, mLinkAddresses5, + mLinkAddresses7, mStringRes, mDayOfWeek, mCoords, @@ -1976,7 +2045,7 @@ public final class SampleDataClass implements Parcelable { } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x400000) != 0) { + if ((mBuilderFieldsSet & 0x800000) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -1984,10 +2053,10 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1616541539978L, + time = 1697693846352L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", - inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final int STATE_UNDEFINED\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange int mDayOfWeek\nprivate @android.annotation.Size @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange float[] mCoords\nprivate @android.annotation.NonNull android.os.IBinder mToken\nprivate @android.annotation.Nullable android.companion.ICompanionDeviceManager mIPCInterface\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") + inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final int STATE_UNDEFINED\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses7\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange int mDayOfWeek\nprivate @android.annotation.Size @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange float[] mCoords\nprivate @android.annotation.NonNull android.os.IBinder mToken\nprivate @android.annotation.Nullable android.companion.ICompanionDeviceManager mIPCInterface\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated private void __metadata() {} diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 685733386cae99054a982c6c527549fcc32d713d..d3a8b033dfff9dec360a52fe4b1cc573be4378ae 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -327,7 +327,8 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) { +"return$maybeCast this;" } - val javadocSeeSetter = "/** @see #$setterName */" + val javadocSeeSetter = + if (isHidden()) "/** @see #$setterName @hide */" else "/** @see #$setterName */" val adderName = "add$SingularName" val singularNameCustomizationHint = if (SingularNameOrNull == null) { @@ -750,6 +751,15 @@ fun ClassPrinter.generateGetters() { } } +fun FieldInfo.isHidden(): Boolean { + if (javadocFull != null) { + (javadocFull ?: "/**\n */").lines().forEach { + if (it.contains("@hide")) return true + } + } + return false +} + fun FieldInfo.generateFieldJavadoc(forceHide: Boolean = false) = classPrinter { if (javadocFull != null || forceHide) { var hidden = false