Merge "Merge "AudioDeviceBroker: Fix not setting proper call volume for LE Headset" into udc-dev am: 0eeb400e76" into udc-dev-plus-aosp am: 7aa90ef61f
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23258556
Change-Id: I4efc6d8a118661e72e1b6b53e553c9a1b469a753
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 776e34b..a6313db 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -571,15 +571,6 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
private static final long MEDIA_CONTROL_SESSION_ACTIONS = 203800354L;
- /**
- * Media controls based on {@link android.app.Notification.MediaStyle} notifications will be
- * required to include a non-empty title, either in the {@link android.media.MediaMetadata} or
- * notification title.
- */
- @ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- private static final long MEDIA_CONTROL_REQUIRES_TITLE = 274775190L;
-
@UnsupportedAppUsage
private Context mContext;
private IStatusBarService mService;
@@ -1226,21 +1217,6 @@
}
/**
- * Checks whether the given package must include a non-empty title for its media controls.
- *
- * @param packageName App posting media controls
- * @param user Current user handle
- * @return true if the app is required to provide a non-empty title
- *
- * @hide
- */
- @RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
- android.Manifest.permission.LOG_COMPAT_CHANGE})
- public static boolean isMediaTitleRequiredForApp(String packageName, UserHandle user) {
- return CompatChanges.isChangeEnabled(MEDIA_CONTROL_REQUIRES_TITLE, packageName, user);
- }
-
- /**
* Checks whether the supplied activity can {@link Activity#startActivityForResult(Intent, int)}
* a system activity that captures content on the screen to take a screenshot.
*
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index e908ced..48b5cac 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -212,14 +212,7 @@
@GuardedBy("mLock")
private boolean mFoldedDeviceState;
- private final CameraManager.DeviceStateListener mFoldStateListener =
- new CameraManager.DeviceStateListener() {
- @Override
- public final void onDeviceStateChanged(boolean folded) {
- synchronized (mLock) {
- mFoldedDeviceState = folded;
- }
- }};
+ private CameraManager.DeviceStateListener mFoldStateListener;
private static final String TAG = "CameraCharacteristics";
@@ -245,7 +238,18 @@
/**
* Return the device state listener for this Camera characteristics instance
*/
- CameraManager.DeviceStateListener getDeviceStateListener() { return mFoldStateListener; }
+ CameraManager.DeviceStateListener getDeviceStateListener() {
+ if (mFoldStateListener == null) {
+ mFoldStateListener = new CameraManager.DeviceStateListener() {
+ @Override
+ public final void onDeviceStateChanged(boolean folded) {
+ synchronized (mLock) {
+ mFoldedDeviceState = folded;
+ }
+ }};
+ }
+ return mFoldStateListener;
+ }
/**
* Overrides the property value
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 51501b5..85f8ca6 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1836,6 +1836,7 @@
ctx.getSystemService(DeviceStateManager.class).registerCallback(
new HandlerExecutor(mDeviceStateHandler), mFoldStateListener);
} catch (IllegalStateException e) {
+ mFoldStateListener = null;
Log.v(TAG, "Failed to register device state listener!");
Log.v(TAG, "Device state dependent characteristics updates will not be" +
"functional!");
diff --git a/core/res/res/layout/shutdown_dialog.xml b/core/res/res/layout/shutdown_dialog.xml
index ec67aa8..726c255 100644
--- a/core/res/res/layout/shutdown_dialog.xml
+++ b/core/res/res/layout/shutdown_dialog.xml
@@ -40,7 +40,7 @@
android:fontFamily="@string/config_headlineFontFamily"/>
<TextView
- android:id="@+id/text2"
+ android:id="@id/text2"
android:layout_width="wrap_content"
android:layout_height="32sp"
android:text="@string/shutdown_progress"
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index fd74185..8420934 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -167,9 +167,4 @@
true, routing from the android emergency number database will be ignored. -->
<bool name="ignore_emergency_number_routing_from_db">false</bool>
<java-symbol type="bool" name="ignore_emergency_number_routing_from_db" />
-
- <!-- Whether "Virtual DSDA", i.e. in-call IMS connectivity can be provided on both subs with
- only single logical modem, by using its data connection in addition to cellular IMS. -->
- <bool name="config_enable_virtual_dsda">false</bool>
- <java-symbol type="bool" name="config_enable_virtual_dsda" />
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 94cfc09..d374bdd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5020,6 +5020,7 @@
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" />
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" />
+
<java-symbol name="materialColorOnSecondaryFixedVariant" type="attr"/>
<java-symbol name="materialColorOnTertiaryFixedVariant" type="attr"/>
<java-symbol name="materialColorSurfaceContainerLowest" type="attr"/>
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DynamicSummary.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DynamicSummary.java
index c9d9b57..5b7899b 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DynamicSummary.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DynamicSummary.java
@@ -16,7 +16,7 @@
package com.android.settingslib.drawer;
-/** Interface for {@link SwitchController} whose instances support dynamic summary */
+/** Interface for {@link EntryController} whose instances support dynamic summary */
public interface DynamicSummary {
/** @return the dynamic summary text */
String getDynamicSummary();
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DynamicTitle.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DynamicTitle.java
index af711dd..cb15773 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DynamicTitle.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DynamicTitle.java
@@ -16,7 +16,7 @@
package com.android.settingslib.drawer;
-/** Interface for {@link SwitchController} whose instances support dynamic title */
+/** Interface for {@link EntryController} whose instances support dynamic title */
public interface DynamicTitle {
/** @return the dynamic title text */
String getDynamicTitle();
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/EntriesProvider.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/EntriesProvider.java
new file mode 100644
index 0000000..1c14c0a
--- /dev/null
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/EntriesProvider.java
@@ -0,0 +1,222 @@
+/*
+ * 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.settingslib.drawer;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An abstract class for injecting entries to Settings.
+ */
+public abstract class EntriesProvider extends ContentProvider {
+ private static final String TAG = "EntriesProvider";
+
+ public static final String METHOD_GET_ENTRY_DATA = "getEntryData";
+ public static final String METHOD_GET_PROVIDER_ICON = "getProviderIcon";
+ public static final String METHOD_GET_DYNAMIC_TITLE = "getDynamicTitle";
+ public static final String METHOD_GET_DYNAMIC_SUMMARY = "getDynamicSummary";
+ public static final String METHOD_IS_CHECKED = "isChecked";
+ public static final String METHOD_ON_CHECKED_CHANGED = "onCheckedChanged";
+
+ /**
+ * @deprecated use {@link #METHOD_GET_ENTRY_DATA} instead.
+ */
+ @Deprecated
+ public static final String METHOD_GET_SWITCH_DATA = "getSwitchData";
+
+ public static final String EXTRA_ENTRY_DATA = "entry_data";
+ public static final String EXTRA_SWITCH_CHECKED_STATE = "checked_state";
+ public static final String EXTRA_SWITCH_SET_CHECKED_ERROR = "set_checked_error";
+ public static final String EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE = "set_checked_error_message";
+
+ /**
+ * @deprecated use {@link #EXTRA_ENTRY_DATA} instead.
+ */
+ @Deprecated
+ public static final String EXTRA_SWITCH_DATA = "switch_data";
+
+ private String mAuthority;
+ private final Map<String, EntryController> mControllerMap = new LinkedHashMap<>();
+ private final List<Bundle> mEntryDataList = new ArrayList<>();
+
+ /**
+ * Get a list of {@link EntryController} for this provider.
+ */
+ protected abstract List<? extends EntryController> createEntryControllers();
+
+ protected EntryController getController(String key) {
+ return mControllerMap.get(key);
+ }
+
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ mAuthority = info.authority;
+ Log.i(TAG, mAuthority);
+ super.attachInfo(context, info);
+ }
+
+ @Override
+ public boolean onCreate() {
+ final List<? extends EntryController> controllers = createEntryControllers();
+ if (controllers == null || controllers.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+
+ for (EntryController controller : controllers) {
+ final String key = controller.getKey();
+ if (TextUtils.isEmpty(key)) {
+ throw new NullPointerException("Entry key cannot be null: "
+ + controller.getClass().getSimpleName());
+ } else if (mControllerMap.containsKey(key)) {
+ throw new IllegalArgumentException("Entry key " + key + " is duplicated by: "
+ + controller.getClass().getSimpleName());
+ }
+
+ controller.setAuthority(mAuthority);
+ mControllerMap.put(key, controller);
+ if (!(controller instanceof PrimarySwitchController)) {
+ mEntryDataList.add(controller.getBundle());
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Bundle call(String method, String uriString, Bundle extras) {
+ final Bundle bundle = new Bundle();
+ final String key = extras != null
+ ? extras.getString(META_DATA_PREFERENCE_KEYHINT)
+ : null;
+ if (TextUtils.isEmpty(key)) {
+ switch (method) {
+ case METHOD_GET_ENTRY_DATA:
+ bundle.putParcelableList(EXTRA_ENTRY_DATA, mEntryDataList);
+ return bundle;
+ case METHOD_GET_SWITCH_DATA:
+ bundle.putParcelableList(EXTRA_SWITCH_DATA, mEntryDataList);
+ return bundle;
+ default:
+ return null;
+ }
+ }
+
+ final EntryController controller = mControllerMap.get(key);
+ if (controller == null) {
+ return null;
+ }
+
+ switch (method) {
+ case METHOD_GET_ENTRY_DATA:
+ case METHOD_GET_SWITCH_DATA:
+ if (!(controller instanceof PrimarySwitchController)) {
+ return controller.getBundle();
+ }
+ break;
+ case METHOD_GET_PROVIDER_ICON:
+ if (controller instanceof ProviderIcon) {
+ return ((ProviderIcon) controller).getProviderIcon();
+ }
+ break;
+ case METHOD_GET_DYNAMIC_TITLE:
+ if (controller instanceof DynamicTitle) {
+ bundle.putString(META_DATA_PREFERENCE_TITLE,
+ ((DynamicTitle) controller).getDynamicTitle());
+ return bundle;
+ }
+ break;
+ case METHOD_GET_DYNAMIC_SUMMARY:
+ if (controller instanceof DynamicSummary) {
+ bundle.putString(META_DATA_PREFERENCE_SUMMARY,
+ ((DynamicSummary) controller).getDynamicSummary());
+ return bundle;
+ }
+ break;
+ case METHOD_IS_CHECKED:
+ if (controller instanceof ProviderSwitch) {
+ bundle.putBoolean(EXTRA_SWITCH_CHECKED_STATE,
+ ((ProviderSwitch) controller).isSwitchChecked());
+ return bundle;
+ }
+ break;
+ case METHOD_ON_CHECKED_CHANGED:
+ if (controller instanceof ProviderSwitch) {
+ return onSwitchCheckedChanged(extras.getBoolean(EXTRA_SWITCH_CHECKED_STATE),
+ (ProviderSwitch) controller);
+ }
+ break;
+ }
+ return null;
+ }
+
+ private Bundle onSwitchCheckedChanged(boolean checked, ProviderSwitch controller) {
+ final boolean success = controller.onSwitchCheckedChanged(checked);
+ final Bundle bundle = new Bundle();
+ bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, !success);
+ if (success) {
+ if (controller instanceof DynamicSummary) {
+ ((EntryController) controller).notifySummaryChanged(getContext());
+ }
+ } else {
+ bundle.putString(EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE,
+ controller.getSwitchErrorMessage(checked));
+ }
+ return bundle;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/EntryController.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/EntryController.java
new file mode 100644
index 0000000..5d6e6a3
--- /dev/null
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/EntryController.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 com.android.settingslib.drawer;
+
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_SUMMARY;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_TITLE;
+import static com.android.settingslib.drawer.TileUtils.EXTRA_CATEGORY_KEY;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_PENDING_INTENT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE_URI;
+
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+
+/**
+ * A controller that manages events for switch.
+ */
+public abstract class EntryController {
+
+ private String mAuthority;
+
+ /**
+ * Returns the key for this switch.
+ */
+ public abstract String getKey();
+
+ /**
+ * Returns the {@link MetaData} for this switch.
+ */
+ protected abstract MetaData getMetaData();
+
+ /**
+ * Notify registered observers that title was updated and attempt to sync changes.
+ */
+ public void notifyTitleChanged(Context context) {
+ if (this instanceof DynamicTitle) {
+ notifyChanged(context, METHOD_GET_DYNAMIC_TITLE);
+ }
+ }
+
+ /**
+ * Notify registered observers that summary was updated and attempt to sync changes.
+ */
+ public void notifySummaryChanged(Context context) {
+ if (this instanceof DynamicSummary) {
+ notifyChanged(context, METHOD_GET_DYNAMIC_SUMMARY);
+ }
+ }
+
+ void setAuthority(String authority) {
+ mAuthority = authority;
+ }
+
+ Bundle getBundle() {
+ final MetaData metaData = getMetaData();
+ if (metaData == null) {
+ throw new NullPointerException("Should not return null in getMetaData()");
+ }
+
+ final Bundle bundle = metaData.build();
+ final String uriString = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(mAuthority)
+ .build()
+ .toString();
+ bundle.putString(META_DATA_PREFERENCE_KEYHINT, getKey());
+ if (this instanceof ProviderIcon) {
+ bundle.putString(META_DATA_PREFERENCE_ICON_URI, uriString);
+ }
+ if (this instanceof DynamicTitle) {
+ bundle.putString(META_DATA_PREFERENCE_TITLE_URI, uriString);
+ }
+ if (this instanceof DynamicSummary) {
+ bundle.putString(META_DATA_PREFERENCE_SUMMARY_URI, uriString);
+ }
+ if (this instanceof ProviderSwitch) {
+ bundle.putString(META_DATA_PREFERENCE_SWITCH_URI, uriString);
+ }
+ return bundle;
+ }
+
+ private void notifyChanged(Context context, String method) {
+ final Uri uri = TileUtils.buildUri(mAuthority, method, getKey());
+ context.getContentResolver().notifyChange(uri, null);
+ }
+
+ /**
+ * Collects all meta data of the item.
+ */
+ protected static class MetaData {
+ private String mCategory;
+ private int mOrder;
+ @DrawableRes
+ private int mIcon;
+ private int mIconBackgroundHint;
+ private int mIconBackgroundArgb;
+ private Boolean mIconTintable;
+ @StringRes
+ private int mTitleId;
+ private String mTitle;
+ @StringRes
+ private int mSummaryId;
+ private String mSummary;
+ private PendingIntent mPendingIntent;
+
+ /**
+ * @param category the category of the switch. This value must be from {@link CategoryKey}.
+ */
+ public MetaData(@NonNull String category) {
+ mCategory = category;
+ }
+
+ /**
+ * Set the order of the item that should be displayed on screen. Bigger value items displays
+ * closer on top.
+ */
+ public MetaData setOrder(int order) {
+ mOrder = order;
+ return this;
+ }
+
+ /** Set the icon that should be displayed for the item. */
+ public MetaData setIcon(@DrawableRes int icon) {
+ mIcon = icon;
+ return this;
+ }
+
+ /** Set the icon background color. The value may or may not be used by Settings app. */
+ public MetaData setIconBackgoundHint(int hint) {
+ mIconBackgroundHint = hint;
+ return this;
+ }
+
+ /** Set the icon background color as raw ARGB. */
+ public MetaData setIconBackgoundArgb(int argb) {
+ mIconBackgroundArgb = argb;
+ return this;
+ }
+
+ /** Specify whether the icon is tintable. */
+ public MetaData setIconTintable(boolean tintable) {
+ mIconTintable = tintable;
+ return this;
+ }
+
+ /** Set the title that should be displayed for the item. */
+ public MetaData setTitle(@StringRes int id) {
+ mTitleId = id;
+ return this;
+ }
+
+ /** Set the title that should be displayed for the item. */
+ public MetaData setTitle(String title) {
+ mTitle = title;
+ return this;
+ }
+
+ /** Set the summary text that should be displayed for the item. */
+ public MetaData setSummary(@StringRes int id) {
+ mSummaryId = id;
+ return this;
+ }
+
+ /** Set the summary text that should be displayed for the item. */
+ public MetaData setSummary(String summary) {
+ mSummary = summary;
+ return this;
+ }
+
+ public MetaData setPendingIntent(PendingIntent pendingIntent) {
+ mPendingIntent = pendingIntent;
+ return this;
+ }
+
+ protected Bundle build() {
+ final Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_CATEGORY_KEY, mCategory);
+
+ if (mOrder != 0) {
+ bundle.putInt(META_DATA_KEY_ORDER, mOrder);
+ }
+
+ if (mIcon != 0) {
+ bundle.putInt(META_DATA_PREFERENCE_ICON, mIcon);
+ }
+ if (mIconBackgroundHint != 0) {
+ bundle.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT, mIconBackgroundHint);
+ }
+ if (mIconBackgroundArgb != 0) {
+ bundle.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, mIconBackgroundArgb);
+ }
+ if (mIconTintable != null) {
+ bundle.putBoolean(META_DATA_PREFERENCE_ICON_TINTABLE, mIconTintable);
+ }
+
+ if (mTitleId != 0) {
+ bundle.putInt(META_DATA_PREFERENCE_TITLE, mTitleId);
+ } else if (mTitle != null) {
+ bundle.putString(META_DATA_PREFERENCE_TITLE, mTitle);
+ }
+
+ if (mSummaryId != 0) {
+ bundle.putInt(META_DATA_PREFERENCE_SUMMARY, mSummaryId);
+ } else if (mSummary != null) {
+ bundle.putString(META_DATA_PREFERENCE_SUMMARY, mSummary);
+ }
+
+ if (mPendingIntent != null) {
+ bundle.putParcelable(META_DATA_PREFERENCE_PENDING_INTENT, mPendingIntent);
+ }
+
+ return bundle;
+ }
+ }
+}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderIcon.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderIcon.java
index 2945d5c1..3aa6fcb 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderIcon.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderIcon.java
@@ -19,7 +19,7 @@
import android.os.Bundle;
/**
- * Interface for {@link SwitchController} whose instances support icon provided from the content
+ * Interface for {@link EntryController} whose instances support icon provided from the content
* provider
*/
public interface ProviderIcon {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderSwitch.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderSwitch.java
new file mode 100644
index 0000000..47eb31c
--- /dev/null
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderSwitch.java
@@ -0,0 +1,41 @@
+/*
+ * 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.settingslib.drawer;
+
+/**
+ * Interface for {@link EntryController} whose instances support switch widget provided from the
+ * content provider
+ */
+public interface ProviderSwitch {
+ /**
+ * Returns the checked state of this switch.
+ */
+ boolean isSwitchChecked();
+
+ /**
+ * Called when the checked state of this switch is changed.
+ *
+ * @return true if the checked state was successfully changed, otherwise false
+ */
+ boolean onSwitchCheckedChanged(boolean checked);
+
+ /**
+ * Returns the error message which will be toasted when {@link #onSwitchCheckedChanged} returns
+ * false.
+ */
+ String getSwitchErrorMessage(boolean attemptedChecked);
+}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderTile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderTile.java
index 54da585..b775e93 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderTile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ProviderTile.java
@@ -75,7 +75,7 @@
if (infoList != null && !infoList.isEmpty()) {
final ProviderInfo providerInfo = infoList.get(0).providerInfo;
mComponentInfo = providerInfo;
- setMetaData(TileUtils.getSwitchDataFromProvider(context, providerInfo.authority,
+ setMetaData(TileUtils.getEntryDataFromProvider(context, providerInfo.authority,
mKey));
} else {
Log.e(TAG, "Cannot find package info for "
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchController.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchController.java
index 23669b2..a1a4e58 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchController.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchController.java
@@ -16,38 +16,16 @@
package com.android.settingslib.drawer;
-import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_SUMMARY;
-import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_TITLE;
-import static com.android.settingslib.drawer.SwitchesProvider.METHOD_IS_CHECKED;
-import static com.android.settingslib.drawer.TileUtils.EXTRA_CATEGORY_KEY;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE_URI;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Bundle;
-
-import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
-import androidx.annotation.StringRes;
/**
* A controller that manages events for switch.
+ *
+ * @deprecated Use {@link EntriesProvider} with {@link ProviderSwitch} instead.
*/
-public abstract class SwitchController {
+@Deprecated
+public abstract class SwitchController extends EntryController implements ProviderSwitch {
- private String mAuthority;
/**
* Returns the key for this switch.
@@ -55,11 +33,6 @@
public abstract String getSwitchKey();
/**
- * Returns the {@link MetaData} for this switch.
- */
- protected abstract MetaData getMetaData();
-
- /**
* Returns the checked state of this switch.
*/
protected abstract boolean isChecked();
@@ -76,181 +49,41 @@
*/
protected abstract String getErrorMessage(boolean attemptedChecked);
- /**
- * Notify registered observers that title was updated and attempt to sync changes.
- */
- public void notifyTitleChanged(Context context) {
- if (this instanceof DynamicTitle) {
- notifyChanged(context, METHOD_GET_DYNAMIC_TITLE);
- }
+ @Override
+ public String getKey() {
+ return getSwitchKey();
+ }
+
+ @Override
+ public boolean isSwitchChecked() {
+ return isChecked();
+ }
+
+ @Override
+ public boolean onSwitchCheckedChanged(boolean checked) {
+ return onCheckedChanged(checked);
+ }
+
+ @Override
+ public String getSwitchErrorMessage(boolean attemptedChecked) {
+ return getErrorMessage(attemptedChecked);
}
/**
- * Notify registered observers that summary was updated and attempt to sync changes.
+ * Same as {@link EntryController.MetaData}, for backwards compatibility purpose.
+ *
+ * @deprecated Use {@link EntryController.MetaData} instead.
*/
- public void notifySummaryChanged(Context context) {
- if (this instanceof DynamicSummary) {
- notifyChanged(context, METHOD_GET_DYNAMIC_SUMMARY);
- }
- }
-
- /**
- * Notify registered observers that checked state was updated and attempt to sync changes.
- */
- public void notifyCheckedChanged(Context context) {
- notifyChanged(context, METHOD_IS_CHECKED);
- }
-
- void setAuthority(String authority) {
- mAuthority = authority;
- }
-
- Bundle getBundle() {
- final MetaData metaData = getMetaData();
- if (metaData == null) {
- throw new NullPointerException("Should not return null in getMetaData()");
- }
-
- final Bundle bundle = metaData.build();
- final String uriString = new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority(mAuthority)
- .build()
- .toString();
- bundle.putString(META_DATA_PREFERENCE_KEYHINT, getSwitchKey());
- bundle.putString(META_DATA_PREFERENCE_SWITCH_URI, uriString);
- if (this instanceof ProviderIcon) {
- bundle.putString(META_DATA_PREFERENCE_ICON_URI, uriString);
- }
- if (this instanceof DynamicTitle) {
- bundle.putString(META_DATA_PREFERENCE_TITLE_URI, uriString);
- }
- if (this instanceof DynamicSummary) {
- bundle.putString(META_DATA_PREFERENCE_SUMMARY_URI, uriString);
- }
- return bundle;
- }
-
- private void notifyChanged(Context context, String method) {
- final Uri uri = TileUtils.buildUri(mAuthority, method, getSwitchKey());
- context.getContentResolver().notifyChange(uri, null);
- }
-
- /**
- * Collects all meta data of the item.
- */
- protected static class MetaData {
- private String mCategory;
- private int mOrder;
- @DrawableRes
- private int mIcon;
- private int mIconBackgroundHint;
- private int mIconBackgroundArgb;
- private Boolean mIconTintable;
- @StringRes
- private int mTitleId;
- private String mTitle;
- @StringRes
- private int mSummaryId;
- private String mSummary;
-
+ @Deprecated
+ protected static class MetaData extends EntryController.MetaData {
/**
* @param category the category of the switch. This value must be from {@link CategoryKey}.
+ *
+ * @deprecated Use {@link EntryController.MetaData} instead.
*/
+ @Deprecated
public MetaData(@NonNull String category) {
- mCategory = category;
- }
-
- /**
- * Set the order of the item that should be displayed on screen. Bigger value items displays
- * closer on top.
- */
- public MetaData setOrder(int order) {
- mOrder = order;
- return this;
- }
-
- /** Set the icon that should be displayed for the item. */
- public MetaData setIcon(@DrawableRes int icon) {
- mIcon = icon;
- return this;
- }
-
- /** Set the icon background color. The value may or may not be used by Settings app. */
- public MetaData setIconBackgoundHint(int hint) {
- mIconBackgroundHint = hint;
- return this;
- }
-
- /** Set the icon background color as raw ARGB. */
- public MetaData setIconBackgoundArgb(int argb) {
- mIconBackgroundArgb = argb;
- return this;
- }
-
- /** Specify whether the icon is tintable. */
- public MetaData setIconTintable(boolean tintable) {
- mIconTintable = tintable;
- return this;
- }
-
- /** Set the title that should be displayed for the item. */
- public MetaData setTitle(@StringRes int id) {
- mTitleId = id;
- return this;
- }
-
- /** Set the title that should be displayed for the item. */
- public MetaData setTitle(String title) {
- mTitle = title;
- return this;
- }
-
- /** Set the summary text that should be displayed for the item. */
- public MetaData setSummary(@StringRes int id) {
- mSummaryId = id;
- return this;
- }
-
- /** Set the summary text that should be displayed for the item. */
- public MetaData setSummary(String summary) {
- mSummary = summary;
- return this;
- }
-
- private Bundle build() {
- final Bundle bundle = new Bundle();
- bundle.putString(EXTRA_CATEGORY_KEY, mCategory);
-
- if (mOrder != 0) {
- bundle.putInt(META_DATA_KEY_ORDER, mOrder);
- }
-
- if (mIcon != 0) {
- bundle.putInt(META_DATA_PREFERENCE_ICON, mIcon);
- }
- if (mIconBackgroundHint != 0) {
- bundle.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT, mIconBackgroundHint);
- }
- if (mIconBackgroundArgb != 0) {
- bundle.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, mIconBackgroundArgb);
- }
- if (mIconTintable != null) {
- bundle.putBoolean(META_DATA_PREFERENCE_ICON_TINTABLE, mIconTintable);
- }
-
- if (mTitleId != 0) {
- bundle.putInt(META_DATA_PREFERENCE_TITLE, mTitleId);
- } else if (mTitle != null) {
- bundle.putString(META_DATA_PREFERENCE_TITLE, mTitle);
- }
-
- if (mSummaryId != 0) {
- bundle.putInt(META_DATA_PREFERENCE_SUMMARY, mSummaryId);
- } else if (mSummary != null) {
- bundle.putString(META_DATA_PREFERENCE_SUMMARY, mSummary);
- }
- return bundle;
+ super(category);
}
}
}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
index f2b3e30..ad00ced 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
@@ -16,46 +16,15 @@
package com.android.settingslib.drawer;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
-import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.pm.ProviderInfo;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
/**
* An abstract class for injecting switches to Settings.
+ *
+ * @deprecated Use {@link EntriesProvider} instead.
*/
-public abstract class SwitchesProvider extends ContentProvider {
- private static final String TAG = "SwitchesProvider";
-
- public static final String METHOD_GET_SWITCH_DATA = "getSwitchData";
- public static final String METHOD_GET_PROVIDER_ICON = "getProviderIcon";
- public static final String METHOD_GET_DYNAMIC_TITLE = "getDynamicTitle";
- public static final String METHOD_GET_DYNAMIC_SUMMARY = "getDynamicSummary";
- public static final String METHOD_IS_CHECKED = "isChecked";
- public static final String METHOD_ON_CHECKED_CHANGED = "onCheckedChanged";
-
- public static final String EXTRA_SWITCH_DATA = "switch_data";
- public static final String EXTRA_SWITCH_CHECKED_STATE = "checked_state";
- public static final String EXTRA_SWITCH_SET_CHECKED_ERROR = "set_checked_error";
- public static final String EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE = "set_checked_error_message";
-
- private String mAuthority;
- private final Map<String, SwitchController> mControllerMap = new LinkedHashMap<>();
- private final List<Bundle> mSwitchDataList = new ArrayList<>();
+@Deprecated
+public abstract class SwitchesProvider extends EntriesProvider {
/**
* Get a list of {@link SwitchController} for this provider.
@@ -63,129 +32,7 @@
protected abstract List<SwitchController> createSwitchControllers();
@Override
- public void attachInfo(Context context, ProviderInfo info) {
- mAuthority = info.authority;
- Log.i(TAG, mAuthority);
- super.attachInfo(context, info);
- }
-
- @Override
- public boolean onCreate() {
- final List<SwitchController> controllers = createSwitchControllers();
- if (controllers == null || controllers.isEmpty()) {
- throw new IllegalArgumentException();
- }
-
- controllers.forEach(controller -> {
- final String key = controller.getSwitchKey();
- if (TextUtils.isEmpty(key)) {
- throw new NullPointerException("Switch key cannot be null: "
- + controller.getClass().getSimpleName());
- } else if (mControllerMap.containsKey(key)) {
- throw new IllegalArgumentException("Switch key " + key + " is duplicated by: "
- + controller.getClass().getSimpleName());
- }
-
- controller.setAuthority(mAuthority);
- mControllerMap.put(key, controller);
- if (!(controller instanceof PrimarySwitchController)) {
- mSwitchDataList.add(controller.getBundle());
- }
- });
- return true;
- }
-
- @Override
- public Bundle call(String method, String uriString, Bundle extras) {
- final Bundle bundle = new Bundle();
- final String key = extras != null
- ? extras.getString(META_DATA_PREFERENCE_KEYHINT)
- : null;
- if (TextUtils.isEmpty(key)) {
- if (METHOD_GET_SWITCH_DATA.equals(method)) {
- bundle.putParcelableList(EXTRA_SWITCH_DATA, mSwitchDataList);
- return bundle;
- }
- return null;
- }
-
- final SwitchController controller = mControllerMap.get(key);
- if (controller == null) {
- return null;
- }
-
- switch (method) {
- case METHOD_GET_SWITCH_DATA:
- if (!(controller instanceof PrimarySwitchController)) {
- return controller.getBundle();
- }
- break;
- case METHOD_GET_PROVIDER_ICON:
- if (controller instanceof ProviderIcon) {
- return ((ProviderIcon) controller).getProviderIcon();
- }
- break;
- case METHOD_GET_DYNAMIC_TITLE:
- if (controller instanceof DynamicTitle) {
- bundle.putString(META_DATA_PREFERENCE_TITLE,
- ((DynamicTitle) controller).getDynamicTitle());
- return bundle;
- }
- break;
- case METHOD_GET_DYNAMIC_SUMMARY:
- if (controller instanceof DynamicSummary) {
- bundle.putString(META_DATA_PREFERENCE_SUMMARY,
- ((DynamicSummary) controller).getDynamicSummary());
- return bundle;
- }
- break;
- case METHOD_IS_CHECKED:
- bundle.putBoolean(EXTRA_SWITCH_CHECKED_STATE, controller.isChecked());
- return bundle;
- case METHOD_ON_CHECKED_CHANGED:
- return onCheckedChanged(extras.getBoolean(EXTRA_SWITCH_CHECKED_STATE), controller);
- }
- return null;
- }
-
- private Bundle onCheckedChanged(boolean checked, SwitchController controller) {
- final boolean success = controller.onCheckedChanged(checked);
- final Bundle bundle = new Bundle();
- bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, !success);
- if (success) {
- if (controller instanceof DynamicSummary) {
- controller.notifySummaryChanged(getContext());
- }
- } else {
- bundle.putString(EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE,
- controller.getErrorMessage(checked));
- }
- return bundle;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getType(Uri uri) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- throw new UnsupportedOperationException();
+ protected List<? extends EntryController> createEntryControllers() {
+ return createSwitchControllers();
}
}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index a0c8ac4..00dd8cc 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -19,6 +19,7 @@
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_NEW_TASK;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
@@ -29,6 +30,7 @@
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ComponentInfo;
@@ -47,6 +49,7 @@
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.HashMap;
/**
* Description of a single dashboard tile that the user can select.
@@ -60,6 +63,8 @@
*/
public ArrayList<UserHandle> userHandle = new ArrayList<>();
+ public HashMap<UserHandle, PendingIntent> pendingIntentMap = new HashMap<>();
+
@VisibleForTesting
long mLastUpdateTime;
private final String mComponentPackage;
@@ -186,6 +191,13 @@
}
/**
+ * Check whether tile has a pending intent.
+ */
+ public boolean hasPendingIntent() {
+ return !pendingIntentMap.isEmpty();
+ }
+
+ /**
* Title of the tile that is shown to the user.
*/
public CharSequence getTitle(Context context) {
@@ -395,6 +407,76 @@
return TextUtils.equals(profile, PROFILE_PRIMARY);
}
+ /**
+ * Returns whether the tile belongs to another group / category.
+ */
+ public boolean hasGroupKey() {
+ return mMetaData != null
+ && !TextUtils.isEmpty(mMetaData.getString(META_DATA_PREFERENCE_GROUP_KEY));
+ }
+
+ /**
+ * Returns the group / category key this tile belongs to.
+ */
+ public String getGroupKey() {
+ return (mMetaData == null) ? null : mMetaData.getString(META_DATA_PREFERENCE_GROUP_KEY);
+ }
+
+ /**
+ * The type of the tile.
+ */
+ public enum Type {
+ /**
+ * A preference that can be tapped on to open a new page.
+ */
+ ACTION,
+
+ /**
+ * A preference that can be tapped on to open an external app.
+ */
+ EXTERNAL_ACTION,
+
+ /**
+ * A preference that shows an on / off switch that can be toggled by the user.
+ */
+ SWITCH,
+
+ /**
+ * A preference with both an on / off switch, and a tappable area that can perform an
+ * action.
+ */
+ SWITCH_WITH_ACTION,
+
+ /**
+ * A preference category with a title that can be used to group multiple preferences
+ * together.
+ */
+ GROUP;
+ }
+
+ /**
+ * Returns the type of the tile.
+ *
+ * @see Type
+ */
+ public Type getType() {
+ boolean hasExternalAction = hasPendingIntent();
+ boolean hasAction = hasExternalAction || this instanceof ActivityTile;
+ boolean hasSwitch = hasSwitch();
+
+ if (hasSwitch && hasAction) {
+ return Type.SWITCH_WITH_ACTION;
+ } else if (hasSwitch) {
+ return Type.SWITCH;
+ } else if (hasExternalAction) {
+ return Type.EXTERNAL_ACTION;
+ } else if (hasAction) {
+ return Type.ACTION;
+ } else {
+ return Type.GROUP;
+ }
+ }
+
public static final Comparator<Tile> TILE_COMPARATOR =
(lhs, rhs) -> rhs.getOrder() - lhs.getOrder();
}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index acc0087..e46db75 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -113,6 +113,12 @@
public static final String META_DATA_PREFERENCE_KEYHINT = "com.android.settings.keyhint";
/**
+ * Name of the meta-data item that can be set in the AndroidManifest.xml or in the content
+ * provider to specify the key of a group / category where this preference belongs to.
+ */
+ public static final String META_DATA_PREFERENCE_GROUP_KEY = "com.android.settings.group_key";
+
+ /**
* Order of the item that should be displayed on screen. Bigger value items displays closer on
* top.
*/
@@ -202,6 +208,13 @@
"com.android.settings.switch_uri";
/**
+ * Name of the meta-data item that can be set from the content provider providing the intent
+ * that will be executed when the user taps on the preference.
+ */
+ public static final String META_DATA_PREFERENCE_PENDING_INTENT =
+ "com.android.settings.pending_intent";
+
+ /**
* Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile,
* the app will always be run in the primary profile.
*
@@ -331,12 +344,12 @@
continue;
}
final ProviderInfo providerInfo = resolved.providerInfo;
- final List<Bundle> switchData = getSwitchDataFromProvider(context,
+ final List<Bundle> entryData = getEntryDataFromProvider(context,
providerInfo.authority);
- if (switchData == null || switchData.isEmpty()) {
+ if (entryData == null || entryData.isEmpty()) {
continue;
}
- for (Bundle metaData : switchData) {
+ for (Bundle metaData : entryData) {
loadTile(user, addedCache, defaultCategory, outTiles, intent, metaData,
providerInfo);
}
@@ -386,27 +399,43 @@
if (!tile.userHandle.contains(user)) {
tile.userHandle.add(user);
}
+ if (metaData.containsKey(META_DATA_PREFERENCE_PENDING_INTENT)) {
+ tile.pendingIntentMap.put(
+ user, metaData.getParcelable(META_DATA_PREFERENCE_PENDING_INTENT));
+ }
if (!outTiles.contains(tile)) {
outTiles.add(tile);
}
}
- /** Returns the switch data of the key specified from the provider */
+ /** Returns the entry data of the key specified from the provider */
// TODO(b/144732809): rearrange methods by access level modifiers
- static Bundle getSwitchDataFromProvider(Context context, String authority, String key) {
+ static Bundle getEntryDataFromProvider(Context context, String authority, String key) {
final Map<String, IContentProvider> providerMap = new ArrayMap<>();
- final Uri uri = buildUri(authority, SwitchesProvider.METHOD_GET_SWITCH_DATA, key);
- return getBundleFromUri(context, uri, providerMap, null /* bundle */);
+ final Uri uri = buildUri(authority, EntriesProvider.METHOD_GET_ENTRY_DATA, key);
+ Bundle result = getBundleFromUri(context, uri, providerMap, null /* bundle */);
+ if (result == null) {
+ Uri fallbackUri = buildUri(authority, EntriesProvider.METHOD_GET_SWITCH_DATA, key);
+ result = getBundleFromUri(context, fallbackUri, providerMap, null /* bundle */);
+ }
+ return result;
}
- /** Returns all switch data from the provider */
- private static List<Bundle> getSwitchDataFromProvider(Context context, String authority) {
+ /** Returns all entry data from the provider */
+ private static List<Bundle> getEntryDataFromProvider(Context context, String authority) {
final Map<String, IContentProvider> providerMap = new ArrayMap<>();
- final Uri uri = buildUri(authority, SwitchesProvider.METHOD_GET_SWITCH_DATA);
+ final Uri uri = buildUri(authority, EntriesProvider.METHOD_GET_ENTRY_DATA);
final Bundle result = getBundleFromUri(context, uri, providerMap, null /* bundle */);
- return result != null
- ? result.getParcelableArrayList(SwitchesProvider.EXTRA_SWITCH_DATA)
- : null;
+ if (result != null) {
+ return result.getParcelableArrayList(EntriesProvider.EXTRA_ENTRY_DATA);
+ } else {
+ Uri fallbackUri = buildUri(authority, EntriesProvider.METHOD_GET_SWITCH_DATA);
+ Bundle fallbackResult =
+ getBundleFromUri(context, fallbackUri, providerMap, null /* bundle */);
+ return fallbackResult != null
+ ? fallbackResult.getParcelableArrayList(EntriesProvider.EXTRA_SWITCH_DATA)
+ : null;
+ }
}
/**
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 058065e..bac6306 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -519,12 +519,9 @@
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Daugiau laiko."</string>
<string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Mažiau laiko."</string>
<string name="cancel" msgid="5665114069455378395">"Atšaukti"</string>
- <!-- no translation found for next (2699398661093607009) -->
- <skip />
- <!-- no translation found for back (5554327870352703710) -->
- <skip />
- <!-- no translation found for save (3745809743277153149) -->
- <skip />
+ <string name="next" msgid="2699398661093607009">"Kitas"</string>
+ <string name="back" msgid="5554327870352703710">"Atgal"</string>
+ <string name="save" msgid="3745809743277153149">"Išsaugoti"</string>
<string name="okay" msgid="949938843324579502">"Gerai"</string>
<string name="done" msgid="381184316122520313">"Atlikta"</string>
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Signalai ir priminimai"</string>
@@ -579,12 +576,9 @@
<string name="user_add_user_title" msgid="5457079143694924885">"Pridėti naują naudotoją?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"Galite bendrinti šį įrenginį su kitais žmonėmis sukūrę papildomų naudotojų. Kiekvienam naudotojui suteikiama atskira erdvė, kurią jie gali tinkinti naudodami programas, ekrano foną ir kt. Be to, naudotojai gali koreguoti įrenginio nustatymus, pvz., „Wi‑Fi“, kurie taikomi visiems.\n\nKai pridedate naują naudotoją, šis asmuo turi nusistatyti savo erdvę.\n\nBet kuris naudotojas gali atnaujinti visų kitų naudotojų programas. Pasiekiamumo nustatymai ir paslaugos gali nebūti perkeltos naujam naudotojui."</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"Kai pridedate naują naudotoją, šis asmuo turi nustatyti savo vietą.\n\nBet kuris naudotojas gali atnaujinti visų kitų naudotojų programas."</string>
- <!-- no translation found for user_grant_admin_title (5157031020083343984) -->
- <skip />
- <!-- no translation found for user_grant_admin_message (1673791931033486709) -->
- <skip />
- <!-- no translation found for user_grant_admin_button (5441486731331725756) -->
- <skip />
+ <string name="user_grant_admin_title" msgid="5157031020083343984">"Nustatyti šį naudotoją kaip administratorių?"</string>
+ <string name="user_grant_admin_message" msgid="1673791931033486709">"Administratoriai turi specialių privilegijų, kurių kiti naudotojai neturi. Administratorius gali tvarkyti visus naudotojus, atnaujinti ar iš naujo nustatyti šį įrenginį, keisti nustatymus, peržiūrėti visas įdiegtas programas ir suteikti administratoriaus privilegijas kitiems naudotojams arba jas panaikinti."</string>
+ <string name="user_grant_admin_button" msgid="5441486731331725756">"Nustatyti kaip administratorių"</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"Nustatyti naudotoją dabar?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"Įsitikinkite, kad asmuo gali paimti įrenginį ir nustatyti savo vietą"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Nustatyti profilį dabar?"</string>
@@ -616,10 +610,8 @@
<string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bus pradėta nauja svečio sesija ir iš esamos sesijos bus ištrintos visos programos ir duomenys"</string>
<string name="guest_exit_dialog_title" msgid="1846494656849381804">"Išeiti iš svečio režimo?"</string>
<string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bus ištrintos esamos svečio sesijos programos ir duomenys"</string>
- <!-- no translation found for grant_admin (4323199171790522574) -->
- <skip />
- <!-- no translation found for not_grant_admin (3557849576157702485) -->
- <skip />
+ <string name="grant_admin" msgid="4323199171790522574">"Taip, nustatyti kaip administratorių"</string>
+ <string name="not_grant_admin" msgid="3557849576157702485">"Ne, nenustatyti kaip administratoriaus"</string>
<string name="guest_exit_dialog_button" msgid="1736401897067442044">"Išeiti"</string>
<string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Išsaugoti svečio veiklą?"</string>
<string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Galite išsaugoti esamos sesijos veiklą arba ištrinti visas programas ir duomenis"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 7cf8968..052840d 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -519,12 +519,9 @@
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"增加时间。"</string>
<string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"减少时间。"</string>
<string name="cancel" msgid="5665114069455378395">"取消"</string>
- <!-- no translation found for next (2699398661093607009) -->
- <skip />
- <!-- no translation found for back (5554327870352703710) -->
- <skip />
- <!-- no translation found for save (3745809743277153149) -->
- <skip />
+ <string name="next" msgid="2699398661093607009">"继续"</string>
+ <string name="back" msgid="5554327870352703710">"返回"</string>
+ <string name="save" msgid="3745809743277153149">"保存"</string>
<string name="okay" msgid="949938843324579502">"确定"</string>
<string name="done" msgid="381184316122520313">"完成"</string>
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"闹钟和提醒"</string>
@@ -579,12 +576,9 @@
<string name="user_add_user_title" msgid="5457079143694924885">"要添加新用户吗?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"创建新用户后,您就能够与其他人共用此设备。每位用户都有自己的专属空间,而且在自己的个人空间内还可以自行安装自己想要的应用、设置壁纸等。此外,用户还可以调整会影响所有用户的设备设置(例如 WLAN 设置)。\n\n当您添加新用户后,该用户需要自行设置个人空间。\n\n任何用户都可以为所有其他用户更新应用。无障碍功能设置和服务可能无法转移给新用户。"</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"当您添加新用户后,该用户需要自行设置个人空间。\n\n任何用户都可以为所有其他用户更新应用。"</string>
- <!-- no translation found for user_grant_admin_title (5157031020083343984) -->
- <skip />
- <!-- no translation found for user_grant_admin_message (1673791931033486709) -->
- <skip />
- <!-- no translation found for user_grant_admin_button (5441486731331725756) -->
- <skip />
+ <string name="user_grant_admin_title" msgid="5157031020083343984">"将此用户设为管理员?"</string>
+ <string name="user_grant_admin_message" msgid="1673791931033486709">"管理员拥有其他用户没有的特殊权限。管理员可以管理所有用户、更新或重置此设备、修改设置、查看所有已安装的应用,以及授予或撤消其他用户的管理员权限。"</string>
+ <string name="user_grant_admin_button" msgid="5441486731331725756">"设为管理员"</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"要现在设置该用户吗?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"请让相应用户操作设备并设置他们自己的空间。"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"要立即设置个人资料吗?"</string>
@@ -616,10 +610,8 @@
<string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"此操作会开始新的访客会话,并删除当前会话中的所有应用和数据"</string>
<string name="guest_exit_dialog_title" msgid="1846494656849381804">"要退出访客模式吗?"</string>
<string name="guest_exit_dialog_message" msgid="1743218864242719783">"此操作会删除当前访客会话中的所有应用和数据"</string>
- <!-- no translation found for grant_admin (4323199171790522574) -->
- <skip />
- <!-- no translation found for not_grant_admin (3557849576157702485) -->
- <skip />
+ <string name="grant_admin" msgid="4323199171790522574">"是,将其设为管理员"</string>
+ <string name="not_grant_admin" msgid="3557849576157702485">"不,不要将其设为管理员"</string>
<string name="guest_exit_dialog_button" msgid="1736401897067442044">"退出"</string>
<string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"要保存访客活动记录吗?"</string>
<string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"您可以保存当前会话中的活动记录,也可以删除所有应用和数据"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
index 09abc39..9ee8a32 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
@@ -209,8 +209,7 @@
}
final ComponentName cn = intent.getComponent();
final String key = cn != null ? cn.flattenToString() : intent.getAction();
- return logSettingsTileClick(key + (isWorkProfile ? "/work" : "/personal"),
- sourceMetricsCategory);
+ return logSettingsTileClickWithProfile(key, sourceMetricsCategory, isWorkProfile);
}
/**
@@ -226,4 +225,20 @@
clicked(sourceMetricsCategory, logKey);
return true;
}
+
+ /**
+ * Logs an event when the setting key is clicked with a specific profile from Profile select
+ * dialog.
+ *
+ * @return true if the key is loggable, otherwise false
+ */
+ public boolean logSettingsTileClickWithProfile(String logKey, int sourceMetricsCategory,
+ boolean isWorkProfile) {
+ if (TextUtils.isEmpty(logKey)) {
+ // Not loggable
+ return false;
+ }
+ clicked(sourceMetricsCategory, logKey + (isWorkProfile ? "/work" : "/personal"));
+ return true;
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index 3352d86..dd8d54a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -203,4 +203,24 @@
assertThat(loggable).isFalse();
verifyNoMoreInteractions(mLogWriter);
}
+
+ @Test
+ public void logSettingsTileClickWithProfile_isPersonalProfile_shouldTagPersonal() {
+ final String key = "abc";
+ final boolean loggable = mProvider.logSettingsTileClickWithProfile(key,
+ MetricsEvent.SETTINGS_GESTURES, false);
+
+ assertThat(loggable).isTrue();
+ verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, "abc/personal");
+ }
+
+ @Test
+ public void logSettingsTileClickWithProfile_isWorkProfile_shouldTagWork() {
+ final String key = "abc";
+ final boolean loggable = mProvider.logSettingsTileClickWithProfile(key,
+ MetricsEvent.SETTINGS_GESTURES, true);
+
+ assertThat(loggable).isTrue();
+ verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, "abc/work");
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
index aa6b0bf..4d2b1ae 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
@@ -17,19 +17,23 @@
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
import static com.google.common.truth.Truth.assertThat;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
+import android.os.UserHandle;
import org.junit.Before;
import org.junit.Test;
@@ -191,4 +195,65 @@
assertThat(tile.getTitle(RuntimeEnvironment.application)).isNull();
}
+
+ @Test
+ public void hasPendingIntent_empty_returnsFalse() {
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.hasPendingIntent()).isFalse();
+ }
+
+ @Test
+ public void hasPendingIntent_notEmpty_returnsTrue() {
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+ tile.pendingIntentMap.put(
+ UserHandle.CURRENT, PendingIntent.getActivity(mContext, 0, new Intent(), 0));
+
+ assertThat(tile.hasPendingIntent()).isTrue();
+ }
+
+ @Test
+ public void hasGroupKey_empty_returnsFalse() {
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.hasGroupKey()).isFalse();
+ }
+
+ @Test
+ public void hasGroupKey_notEmpty_returnsTrue() {
+ mActivityInfo.metaData.putString(META_DATA_PREFERENCE_GROUP_KEY, "test_key");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.hasGroupKey()).isTrue();
+ }
+
+ @Test
+ public void getGroupKey_empty_returnsNull() {
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.getGroupKey()).isNull();
+ }
+
+ @Test
+ public void getGroupKey_notEmpty_returnsValue() {
+ mActivityInfo.metaData.putString(META_DATA_PREFERENCE_GROUP_KEY, "test_key");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.getGroupKey()).isEqualTo("test_key");
+ }
+
+ @Test
+ public void getType_withoutSwitch_returnsAction() {
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.getType()).isEqualTo(Tile.Type.ACTION);
+ }
+
+ @Test
+ public void getType_withSwitch_returnsSwitchWithAction() {
+ mActivityInfo.metaData.putString(META_DATA_PREFERENCE_SWITCH_URI, "test://testabc/");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.getType()).isEqualTo(Tile.Type.SWITCH_WITH_ACTION);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/EntriesProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/EntriesProviderTest.java
new file mode 100644
index 0000000..a248330
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/EntriesProviderTest.java
@@ -0,0 +1,472 @@
+/*
+ * 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.settingslib.drawer;
+
+import static com.android.settingslib.drawer.EntriesProvider.EXTRA_ENTRY_DATA;
+import static com.android.settingslib.drawer.EntriesProvider.EXTRA_SWITCH_CHECKED_STATE;
+import static com.android.settingslib.drawer.EntriesProvider.EXTRA_SWITCH_DATA;
+import static com.android.settingslib.drawer.EntriesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR;
+import static com.android.settingslib.drawer.EntriesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE;
+import static com.android.settingslib.drawer.EntriesProvider.METHOD_GET_DYNAMIC_SUMMARY;
+import static com.android.settingslib.drawer.EntriesProvider.METHOD_GET_DYNAMIC_TITLE;
+import static com.android.settingslib.drawer.EntriesProvider.METHOD_GET_ENTRY_DATA;
+import static com.android.settingslib.drawer.EntriesProvider.METHOD_GET_PROVIDER_ICON;
+import static com.android.settingslib.drawer.EntriesProvider.METHOD_GET_SWITCH_DATA;
+import static com.android.settingslib.drawer.EntriesProvider.METHOD_IS_CHECKED;
+import static com.android.settingslib.drawer.EntriesProvider.METHOD_ON_CHECKED_CHANGED;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_PENDING_INTENT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.os.Bundle;
+
+import com.android.settingslib.drawer.EntryController.MetaData;
+import com.android.settingslib.drawer.PrimarySwitchControllerTest.TestPrimarySwitchController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class EntriesProviderTest {
+
+ @Rule
+ public final ExpectedException thrown = ExpectedException.none();
+
+ private Context mContext;
+ private ProviderInfo mProviderInfo;
+
+ private TestEntriesProvider mEntriesProvider;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mEntriesProvider = new TestEntriesProvider();
+ mProviderInfo = new ProviderInfo();
+ mProviderInfo.authority = "auth";
+ }
+
+ @Test
+ public void attachInfo_noController_shouldThrowIllegalArgumentException() {
+ thrown.expect(IllegalArgumentException.class);
+
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+ }
+
+ @Test
+ public void attachInfo_NoKeyInController_shouldThrowNullPointerException() {
+ thrown.expect(NullPointerException.class);
+ final TestEntryController controller = new TestEntryController();
+ mEntriesProvider.addController(controller);
+
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+ }
+
+ @Test
+ public void attachInfo_NoMetaDataInController_shouldThrowNullPointerException() {
+ thrown.expect(NullPointerException.class);
+ final TestEntryController controller = new TestEntryController();
+ controller.setKey("123");
+ mEntriesProvider.addController(controller);
+
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+ }
+
+ @Test
+ public void attachInfo_duplicateKey_shouldThrowIllegalArgumentException() {
+ thrown.expect(IllegalArgumentException.class);
+ final TestEntryController controller1 = new TestEntryController();
+ final TestEntryController controller2 = new TestEntryController();
+ controller1.setKey("123");
+ controller2.setKey("123");
+ controller1.setMetaData(new MetaData("category"));
+ controller2.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller1);
+ mEntriesProvider.addController(controller2);
+
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+ }
+
+ @Test
+ public void attachInfo_hasDifferentControllers_shouldNotThrowException() {
+ final TestEntryController controller1 = new TestEntryController();
+ final TestEntryController controller2 = new TestEntryController();
+ controller1.setKey("123");
+ controller2.setKey("456");
+ controller1.setMetaData(new MetaData("category"));
+ controller2.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller1);
+ mEntriesProvider.addController(controller2);
+
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+ }
+
+ @Test
+ public void getEntryData_shouldNotReturnPrimarySwitchData() {
+ final EntryController controller = new TestPrimarySwitchController("123");
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle switchData = mEntriesProvider.call(METHOD_GET_ENTRY_DATA, "uri",
+ null /* extras*/);
+
+ final ArrayList<Bundle> dataList = switchData.getParcelableArrayList(EXTRA_ENTRY_DATA);
+ assertThat(dataList).isEmpty();
+ }
+
+ @Test
+ public void getEntryData_shouldReturnDataList() {
+ final TestEntryController controller = new TestEntryController();
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category").setPendingIntent(pendingIntent));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle entryData = mEntriesProvider.call(METHOD_GET_ENTRY_DATA, "uri",
+ null /* extras*/);
+
+ final ArrayList<Bundle> dataList = entryData.getParcelableArrayList(EXTRA_ENTRY_DATA);
+ assertThat(dataList).hasSize(1);
+ assertThat(dataList.get(0).getString(META_DATA_PREFERENCE_KEYHINT)).isEqualTo("123");
+ assertThat(dataList.get(0).getParcelable(META_DATA_PREFERENCE_PENDING_INTENT,
+ PendingIntent.class))
+ .isEqualTo(pendingIntent);
+ }
+
+ @Test
+ public void getSwitchData_shouldReturnDataList() {
+ final TestEntryController controller = new TestEntryController();
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category").setPendingIntent(pendingIntent));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle entryData = mEntriesProvider.call(METHOD_GET_SWITCH_DATA, "uri",
+ null /* extras*/);
+
+ final ArrayList<Bundle> dataList = entryData.getParcelableArrayList(EXTRA_SWITCH_DATA);
+ assertThat(dataList).hasSize(1);
+ assertThat(dataList.get(0).getString(META_DATA_PREFERENCE_KEYHINT)).isEqualTo("123");
+ assertThat(dataList.get(0).getParcelable(META_DATA_PREFERENCE_PENDING_INTENT,
+ PendingIntent.class))
+ .isEqualTo(pendingIntent);
+ }
+
+ @Test
+ public void getEntryDataByKey_shouldReturnData() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestEntryController controller = new TestEntryController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle entryData = mEntriesProvider.call(METHOD_GET_ENTRY_DATA, "uri", extras);
+
+ assertThat(entryData.getString(META_DATA_PREFERENCE_KEYHINT)).isEqualTo("123");
+ }
+
+ @Test
+ public void getSwitchDataByKey_shouldReturnData() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestEntryController controller = new TestEntryController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle entryData = mEntriesProvider.call(METHOD_GET_SWITCH_DATA, "uri", extras);
+
+ assertThat(entryData.getString(META_DATA_PREFERENCE_KEYHINT)).isEqualTo("123");
+ }
+
+ @Test
+ public void isSwitchChecked_shouldReturnCheckedState() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestSwitchController controller = new TestSwitchController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ controller.setSwitchChecked(true);
+ Bundle result = mEntriesProvider.call(METHOD_IS_CHECKED, "uri", extras);
+
+ assertThat(result.getBoolean(EXTRA_SWITCH_CHECKED_STATE)).isTrue();
+
+ controller.setSwitchChecked(false);
+ result = mEntriesProvider.call(METHOD_IS_CHECKED, "uri", extras);
+
+ assertThat(result.getBoolean(EXTRA_SWITCH_CHECKED_STATE)).isFalse();
+ }
+
+ @Test
+ public void getProviderIcon_noImplementInterface_shouldReturnNull() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestEntryController controller = new TestEntryController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle iconBundle = mEntriesProvider.call(METHOD_GET_PROVIDER_ICON, "uri", extras);
+
+ assertThat(iconBundle).isNull();
+ }
+
+ @Test
+ public void getProviderIcon_implementInterface_shouldReturnIcon() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestEntryController controller = new TestDynamicController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle iconBundle = mEntriesProvider.call(METHOD_GET_PROVIDER_ICON, "uri", extras);
+
+ assertThat(iconBundle).isEqualTo(TestDynamicController.ICON_BUNDLE);
+ }
+
+ @Test
+ public void getDynamicTitle_noImplementInterface_shouldReturnNull() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestEntryController controller = new TestEntryController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle result = mEntriesProvider.call(METHOD_GET_DYNAMIC_TITLE, "uri", extras);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void getDynamicTitle_implementInterface_shouldReturnTitle() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestEntryController controller = new TestDynamicController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle result = mEntriesProvider.call(METHOD_GET_DYNAMIC_TITLE, "uri", extras);
+
+ assertThat(result.getString(META_DATA_PREFERENCE_TITLE))
+ .isEqualTo(TestDynamicController.TITLE);
+ }
+
+ @Test
+ public void getDynamicSummary_noImplementInterface_shouldReturnNull() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestEntryController controller = new TestEntryController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle result = mEntriesProvider.call(METHOD_GET_DYNAMIC_SUMMARY, "uri", extras);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void getDynamicSummary_implementInterface_shouldReturnSummary() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestEntryController controller = new TestDynamicController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle result = mEntriesProvider.call(METHOD_GET_DYNAMIC_SUMMARY, "uri", extras);
+
+ assertThat(result.getString(META_DATA_PREFERENCE_SUMMARY))
+ .isEqualTo(TestDynamicController.SUMMARY);
+ }
+
+ @Test
+ public void onSwitchCheckedChangedSuccess_shouldReturnNoError() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestSwitchController controller = new TestSwitchController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle result = mEntriesProvider.call(METHOD_ON_CHECKED_CHANGED, "uri", extras);
+
+ assertThat(result.getBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR)).isFalse();
+ }
+
+ @Test
+ public void onSwitchCheckedChangedFailed_shouldReturnErrorMessage() {
+ final Bundle extras = new Bundle();
+ extras.putString(META_DATA_PREFERENCE_KEYHINT, "123");
+ final TestSwitchController controller = new TestSwitchController();
+ controller.setKey("123");
+ controller.setMetaData(new MetaData("category"));
+ controller.setSwitchErrorMessage("error");
+ mEntriesProvider.addController(controller);
+ mEntriesProvider.attachInfo(mContext, mProviderInfo);
+
+ final Bundle result = mEntriesProvider.call(METHOD_ON_CHECKED_CHANGED, "uri", extras);
+
+ assertThat(result.getBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR)).isTrue();
+ assertThat(result.getString(EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE)).isEqualTo("error");
+ }
+
+ private static class TestEntriesProvider extends EntriesProvider {
+
+ private List<EntryController> mControllers;
+
+ @Override
+ protected List<EntryController> createEntryControllers() {
+ return mControllers;
+ }
+
+ void addController(EntryController controller) {
+ if (mControllers == null) {
+ mControllers = new ArrayList<>();
+ }
+ mControllers.add(controller);
+ }
+ }
+
+ private static class TestEntryController extends EntryController {
+
+ private String mKey;
+ private MetaData mMetaData;
+
+ @Override
+ public String getKey() {
+ return mKey;
+ }
+
+ @Override
+ protected MetaData getMetaData() {
+ return mMetaData;
+ }
+
+ void setKey(String key) {
+ mKey = key;
+ }
+
+ void setMetaData(MetaData metaData) {
+ mMetaData = metaData;
+ }
+ }
+
+ private static class TestSwitchController extends EntryController implements ProviderSwitch {
+
+ private String mKey;
+ private MetaData mMetaData;
+ private boolean mChecked;
+ private String mErrorMsg;
+
+ @Override
+ public String getKey() {
+ return mKey;
+ }
+
+ @Override
+ protected MetaData getMetaData() {
+ return mMetaData;
+ }
+
+ @Override
+ public boolean isSwitchChecked() {
+ return mChecked;
+ }
+
+ @Override
+ public boolean onSwitchCheckedChanged(boolean checked) {
+ return mErrorMsg == null ? true : false;
+ }
+
+ @Override
+ public String getSwitchErrorMessage(boolean attemptedChecked) {
+ return mErrorMsg;
+ }
+
+ void setKey(String key) {
+ mKey = key;
+ }
+
+ void setMetaData(MetaData metaData) {
+ mMetaData = metaData;
+ }
+
+ void setSwitchChecked(boolean checked) {
+ mChecked = checked;
+ }
+
+ void setSwitchErrorMessage(String errorMsg) {
+ mErrorMsg = errorMsg;
+ }
+ }
+
+ private static class TestDynamicController extends TestEntryController
+ implements ProviderIcon, DynamicTitle, DynamicSummary {
+
+ static final String TITLE = "title";
+ static final String SUMMARY = "summary";
+ static final Bundle ICON_BUNDLE = new Bundle();
+
+ @Override
+ public Bundle getProviderIcon() {
+ return ICON_BUNDLE;
+ }
+
+ @Override
+ public String getDynamicTitle() {
+ return TITLE;
+ }
+
+ @Override
+ public String getDynamicSummary() {
+ return SUMMARY;
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java
index abfb407..80f9efb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java
@@ -17,20 +17,24 @@
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
import static com.google.common.truth.Truth.assertThat;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
+import android.os.UserHandle;
import org.junit.Before;
import org.junit.Rule;
@@ -173,13 +177,93 @@
assertThat(tile.mLastUpdateTime).isNotEqualTo(staleTimeStamp);
}
+ @Test
+ public void hasPendingIntent_empty_returnsFalse() {
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.hasPendingIntent()).isFalse();
+ }
+
+ @Test
+ public void hasPendingIntent_notEmpty_returnsTrue() {
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+ tile.pendingIntentMap.put(
+ UserHandle.CURRENT, PendingIntent.getActivity(mContext, 0, new Intent(), 0));
+
+ assertThat(tile.hasPendingIntent()).isTrue();
+ }
+
+ @Test
+ public void hasGroupKey_empty_returnsFalse() {
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.hasGroupKey()).isFalse();
+ }
+
+ @Test
+ public void hasGroupKey_notEmpty_returnsTrue() {
+ mMetaData.putString(META_DATA_PREFERENCE_GROUP_KEY, "test_key");
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.hasGroupKey()).isTrue();
+ }
+
+ @Test
+ public void getGroupKey_empty_returnsNull() {
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.getGroupKey()).isNull();
+ }
+
+ @Test
+ public void getGroupKey_notEmpty_returnsValue() {
+ mMetaData.putString(META_DATA_PREFERENCE_GROUP_KEY, "test_key");
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.getGroupKey()).isEqualTo("test_key");
+ }
+
+ @Test
+ public void getType_withSwitch_returnsSwitch() {
+ mMetaData.putString(META_DATA_PREFERENCE_SWITCH_URI, "test://testabc/");
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.getType()).isEqualTo(Tile.Type.SWITCH);
+ }
+
+ @Test
+ public void getType_withSwitchAndPendingIntent_returnsSwitchWithAction() {
+ mMetaData.putString(META_DATA_PREFERENCE_SWITCH_URI, "test://testabc/");
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+ tile.pendingIntentMap.put(
+ UserHandle.CURRENT, PendingIntent.getActivity(mContext, 0, new Intent(), 0));
+
+ assertThat(tile.getType()).isEqualTo(Tile.Type.SWITCH_WITH_ACTION);
+ }
+
+ @Test
+ public void getType_withPendingIntent_returnsExternalAction() {
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+ tile.pendingIntentMap.put(
+ UserHandle.CURRENT, PendingIntent.getActivity(mContext, 0, new Intent(), 0));
+
+ assertThat(tile.getType()).isEqualTo(Tile.Type.EXTERNAL_ACTION);
+ }
+
+ @Test
+ public void getType_withoutSwitchAndPendingIntent_returnsGroup() {
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.getType()).isEqualTo(Tile.Type.GROUP);
+ }
+
@Implements(TileUtils.class)
private static class ShadowTileUtils {
private static Bundle sMetaData;
@Implementation
- protected static Bundle getSwitchDataFromProvider(Context context, String authority,
+ protected static Bundle getEntryDataFromProvider(Context context, String authority,
String key) {
return sMetaData;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 906e06e..2086466 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -21,6 +21,7 @@
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_PENDING_INTENT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
@@ -40,6 +41,7 @@
import static org.robolectric.RuntimeEnvironment.application;
import android.app.ActivityManager;
+import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -350,6 +352,53 @@
assertThat(outTiles).isEmpty();
}
+ @Test
+ public void loadTilesForAction_multipleUserProfiles_updatesUserHandle() {
+ Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
+ List<Tile> outTiles = new ArrayList<>();
+ List<ResolveInfo> info = new ArrayList<>();
+ ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
+ URI_GET_SUMMARY, null, 123, PROFILE_ALL);
+ info.add(resolveInfo);
+
+ when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
+ .thenReturn(info);
+
+ TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION,
+ addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
+ TileUtils.loadTilesForAction(mContext, new UserHandle(10), IA_SETTINGS_ACTION,
+ addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
+
+ assertThat(outTiles).hasSize(1);
+ assertThat(outTiles.get(0).userHandle)
+ .containsExactly(UserHandle.CURRENT, new UserHandle(10));
+ }
+
+ @Test
+ public void loadTilesForAction_withPendingIntent_updatesPendingIntentMap() {
+ Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
+ List<Tile> outTiles = new ArrayList<>();
+ List<ResolveInfo> info = new ArrayList<>();
+ ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
+ URI_GET_SUMMARY, null, 123, PROFILE_ALL);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ resolveInfo.activityInfo.metaData
+ .putParcelable(META_DATA_PREFERENCE_PENDING_INTENT, pendingIntent);
+ info.add(resolveInfo);
+
+ when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt()))
+ .thenReturn(info);
+
+ TileUtils.loadTilesForAction(mContext, UserHandle.CURRENT, IA_SETTINGS_ACTION,
+ addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
+ TileUtils.loadTilesForAction(mContext, new UserHandle(10), IA_SETTINGS_ACTION,
+ addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */);
+
+ assertThat(outTiles).hasSize(1);
+ assertThat(outTiles.get(0).pendingIntentMap).containsExactly(
+ UserHandle.CURRENT, pendingIntent, new UserHandle(10), pendingIntent);
+ }
+
public static ResolveInfo newInfo(boolean systemApp, String category) {
return newInfo(systemApp, category, null);
}
@@ -424,7 +473,7 @@
private static Bundle sMetaData;
@Implementation
- protected static List<Bundle> getSwitchDataFromProvider(Context context, String authority) {
+ protected static List<Bundle> getEntryDataFromProvider(Context context, String authority) {
return Arrays.asList(sMetaData);
}
diff --git a/packages/SystemUI/docs/device-entry/keyguard.md b/packages/SystemUI/docs/device-entry/keyguard.md
index 1898b97..8634c95 100644
--- a/packages/SystemUI/docs/device-entry/keyguard.md
+++ b/packages/SystemUI/docs/device-entry/keyguard.md
@@ -20,10 +20,6 @@
An indication to power off the device most likely comes from one of two signals: the user presses the power button or the screen timeout has passed. This may [lock the device](#How-the-device-locks)
-#### Long-pressing on keyguard
-
-OEMs may choose to enable a long-press action that displays a button at the bottom of lockscreen. This button links to lockscreen customization. This can be achieved by overriding the `long_press_keyguard_customize_lockscreen_enabled` resource in `packages/SystemUI/res/values/config.xml`.
-
#### On Lockscreen
#### On Lockscreen, occluded by an activity
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
index afcf846..d662649 100644
--- a/packages/SystemUI/docs/device-entry/quickaffordance.md
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -17,9 +17,7 @@
By default, AOSP ships with a "bottom right" and a "bottom left" slot, each with a slot capacity of `1`, allowing only one Quick Affordance on each side of the lock screen.
### Customizing Slots
-OEMs may choose to enable customization of slots. An entry point in settings will appear when overriding the `custom_lockscreen_shortcuts_enabled` resource in `packages/SystemUI/res/values/config.xml`.
-
-OEMs may also choose to override the IDs and number of slots and/or override the default capacities. This can be achieved by overridding the `config_keyguardQuickAffordanceSlots` resource in `packages/SystemUI/res/values/config.xml`.
+OEMs may choose to override the IDs and number of slots and/or override the default capacities. This can be achieved by overridding the `config_keyguardQuickAffordanceSlots` resource in `packages/SystemUI/res/values/config.xml`.
### Default Quick Affordances
OEMs may also choose to predefine default Quick Affordances for each slot. To achieve this, a developer may override the `config_keyguardQuickAffordanceDefaults` resource in `packages/SystemUI/res/values/config.xml`. Note that defaults only work until the user of the device selects a different quick affordance for that slot, even if they select the "None" option.
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c3651cf..b4e1b66 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -44,12 +44,6 @@
<!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
<integer name="navigation_bar_deadzone_orientation">0</integer>
- <!-- Whether or not lockscreen shortcuts can be customized -->
- <bool name="custom_lockscreen_shortcuts_enabled">false</bool>
-
- <!-- Whether or not long-pressing on keyguard will display to customize lockscreen -->
- <bool name="long_press_keyguard_customize_lockscreen_enabled">false</bool>
-
<bool name="config_dead_zone_flash">false</bool>
<!-- Whether to enable dimming navigation buttons when wallpaper is not visible, should be
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 271fab1..a62dead 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1730,15 +1730,15 @@
<!-- Keyboard backlight indicator-->
<dimen name="backlight_indicator_root_corner_radius">48dp</dimen>
<dimen name="backlight_indicator_root_vertical_padding">8dp</dimen>
- <dimen name="backlight_indicator_root_horizontal_padding">4dp</dimen>
+ <dimen name="backlight_indicator_root_horizontal_padding">6dp</dimen>
<dimen name="backlight_indicator_icon_width">22dp</dimen>
<dimen name="backlight_indicator_icon_height">11dp</dimen>
- <dimen name="backlight_indicator_icon_left_margin">2dp</dimen>
+ <dimen name="backlight_indicator_icon_padding">10dp</dimen>
<dimen name="backlight_indicator_step_width">52dp</dimen>
<dimen name="backlight_indicator_step_height">40dp</dimen>
- <dimen name="backlight_indicator_step_horizontal_margin">4dp</dimen>
+ <dimen name="backlight_indicator_step_horizontal_margin">2dp</dimen>
<dimen name="backlight_indicator_step_small_radius">4dp</dimen>
- <dimen name="backlight_indicator_step_large_radius">48dp</dimen>
+ <dimen name="backlight_indicator_step_large_radius">28dp</dimen>
<!-- Broadcast dialog -->
<dimen name="broadcast_dialog_title_img_margin_top">18dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index eaeaabe..e5c9461 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -203,5 +203,8 @@
<item type="id" name="log_access_dialog_allow_button" />
<item type="id" name="log_access_dialog_deny_button" />
+
+ <!-- keyboard backlight indicator-->
+ <item type="id" name="backlight_icon" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 003f9b0..511b1a3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2582,7 +2582,7 @@
<!-- Title for media controls [CHAR_LIMIT=50] -->
<string name="controls_media_title">Media</string>
<!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=50] -->
- <string name="controls_media_close_session">Hide this media control for <xliff:g id="app_name" example="Foo Music App">%1$s</xliff:g>?</string>
+ <string name="controls_media_close_session">Hide this media control for <xliff:g id="app_name" example="YouTube Music">%1$s</xliff:g>?</string>
<!-- Explanation that controls associated with a specific media session are active [CHAR_LIMIT=50] -->
<string name="controls_media_active_session">The current media session cannot be hidden.</string>
<!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
@@ -2595,8 +2595,6 @@
<string name="controls_media_playing_item_description"><xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> by <xliff:g id="artist_name" example="Various artists">%2$s</xliff:g> is playing from <xliff:g id="app_label" example="Spotify">%3$s</xliff:g></string>
<!-- Content description for media cotnrols progress bar [CHAR_LIMIT=NONE] -->
<string name="controls_media_seekbar_description"><xliff:g id="elapsed_time" example="1:30">%1$s</xliff:g> of <xliff:g id="total_time" example="3:00">%2$s</xliff:g></string>
- <!-- Placeholder title to inform user that an app has posted media controls [CHAR_LIMIT=NONE] -->
- <string name="controls_media_empty_title"><xliff:g id="app_name" example="Foo Music App">%1$s</xliff:g> is running</string>
<!-- Description for button in media controls. Pressing button starts playback [CHAR_LIMIT=NONE] -->
<string name="controls_media_button_play">Play</string>
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index a431a59..a90980f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -16,6 +16,7 @@
package com.android.systemui.dagger;
+import com.android.systemui.globalactions.ShutdownUiModule;
import com.android.systemui.keyguard.CustomizationProvider;
import com.android.systemui.statusbar.NotificationInsetsModule;
import com.android.systemui.statusbar.QsFrameTranslateModule;
@@ -31,6 +32,7 @@
DependencyProvider.class,
NotificationInsetsModule.class,
QsFrameTranslateModule.class,
+ ShutdownUiModule.class,
SystemUIBinder.class,
SystemUIModule.class,
SystemUICoreStartableModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index cf517eb..2fa1ef9 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -211,7 +211,7 @@
// TODO(b/267722622): Tracking Bug
@JvmField
val WALLPAPER_PICKER_UI_FOR_AIWP =
- unreleasedFlag(
+ releasedFlag(
229,
"wallpaper_picker_ui_for_aiwp"
)
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 290bf0d..c5027cc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -15,27 +15,12 @@
package com.android.systemui.globalactions;
import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import android.annotation.Nullable;
-import android.annotation.StringRes;
-import android.app.Dialog;
import android.content.Context;
-import android.os.PowerManager;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import com.android.internal.R;
-import com.android.settingslib.Utils;
import com.android.systemui.plugins.GlobalActions;
-import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -50,12 +35,14 @@
private final CommandQueue mCommandQueue;
private final GlobalActionsDialogLite mGlobalActionsDialog;
private boolean mDisabled;
+ private ShutdownUi mShutdownUi;
@Inject
public GlobalActionsImpl(Context context, CommandQueue commandQueue,
GlobalActionsDialogLite globalActionsDialog, BlurUtils blurUtils,
KeyguardStateController keyguardStateController,
- DeviceProvisionedController deviceProvisionedController) {
+ DeviceProvisionedController deviceProvisionedController,
+ ShutdownUi shutdownUi) {
mContext = context;
mGlobalActionsDialog = globalActionsDialog;
mKeyguardStateController = keyguardStateController;
@@ -63,6 +50,7 @@
mCommandQueue = commandQueue;
mBlurUtils = blurUtils;
mCommandQueue.addCallback(this);
+ mShutdownUi = shutdownUi;
}
@Override
@@ -80,103 +68,8 @@
@Override
public void showShutdownUi(boolean isReboot, String reason) {
- ScrimDrawable background = new ScrimDrawable();
-
- final Dialog d = new Dialog(mContext,
- com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
-
- d.setOnShowListener(dialog -> {
- if (mBlurUtils.supportsBlursOnWindows()) {
- int backgroundAlpha = (int) (ScrimController.BUSY_SCRIM_ALPHA * 255);
- background.setAlpha(backgroundAlpha);
- mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
- (int) mBlurUtils.blurRadiusOfRatio(1), backgroundAlpha == 255);
- } else {
- float backgroundAlpha = mContext.getResources().getFloat(
- com.android.systemui.R.dimen.shutdown_scrim_behind_alpha);
- background.setAlpha((int) (backgroundAlpha * 255));
- }
- });
-
- // Window initialization
- Window window = d.getWindow();
- window.requestFeature(Window.FEATURE_NO_TITLE);
- window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- // Inflate the decor view, so the attributes below are not overwritten by the theme.
- window.getDecorView();
- window.getAttributes().width = ViewGroup.LayoutParams.MATCH_PARENT;
- window.getAttributes().height = ViewGroup.LayoutParams.MATCH_PARENT;
- window.getAttributes().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- window.getAttributes().setFitInsetsTypes(0 /* types */);
- window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- window.addFlags(
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
- window.setBackgroundDrawable(background);
- window.setWindowAnimations(com.android.systemui.R.style.Animation_ShutdownUi);
-
- d.setContentView(R.layout.shutdown_dialog);
- d.setCancelable(false);
-
- int color;
- if (mBlurUtils.supportsBlursOnWindows()) {
- color = Utils.getColorAttrDefaultColor(mContext,
- com.android.systemui.R.attr.wallpaperTextColor);
- } else {
- color = mContext.getResources().getColor(
- com.android.systemui.R.color.global_actions_shutdown_ui_text);
- }
-
- ProgressBar bar = d.findViewById(R.id.progress);
- bar.getIndeterminateDrawable().setTint(color);
-
- TextView reasonView = d.findViewById(R.id.text1);
- TextView messageView = d.findViewById(R.id.text2);
-
- reasonView.setTextColor(color);
- messageView.setTextColor(color);
-
- messageView.setText(getRebootMessage(isReboot, reason));
- String rebootReasonMessage = getReasonMessage(reason);
- if (rebootReasonMessage != null) {
- reasonView.setVisibility(View.VISIBLE);
- reasonView.setText(rebootReasonMessage);
- }
-
- d.show();
+ mShutdownUi.showShutdownUi(isReboot, reason);
}
-
- @StringRes
- private int getRebootMessage(boolean isReboot, @Nullable String reason) {
- if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
- return R.string.reboot_to_update_reboot;
- } else if (reason != null && reason.equals(PowerManager.REBOOT_RECOVERY)) {
- return R.string.reboot_to_reset_message;
- } else if (isReboot) {
- return R.string.reboot_to_reset_message;
- } else {
- return R.string.shutdown_progress;
- }
- }
-
- @Nullable
- private String getReasonMessage(@Nullable String reason) {
- if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
- return mContext.getString(R.string.reboot_to_update_title);
- } else if (reason != null && reason.equals(PowerManager.REBOOT_RECOVERY)) {
- return mContext.getString(R.string.reboot_to_reset_title);
- } else {
- return null;
- }
- }
-
@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
new file mode 100644
index 0000000..8125d70
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java
@@ -0,0 +1,159 @@
+/*
+ * 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.globalactions;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.PowerManager;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.R;
+import com.android.settingslib.Utils;
+import com.android.systemui.scrim.ScrimDrawable;
+import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.statusbar.phone.ScrimController;
+
+/**
+ * Provides the UI shown during system shutdown.
+ */
+public class ShutdownUi {
+
+ private Context mContext;
+ private BlurUtils mBlurUtils;
+ public ShutdownUi(Context context, BlurUtils blurUtils) {
+ mContext = context;
+ mBlurUtils = blurUtils;
+ }
+
+ /**
+ * Display the shutdown UI.
+ * @param isReboot Whether the device will be rebooting after this shutdown.
+ * @param reason Cause for the shutdown.
+ */
+ public void showShutdownUi(boolean isReboot, String reason) {
+ ScrimDrawable background = new ScrimDrawable();
+
+ final Dialog d = new Dialog(mContext,
+ com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
+
+ d.setOnShowListener(dialog -> {
+ if (mBlurUtils.supportsBlursOnWindows()) {
+ int backgroundAlpha = (int) (ScrimController.BUSY_SCRIM_ALPHA * 255);
+ background.setAlpha(backgroundAlpha);
+ mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
+ (int) mBlurUtils.blurRadiusOfRatio(1), backgroundAlpha == 255);
+ } else {
+ float backgroundAlpha = mContext.getResources().getFloat(
+ com.android.systemui.R.dimen.shutdown_scrim_behind_alpha);
+ background.setAlpha((int) (backgroundAlpha * 255));
+ }
+ });
+
+ // Window initialization
+ Window window = d.getWindow();
+ window.requestFeature(Window.FEATURE_NO_TITLE);
+ window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ // Inflate the decor view, so the attributes below are not overwritten by the theme.
+ window.getDecorView();
+ window.getAttributes().width = ViewGroup.LayoutParams.MATCH_PARENT;
+ window.getAttributes().height = ViewGroup.LayoutParams.MATCH_PARENT;
+ window.getAttributes().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+ window.getAttributes().setFitInsetsTypes(0 /* types */);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ window.addFlags(
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ window.setBackgroundDrawable(background);
+ window.setWindowAnimations(com.android.systemui.R.style.Animation_ShutdownUi);
+
+ d.setContentView(getShutdownDialogContent());
+ d.setCancelable(false);
+
+ int color;
+ if (mBlurUtils.supportsBlursOnWindows()) {
+ color = Utils.getColorAttrDefaultColor(mContext,
+ com.android.systemui.R.attr.wallpaperTextColor);
+ } else {
+ color = mContext.getResources().getColor(
+ com.android.systemui.R.color.global_actions_shutdown_ui_text);
+ }
+
+ ProgressBar bar = d.findViewById(R.id.progress);
+ bar.getIndeterminateDrawable().setTint(color);
+
+ TextView reasonView = d.findViewById(R.id.text1);
+ TextView messageView = d.findViewById(R.id.text2);
+
+ reasonView.setTextColor(color);
+ messageView.setTextColor(color);
+
+ messageView.setText(getRebootMessage(isReboot, reason));
+ String rebootReasonMessage = getReasonMessage(reason);
+ if (rebootReasonMessage != null) {
+ reasonView.setVisibility(View.VISIBLE);
+ reasonView.setText(rebootReasonMessage);
+ }
+
+ d.show();
+ }
+
+ public int getShutdownDialogContent() {
+ return R.layout.shutdown_dialog;
+ }
+
+ @StringRes
+ @VisibleForTesting int getRebootMessage(boolean isReboot, @Nullable String reason) {
+ if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
+ return R.string.reboot_to_update_reboot;
+ } else if (reason != null && reason.equals(PowerManager.REBOOT_RECOVERY)) {
+ return R.string.reboot_to_reset_message;
+ } else if (isReboot) {
+ return R.string.reboot_to_reset_message;
+ } else {
+ return R.string.shutdown_progress;
+ }
+ }
+
+ @Nullable
+ @VisibleForTesting String getReasonMessage(@Nullable String reason) {
+ if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
+ return mContext.getString(R.string.reboot_to_update_title);
+ } else if (reason != null && reason.equals(PowerManager.REBOOT_RECOVERY)) {
+ return mContext.getString(R.string.reboot_to_reset_title);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUiModule.kt b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUiModule.kt
new file mode 100644
index 0000000..b7285da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUiModule.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.globalactions
+
+import android.content.Context
+import com.android.systemui.statusbar.BlurUtils
+import dagger.Module
+import dagger.Provides
+
+/** Provides the UI shown during system shutdown. */
+@Module
+class ShutdownUiModule {
+ /** Shutdown UI provider. */
+ @Provides
+ fun provideShutdownUi(context: Context?, blurUtils: BlurUtils?): ShutdownUi {
+ return ShutdownUi(context, blurUtils)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
index d3678b5..7078341 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
@@ -22,9 +22,12 @@
import android.app.Dialog
import android.content.Context
import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.OvalShape
import android.graphics.drawable.shapes.RoundRectShape
import android.os.Bundle
import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup.MarginLayoutParams
import android.view.Window
import android.view.WindowManager
import android.widget.FrameLayout
@@ -32,9 +35,10 @@
import android.widget.LinearLayout
import android.widget.LinearLayout.LayoutParams
import android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
+import androidx.annotation.IdRes
+import androidx.core.view.setPadding
import com.android.settingslib.Utils
import com.android.systemui.R
-import com.android.systemui.util.children
class KeyboardBacklightDialog(
context: Context,
@@ -51,7 +55,7 @@
private data class BacklightIconProperties(
val width: Int,
val height: Int,
- val leftMargin: Int,
+ val padding: Int,
)
private data class StepViewProperties(
@@ -71,6 +75,7 @@
private lateinit var rootProperties: RootProperties
private lateinit var iconProperties: BacklightIconProperties
private lateinit var stepProperties: StepViewProperties
+
@ColorInt
var filledRectangleColor = getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
@ColorInt
@@ -78,7 +83,16 @@
getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant)
@ColorInt
var backgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright)
- @ColorInt var iconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
+ @ColorInt
+ var defaultIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
+ @ColorInt
+ var defaultIconBackgroundColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
+ @ColorInt
+ var dimmedIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface)
+ @ColorInt
+ var dimmedIconBackgroundColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceDim)
init {
currentLevel = initialCurrentLevel
@@ -111,8 +125,7 @@
BacklightIconProperties(
width = getDimensionPixelSize(R.dimen.backlight_indicator_icon_width),
height = getDimensionPixelSize(R.dimen.backlight_indicator_icon_height),
- leftMargin =
- getDimensionPixelSize(R.dimen.backlight_indicator_icon_left_margin),
+ padding = getDimensionPixelSize(R.dimen.backlight_indicator_icon_padding),
)
stepProperties =
StepViewProperties(
@@ -139,23 +152,34 @@
if (maxLevel != max || forceRefresh) {
maxLevel = max
rootView.removeAllViews()
+ rootView.addView(buildIconTile())
buildStepViews().forEach { rootView.addView(it) }
}
currentLevel = current
- updateLevel()
+ updateIconTile()
+ updateStepColors()
}
- private fun updateLevel() {
- rootView.children.forEachIndexed(
- action = { index, v ->
- val drawable = v.background as ShapeDrawable
- if (index <= currentLevel) {
- updateColor(drawable, filledRectangleColor)
- } else {
- updateColor(drawable, emptyRectangleColor)
- }
- }
- )
+ private fun updateIconTile() {
+ val iconTile = rootView.findViewById(BACKLIGHT_ICON_ID) as ImageView
+ val backgroundDrawable = iconTile.background as ShapeDrawable
+ if (currentLevel == 0) {
+ iconTile.setColorFilter(dimmedIconColor)
+ updateColor(backgroundDrawable, dimmedIconBackgroundColor)
+ } else {
+ iconTile.setColorFilter(defaultIconColor)
+ updateColor(backgroundDrawable, defaultIconBackgroundColor)
+ }
+ }
+
+ private fun updateStepColors() {
+ (1 until rootView.childCount).forEach { index ->
+ val drawable = rootView.getChildAt(index).background as ShapeDrawable
+ updateColor(
+ drawable,
+ if (index <= currentLevel) filledRectangleColor else emptyRectangleColor,
+ )
+ }
}
private fun updateColor(drawable: ShapeDrawable, @ColorInt color: Int) {
@@ -192,9 +216,33 @@
}
private fun buildStepViews(): List<FrameLayout> {
- val stepViews = (0..maxLevel).map { i -> createStepViewAt(i) }
- stepViews[0].addView(createBacklightIconView())
- return stepViews
+ return (1..maxLevel).map { i -> createStepViewAt(i) }
+ }
+
+ private fun buildIconTile(): View {
+ val diameter = stepProperties.height
+ val circleDrawable =
+ ShapeDrawable(OvalShape()).apply {
+ intrinsicHeight = diameter
+ intrinsicWidth = diameter
+ }
+
+ return ImageView(context).apply {
+ setImageResource(R.drawable.ic_keyboard_backlight)
+ id = BACKLIGHT_ICON_ID
+ setColorFilter(defaultIconColor)
+ setPadding(iconProperties.padding)
+ layoutParams =
+ MarginLayoutParams(diameter, diameter).apply {
+ setMargins(
+ /* left= */ stepProperties.horizontalMargin,
+ /* top= */ 0,
+ /* right= */ stepProperties.horizontalMargin,
+ /* bottom= */ 0
+ )
+ }
+ background = circleDrawable
+ }
}
private fun createStepViewAt(i: Int): FrameLayout {
@@ -221,18 +269,6 @@
}
}
- private fun createBacklightIconView(): ImageView {
- return ImageView(context).apply {
- setImageResource(R.drawable.ic_keyboard_backlight)
- setColorFilter(iconColor)
- layoutParams =
- FrameLayout.LayoutParams(iconProperties.width, iconProperties.height).apply {
- gravity = Gravity.CENTER
- leftMargin = iconProperties.leftMargin
- }
- }
- }
-
private fun setWindowPosition() {
window?.apply {
setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
@@ -262,30 +298,29 @@
private fun radiiForIndex(i: Int, last: Int): FloatArray {
val smallRadius = stepProperties.smallRadius
val largeRadius = stepProperties.largeRadius
- return when (i) {
- 0 -> // left radii bigger
- floatArrayOf(
- largeRadius,
- largeRadius,
- smallRadius,
- smallRadius,
- smallRadius,
- smallRadius,
- largeRadius,
- largeRadius
- )
- last -> // right radii bigger
- floatArrayOf(
- smallRadius,
- smallRadius,
- largeRadius,
- largeRadius,
- largeRadius,
- largeRadius,
- smallRadius,
- smallRadius
- )
- else -> FloatArray(8) { smallRadius } // all radii equal
+ val radii = FloatArray(8) { smallRadius }
+ if (i == 1) {
+ radii.setLeftCorners(largeRadius)
}
+ // note "first" and "last" might be the same tile
+ if (i == last) {
+ radii.setRightCorners(largeRadius)
+ }
+ return radii
+ }
+
+ private fun FloatArray.setLeftCorners(radius: Float) {
+ LEFT_CORNERS_INDICES.forEach { this[it] = radius }
+ }
+ private fun FloatArray.setRightCorners(radius: Float) {
+ RIGHT_CORNERS_INDICES.forEach { this[it] = radius }
+ }
+
+ private companion object {
+ @IdRes val BACKLIGHT_ICON_ID = R.id.backlight_icon
+
+ // indices used to define corners radii in ShapeDrawable
+ val LEFT_CORNERS_INDICES: IntArray = intArrayOf(0, 1, 6, 7)
+ val RIGHT_CORNERS_INDICES: IntArray = intArrayOf(2, 3, 4, 5)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
index ca430da..ea6700e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
@@ -17,14 +17,12 @@
package com.android.systemui.keyguard.domain.interactor
-import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.view.accessibility.AccessibilityManager
import androidx.annotation.VisibleForTesting
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -57,7 +55,6 @@
class KeyguardLongPressInteractor
@Inject
constructor(
- @Application private val appContext: Context,
@Application private val scope: CoroutineScope,
transitionInteractor: KeyguardTransitionInteractor,
repository: KeyguardRepository,
@@ -172,8 +169,7 @@
private fun isFeatureEnabled(): Boolean {
return featureFlags.isEnabled(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED) &&
- featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI) &&
- appContext.resources.getBoolean(R.bool.long_press_keyguard_customize_lockscreen_enabled)
+ featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI)
}
/** Updates application state to ask to show the menu. */
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 2275337..8e65c4d 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
@@ -19,15 +19,12 @@
import android.app.AlertDialog
import android.app.admin.DevicePolicyManager
-import android.content.Context
import android.content.Intent
import android.util.Log
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
import com.android.systemui.dock.DockManager
@@ -78,7 +75,6 @@
private val devicePolicyManager: DevicePolicyManager,
private val dockManager: DockManager,
@Background private val backgroundDispatcher: CoroutineDispatcher,
- @Application private val appContext: Context,
) {
private val isUsingRepository: Boolean
get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
@@ -412,8 +408,7 @@
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
value =
!isFeatureDisabledByDevicePolicy() &&
- featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES) &&
- appContext.resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled),
+ featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
),
KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 1469d96..fa42114 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -43,7 +43,6 @@
import android.net.Uri
import android.os.Parcelable
import android.os.Process
-import android.os.RemoteException
import android.os.UserHandle
import android.provider.Settings
import android.service.notification.StatusBarNotification
@@ -53,7 +52,6 @@
import android.util.Pair as APair
import androidx.media.utils.MediaConstants
import com.android.internal.logging.InstanceId
-import com.android.internal.statusbar.IStatusBarService
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Dumpable
import com.android.systemui.R
@@ -139,8 +137,6 @@
expiryTimeMs = 0,
)
-const val MEDIA_TITLE_ERROR_MESSAGE = "Invalid media data: title is null or blank."
-
fun isMediaNotification(sbn: StatusBarNotification): Boolean {
return sbn.notification.isMediaNotification()
}
@@ -185,7 +181,6 @@
private val logger: MediaUiEventLogger,
private val smartspaceManager: SmartspaceManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val statusBarService: IStatusBarService,
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
companion object {
@@ -257,7 +252,6 @@
mediaFlags: MediaFlags,
logger: MediaUiEventLogger,
smartspaceManager: SmartspaceManager,
- statusBarService: IStatusBarService,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : this(
context,
@@ -283,7 +277,6 @@
logger,
smartspaceManager,
keyguardUpdateMonitor,
- statusBarService,
)
private val appChangeReceiver =
@@ -385,21 +378,21 @@
fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
if (useQsMediaPlayer && isMediaNotification(sbn)) {
- var isNewlyActiveEntry = false
+ var logEvent = false
Assert.isMainThread()
val oldKey = findExistingEntry(key, sbn.packageName)
if (oldKey == null) {
val instanceId = logger.getNewInstanceId()
val temp = LOADING.copy(packageName = sbn.packageName, instanceId = instanceId)
mediaEntries.put(key, temp)
- isNewlyActiveEntry = true
+ logEvent = true
} else if (oldKey != key) {
// Resume -> active conversion; move to new key
val oldData = mediaEntries.remove(oldKey)!!
- isNewlyActiveEntry = true
+ logEvent = true
mediaEntries.put(key, oldData)
}
- loadMediaData(key, sbn, oldKey, isNewlyActiveEntry)
+ loadMediaData(key, sbn, oldKey, logEvent)
} else {
onNotificationRemoved(key)
}
@@ -482,9 +475,9 @@
key: String,
sbn: StatusBarNotification,
oldKey: String?,
- isNewlyActiveEntry: Boolean = false,
+ logEvent: Boolean = false
) {
- backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) }
+ backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, logEvent) }
}
/** Add a listener for changes in this class */
@@ -608,11 +601,9 @@
}
}
- private fun removeEntry(key: String, logEvent: Boolean = true) {
+ private fun removeEntry(key: String) {
mediaEntries.remove(key)?.let {
- if (logEvent) {
- logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
- }
+ logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
}
notifyMediaDataRemoved(key)
}
@@ -760,7 +751,7 @@
key: String,
sbn: StatusBarNotification,
oldKey: String?,
- isNewlyActiveEntry: Boolean = false,
+ logEvent: Boolean = false
) {
val token =
sbn.notification.extras.getParcelable(
@@ -781,42 +772,6 @@
)
?: getAppInfoFromPackage(sbn.packageName)
- // App name
- val appName = getAppName(sbn, appInfo)
-
- // Song name
- var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
- if (song.isNullOrBlank()) {
- song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
- }
- if (song.isNullOrBlank()) {
- song = HybridGroupManager.resolveTitle(notif)
- }
- if (song.isNullOrBlank()) {
- if (mediaFlags.isMediaTitleRequired(sbn.packageName, sbn.user)) {
- // App is required to provide a title: cancel the underlying notification
- try {
- statusBarService.onNotificationError(
- sbn.packageName,
- sbn.tag,
- sbn.id,
- sbn.uid,
- sbn.initialPid,
- MEDIA_TITLE_ERROR_MESSAGE,
- sbn.user.identifier
- )
- } catch (e: RemoteException) {
- Log.e(TAG, "cancelNotification failed: $e")
- }
- // Only add log for media removed if active media is updated with invalid title.
- foregroundExecutor.execute { removeEntry(key, !isNewlyActiveEntry) }
- return
- } else {
- // For apps that don't have the title requirement yet, add a placeholder
- song = context.getString(R.string.controls_media_empty_title, appName)
- }
- }
-
// Album art
var artworkBitmap = metadata?.let { loadBitmapFromUri(it) }
if (artworkBitmap == null) {
@@ -832,9 +787,21 @@
Icon.createWithBitmap(artworkBitmap)
}
+ // App name
+ val appName = getAppName(sbn, appInfo)
+
// App Icon
val smallIcon = sbn.notification.smallIcon
+ // Song name
+ var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
+ if (song == null) {
+ song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
+ }
+ if (song == null) {
+ song = HybridGroupManager.resolveTitle(notif)
+ }
+
// Explicit Indicator
var isExplicit = false
if (mediaFlags.isExplicitIndicatorEnabled()) {
@@ -846,7 +813,7 @@
// Artist name
var artist: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST)
- if (artist.isNullOrBlank()) {
+ if (artist == null) {
artist = HybridGroupManager.resolveText(notif)
}
@@ -906,7 +873,7 @@
val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
val appUid = appInfo?.uid ?: Process.INVALID_UID
- if (isNewlyActiveEntry) {
+ if (logEvent) {
logSingleVsMultipleMediaAdded(appUid, sbn.packageName, instanceId)
logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation)
} else if (playbackLocation != currentEntry?.playbackLocation) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 3751c60..9bc66f6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -64,9 +64,4 @@
/** Check whether we allow remote media to generate resume controls */
fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME)
-
- /** Check whether app is required to provide a non-empty media title */
- fun isMediaTitleRequired(packageName: String, user: UserHandle): Boolean {
- return StatusBarManager.isMediaTitleRequiredForApp(packageName, user)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
index 640adcc..5e489b0 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
@@ -20,15 +20,14 @@
import com.android.systemui.dagger.DependencyProvider;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.SystemUIBinder;
import com.android.systemui.dagger.SystemUIModule;
+import com.android.systemui.globalactions.ShutdownUiModule;
+import com.android.systemui.keyguard.dagger.KeyguardModule;
+import com.android.systemui.recents.RecentsModule;
import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.row.NotificationRowModule;
-import com.android.systemui.keyguard.dagger.KeyguardModule;
-import com.android.systemui.recents.RecentsModule;
-
import dagger.Subcomponent;
/**
@@ -43,6 +42,7 @@
NotificationRowModule.class,
NotificationsModule.class,
RecentsModule.class,
+ ShutdownUiModule.class,
SystemUIModule.class,
TvSystemUIBinder.class,
TVSystemUICoreStartableModule.class,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
new file mode 100644
index 0000000..9d9b263
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.globalactions;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import android.os.PowerManager;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.BlurUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ShutdownUiTest extends SysuiTestCase {
+
+ ShutdownUi mShutdownUi;
+ @Mock
+ BlurUtils mBlurUtils;
+
+ @Before
+ public void setUp() throws Exception {
+ mShutdownUi = new ShutdownUi(getContext(), mBlurUtils);
+ }
+
+ @Test
+ public void getRebootMessage_update() {
+ int messageId = mShutdownUi.getRebootMessage(true, PowerManager.REBOOT_RECOVERY_UPDATE);
+ assertEquals(messageId, R.string.reboot_to_update_reboot);
+ }
+
+ @Test
+ public void getRebootMessage_rebootDefault() {
+ int messageId = mShutdownUi.getRebootMessage(true, "anything-else");
+ assertEquals(messageId, R.string.reboot_to_reset_message);
+ }
+
+ @Test
+ public void getRebootMessage_shutdown() {
+ int messageId = mShutdownUi.getRebootMessage(false, "anything-else");
+ assertEquals(messageId, R.string.shutdown_progress);
+ }
+
+ @Test
+ public void getReasonMessage_update() {
+ String message = mShutdownUi.getReasonMessage(PowerManager.REBOOT_RECOVERY_UPDATE);
+ assertEquals(message, mContext.getString(R.string.reboot_to_update_title));
+ }
+
+ @Test
+ public void getReasonMessage_rebootDefault() {
+ String message = mShutdownUi.getReasonMessage(PowerManager.REBOOT_RECOVERY);
+ assertEquals(message, mContext.getString(R.string.reboot_to_reset_title));
+ }
+
+ @Test
+ public void getRebootMessage_defaultToNone() {
+ String message = mShutdownUi.getReasonMessage("anything-else");
+ assertNull(message);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 8ee7d3e..9cf988e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -30,7 +30,6 @@
import android.view.SurfaceControlViewHost
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.R
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
@@ -68,7 +67,6 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
-import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -105,7 +103,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, true)
whenever(previewRenderer.surfacePackage).thenReturn(previewSurfacePackage)
whenever(previewRendererFactory.create(any())).thenReturn(previewRenderer)
whenever(backgroundHandler.looper).thenReturn(TestableLooper.get(this).looper)
@@ -198,7 +195,6 @@
devicePolicyManager = devicePolicyManager,
dockManager = dockManager,
backgroundDispatcher = testDispatcher,
- appContext = mContext,
)
underTest.previewManager =
KeyguardRemotePreviewManager(
@@ -220,13 +216,6 @@
)
}
- @After
- fun tearDown() {
- mContext
- .getOrCreateTestableResources()
- .removeOverride(R.bool.custom_lockscreen_shortcuts_enabled)
- }
-
@Test
fun onAttachInfo_reportsContext() {
val callback: SystemUIAppComponentFactoryBase.ContextAvailableCallback = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 5de24e4..dfef947 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -21,7 +21,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -40,7 +39,6 @@
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -67,7 +65,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, true)
whenever(accessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenAnswer {
it.arguments[0]
}
@@ -79,13 +76,6 @@
runBlocking { createUnderTest() }
}
- @After
- fun tearDown() {
- mContext
- .getOrCreateTestableResources()
- .removeOverride(R.bool.long_press_keyguard_customize_lockscreen_enabled)
- }
-
@Test
fun isEnabled() =
testScope.runTest {
@@ -118,17 +108,6 @@
}
@Test
- fun isEnabled_alwaysFalseWhenConfigEnabledBooleanIsFalse() =
- testScope.runTest {
- overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, false)
- createUnderTest()
- val isEnabled by collectLastValue(underTest.isLongPressHandlingEnabled)
- runCurrent()
-
- assertThat(isEnabled).isFalse()
- }
-
- @Test
fun longPressed_menuClicked_showsSettings() =
testScope.runTest {
val isMenuVisible by collectLastValue(underTest.isMenuVisible)
@@ -288,7 +267,6 @@
) {
underTest =
KeyguardLongPressInteractor(
- appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor =
KeyguardTransitionInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index fb21847..a75e11a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -340,7 +340,6 @@
devicePolicyManager = devicePolicyManager,
dockManager = dockManager,
backgroundDispatcher = testDispatcher,
- appContext = mContext,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 5d2c3ed..3336e3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -200,7 +200,6 @@
devicePolicyManager = devicePolicyManager,
dockManager = dockManager,
backgroundDispatcher = testDispatcher,
- appContext = mContext,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 8a36dbc..69d43af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -211,7 +211,6 @@
)
val keyguardLongPressInteractor =
KeyguardLongPressInteractor(
- appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor =
KeyguardTransitionInteractor(
@@ -241,7 +240,6 @@
devicePolicyManager = devicePolicyManager,
dockManager = dockManager,
backgroundDispatcher = testDispatcher,
- appContext = mContext,
),
bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
burnInHelperWrapper = burnInHelperWrapper,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 3bcefcf..d428db7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -25,7 +25,6 @@
import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceTarget
import android.content.Intent
-import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.drawable.Icon
import android.media.MediaDescription
@@ -41,7 +40,6 @@
import androidx.media.utils.MediaConstants
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
-import com.android.internal.statusbar.IStatusBarService
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.InstanceIdSequenceFake
import com.android.systemui.R
@@ -78,7 +76,6 @@
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@@ -133,7 +130,6 @@
@Mock lateinit var activityStarter: ActivityStarter
@Mock lateinit var smartspaceManager: SmartspaceManager
@Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock lateinit var statusBarService: IStatusBarService
lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
@Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
@Mock private lateinit var mediaRecommendationItem: SmartspaceAction
@@ -196,8 +192,7 @@
mediaFlags = mediaFlags,
logger = logger,
smartspaceManager = smartspaceManager,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
- statusBarService = statusBarService,
+ keyguardUpdateMonitor = keyguardUpdateMonitor
)
verify(tunerService)
.addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
@@ -522,253 +517,19 @@
}
@Test
- fun testOnNotificationAdded_emptyTitle_isRequired_notLoaded() {
- // When the manager has a notification with an empty title, and the app is required
- // to include a non-empty title
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(true)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- // Then the media control is not added and we report a notification error
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
- verify(logger, never()).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), any())
- }
-
- @Test
- fun testOnNotificationAdded_blankTitle_isRequired_notLoaded() {
- // When the manager has a notification with a blank title, and the app is required
- // to include a non-empty title
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(true)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- // Then the media control is not added and we report a notification error
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
- verify(logger, never()).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), any())
- }
-
- @Test
- fun testOnNotificationUpdated_invalidTitle_isRequired_logMediaRemoved() {
- // When the app is required to provide a non-blank title, and updates a previously valid
- // title to an empty one
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(true)
- addNotificationAndLoad()
- val data = mediaDataCaptor.value
-
- verify(listener)
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
-
- reset(listener)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- // Then the media control is removed
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
- }
-
- @Test
- fun testOnNotificationAdded_emptyTitle_notRequired_hasPlaceholder() {
- // When the manager has a notification with an empty title, and the app is not
- // required to include a non-empty title
- val mockPackageManager = mock(PackageManager::class.java)
- context.setMockPackageManager(mockPackageManager)
- whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(false)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- // Then a media control is created with a placeholder title string
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(listener)
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- val placeholderTitle = context.getString(R.string.controls_media_empty_title, APP_NAME)
- assertThat(mediaDataCaptor.value.song).isEqualTo(placeholderTitle)
- }
-
- @Test
- fun testOnNotificationAdded_blankTitle_notRequired_hasPlaceholder() {
- // GIVEN that the manager has a notification with a blank title, and the app is not
- // required to include a non-empty title
- val mockPackageManager = mock(PackageManager::class.java)
- context.setMockPackageManager(mockPackageManager)
- whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(false)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- // Then a media control is created with a placeholder title string
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(listener)
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- val placeholderTitle = context.getString(R.string.controls_media_empty_title, APP_NAME)
- assertThat(mediaDataCaptor.value.song).isEqualTo(placeholderTitle)
- }
-
- @Test
- fun testOnNotificationAdded_emptyMetadata_usesNotificationTitle() {
- // When the app sets the metadata title fields to empty strings, but does include a
- // non-blank notification title
- val mockPackageManager = mock(PackageManager::class.java)
- context.setMockPackageManager(mockPackageManager)
- whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
- whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(true)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
- .putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, SESSION_EMPTY_TITLE)
- .build()
- )
- mediaNotification =
- SbnBuilder().run {
- setPkg(PACKAGE_NAME)
- modifyNotification(context).also {
- it.setSmallIcon(android.R.drawable.ic_media_pause)
- it.setContentTitle(SESSION_TITLE)
- it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
- }
- build()
- }
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- // Then the media control is added using the notification's title
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(listener)
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- assertThat(mediaDataCaptor.value.song).isEqualTo(SESSION_TITLE)
- }
-
- @Test
fun testOnNotificationRemoved_emptyTitle_notConverted() {
// GIVEN that the manager has a notification with a resume action and empty title.
+ whenever(controller.metadata)
+ .thenReturn(
+ metadataBuilder
+ .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
+ .build()
+ )
addNotificationAndLoad()
val data = mediaDataCaptor.value
val instanceId = data.instanceId
assertThat(data.resumption).isFalse()
- mediaDataManager.onMediaDataLoaded(
- KEY,
- null,
- data.copy(song = SESSION_EMPTY_TITLE, resumeAction = Runnable {})
- )
+ mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the notification is removed
reset(listener)
@@ -793,15 +554,17 @@
@Test
fun testOnNotificationRemoved_blankTitle_notConverted() {
// GIVEN that the manager has a notification with a resume action and blank title.
+ whenever(controller.metadata)
+ .thenReturn(
+ metadataBuilder
+ .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
+ .build()
+ )
addNotificationAndLoad()
val data = mediaDataCaptor.value
val instanceId = data.instanceId
assertThat(data.resumption).isFalse()
- mediaDataManager.onMediaDataLoaded(
- KEY,
- null,
- data.copy(song = SESSION_BLANK_TITLE, resumeAction = Runnable {})
- )
+ mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the notification is removed
reset(listener)
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 778951a..98ee98b 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -248,7 +248,10 @@
Slog.e(TAG, "WM sent Transaction to organized, but never received" +
" commit callback. Application ANR likely to follow.");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- onCommitted(merged);
+ synchronized (mWm.mGlobalLock) {
+ onCommitted(merged.mNativeObject != 0
+ ? merged : mWm.mTransactionFactory.get());
+ }
}
};
CommitCallback callback = new CommitCallback();