diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 6833d59c18fa7..367d953fffa86 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2810,7 +2810,9 @@ package android.content.pm { method @NonNull public android.content.pm.SuspendDialogInfo.Builder setMessage(@StringRes int); method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonAction(int); method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(@StringRes int); + method @NonNull public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(@NonNull String); method @NonNull public android.content.pm.SuspendDialogInfo.Builder setTitle(@StringRes int); + method @NonNull public android.content.pm.SuspendDialogInfo.Builder setTitle(@NonNull String); } } diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java index 60f321883e985..23945eebf546a 100644 --- a/core/java/android/content/pm/SuspendDialogInfo.java +++ b/core/java/android/content/pm/SuspendDialogInfo.java @@ -65,16 +65,20 @@ public final class SuspendDialogInfo implements Parcelable { private static final String TAG = SuspendDialogInfo.class.getSimpleName(); private static final String XML_ATTR_ICON_RES_ID = "iconResId"; private static final String XML_ATTR_TITLE_RES_ID = "titleResId"; + private static final String XML_ATTR_TITLE = "title"; private static final String XML_ATTR_DIALOG_MESSAGE_RES_ID = "dialogMessageResId"; private static final String XML_ATTR_DIALOG_MESSAGE = "dialogMessage"; private static final String XML_ATTR_BUTTON_TEXT_RES_ID = "buttonTextResId"; + private static final String XML_ATTR_BUTTON_TEXT = "buttonText"; private static final String XML_ATTR_BUTTON_ACTION = "buttonAction"; private final int mIconResId; private final int mTitleResId; + private final String mTitle; private final int mDialogMessageResId; private final String mDialogMessage; private final int mNeutralButtonTextResId; + private final String mNeutralButtonText; private final int mNeutralButtonAction; /** @@ -128,6 +132,16 @@ public final class SuspendDialogInfo implements Parcelable { return mTitleResId; } + /** + * @return the title to be shown on the dialog. Returns {@code null} if {@link #getTitleResId()} + * returns a valid resource id + * @hide + */ + @Nullable + public String getTitle() { + return mTitle; + } + /** * @return the resource id of the text to be shown in the dialog's body * @hide @@ -148,7 +162,7 @@ public final class SuspendDialogInfo implements Parcelable { } /** - * @return the text to be shown + * @return the text to be shown on the neutral button * @hide */ @StringRes @@ -156,6 +170,16 @@ public final class SuspendDialogInfo implements Parcelable { return mNeutralButtonTextResId; } + /** + * @return the text to be shown on the neutral button. Returns {@code null} if + * {@link #getNeutralButtonTextResId()} returns a valid resource id + * @hide + */ + @Nullable + public String getNeutralButtonText() { + return mNeutralButtonText; + } + /** * @return The {@link ButtonAction} that happens on tapping this button * @hide @@ -174,6 +198,8 @@ public final class SuspendDialogInfo implements Parcelable { } if (mTitleResId != ID_NULL) { out.attributeInt(null, XML_ATTR_TITLE_RES_ID, mTitleResId); + } else { + XmlUtils.writeStringAttribute(out, XML_ATTR_TITLE, mTitle); } if (mDialogMessageResId != ID_NULL) { out.attributeInt(null, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId); @@ -182,6 +208,8 @@ public final class SuspendDialogInfo implements Parcelable { } if (mNeutralButtonTextResId != ID_NULL) { out.attributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId); + } else { + XmlUtils.writeStringAttribute(out, XML_ATTR_BUTTON_TEXT, mNeutralButtonText); } out.attributeInt(null, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction); } @@ -194,8 +222,10 @@ public final class SuspendDialogInfo implements Parcelable { try { final int iconId = in.getAttributeInt(null, XML_ATTR_ICON_RES_ID, ID_NULL); final int titleId = in.getAttributeInt(null, XML_ATTR_TITLE_RES_ID, ID_NULL); + final String title = XmlUtils.readStringAttribute(in, XML_ATTR_TITLE); final int buttonTextId = in.getAttributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, ID_NULL); + final String buttonText = XmlUtils.readStringAttribute(in, XML_ATTR_BUTTON_TEXT); final int buttonAction = in.getAttributeInt(null, XML_ATTR_BUTTON_ACTION, BUTTON_ACTION_MORE_DETAILS); final int dialogMessageResId = @@ -207,9 +237,13 @@ public final class SuspendDialogInfo implements Parcelable { } if (titleId != ID_NULL) { dialogInfoBuilder.setTitle(titleId); + } else if (title != null) { + dialogInfoBuilder.setTitle(title); } if (buttonTextId != ID_NULL) { dialogInfoBuilder.setNeutralButtonText(buttonTextId); + } else if (buttonText != null) { + dialogInfoBuilder.setNeutralButtonText(buttonText); } if (dialogMessageResId != ID_NULL) { dialogInfoBuilder.setMessage(dialogMessageResId); @@ -227,7 +261,9 @@ public final class SuspendDialogInfo implements Parcelable { public int hashCode() { int hashCode = mIconResId; hashCode = 31 * hashCode + mTitleResId; + hashCode = 31 * hashCode + Objects.hashCode(mTitle); hashCode = 31 * hashCode + mNeutralButtonTextResId; + hashCode = 31 * hashCode + Objects.hashCode(mNeutralButtonText); hashCode = 31 * hashCode + mDialogMessageResId; hashCode = 31 * hashCode + Objects.hashCode(mDialogMessage); hashCode = 31 * hashCode + mNeutralButtonAction; @@ -245,10 +281,12 @@ public final class SuspendDialogInfo implements Parcelable { final SuspendDialogInfo otherDialogInfo = (SuspendDialogInfo) obj; return mIconResId == otherDialogInfo.mIconResId && mTitleResId == otherDialogInfo.mTitleResId + && Objects.equals(mTitle, otherDialogInfo.mTitle) && mDialogMessageResId == otherDialogInfo.mDialogMessageResId + && Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage) && mNeutralButtonTextResId == otherDialogInfo.mNeutralButtonTextResId - && mNeutralButtonAction == otherDialogInfo.mNeutralButtonAction - && Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage); + && Objects.equals(mNeutralButtonText, otherDialogInfo.mNeutralButtonText) + && mNeutralButtonAction == otherDialogInfo.mNeutralButtonAction; } @NonNull @@ -264,11 +302,19 @@ public final class SuspendDialogInfo implements Parcelable { builder.append("mTitleResId = 0x"); builder.append(Integer.toHexString(mTitleResId)); builder.append(" "); + } else if (mTitle != null) { + builder.append("mTitle = \""); + builder.append(mTitle); + builder.append("\""); } if (mNeutralButtonTextResId != ID_NULL) { builder.append("mNeutralButtonTextResId = 0x"); builder.append(Integer.toHexString(mNeutralButtonTextResId)); builder.append(" "); + } else if (mNeutralButtonText != null) { + builder.append("mNeutralButtonText = \""); + builder.append(mNeutralButtonText); + builder.append("\""); } if (mDialogMessageResId != ID_NULL) { builder.append("mDialogMessageResId = 0x"); @@ -294,27 +340,33 @@ public final class SuspendDialogInfo implements Parcelable { public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeInt(mIconResId); dest.writeInt(mTitleResId); + dest.writeString(mTitle); dest.writeInt(mDialogMessageResId); dest.writeString(mDialogMessage); dest.writeInt(mNeutralButtonTextResId); + dest.writeString(mNeutralButtonText); dest.writeInt(mNeutralButtonAction); } private SuspendDialogInfo(Parcel source) { mIconResId = source.readInt(); mTitleResId = source.readInt(); + mTitle = source.readString(); mDialogMessageResId = source.readInt(); mDialogMessage = source.readString(); mNeutralButtonTextResId = source.readInt(); + mNeutralButtonText = source.readString(); mNeutralButtonAction = source.readInt(); } SuspendDialogInfo(Builder b) { mIconResId = b.mIconResId; mTitleResId = b.mTitleResId; + mTitle = (mTitleResId == ID_NULL) ? b.mTitle : null; mDialogMessageResId = b.mDialogMessageResId; mDialogMessage = (mDialogMessageResId == ID_NULL) ? b.mDialogMessage : null; mNeutralButtonTextResId = b.mNeutralButtonTextResId; + mNeutralButtonText = (mNeutralButtonTextResId == ID_NULL) ? b.mNeutralButtonText : null; mNeutralButtonAction = b.mNeutralButtonAction; } @@ -338,8 +390,10 @@ public final class SuspendDialogInfo implements Parcelable { private int mDialogMessageResId = ID_NULL; private String mDialogMessage; private int mTitleResId = ID_NULL; + private String mTitle; private int mIconResId = ID_NULL; private int mNeutralButtonTextResId = ID_NULL; + private String mNeutralButtonText; private int mNeutralButtonAction = BUTTON_ACTION_MORE_DETAILS; /** @@ -369,6 +423,21 @@ public final class SuspendDialogInfo implements Parcelable { return this; } + /** + * Set the title text of the dialog. Ignored if a resource id is set via + * {@link #setTitle(int)} + * + * @param title The title of the dialog. + * @return this builder object. + * @see #setTitle(int) + */ + @NonNull + public Builder setTitle(@NonNull String title) { + Preconditions.checkStringNotEmpty(title, "Title cannot be null or empty"); + mTitle = title; + return this; + } + /** * Set the text to show in the body of the dialog. Ignored if a resource id is set via * {@link #setMessage(int)}. @@ -426,6 +495,22 @@ public final class SuspendDialogInfo implements Parcelable { return this; } + /** + * Set the text to be shown on the neutral button. Ignored if a resource id is set via + * {@link #setNeutralButtonText(int)} + * + * @param neutralButtonText The title of the dialog. + * @return this builder object. + * @see #setNeutralButtonText(int) + */ + @NonNull + public Builder setNeutralButtonText(@NonNull String neutralButtonText) { + Preconditions.checkStringNotEmpty(neutralButtonText, + "Button text cannot be null or empty"); + mNeutralButtonText = neutralButtonText; + return this; + } + /** * Set the action expected to happen on neutral button tap. Defaults to * {@link #BUTTON_ACTION_MORE_DETAILS} if this is not provided. diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index 762297d15e6d9..86f29a8f07ef9 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -106,13 +106,17 @@ public class SuspendedAppActivity extends AlertActivity } private String resolveTitle() { - final int titleId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getTitleResId() - : ID_NULL; - if (titleId != ID_NULL && mSuspendingAppResources != null) { - try { - return mSuspendingAppResources.getString(titleId); - } catch (Resources.NotFoundException nfe) { - Slog.e(TAG, "Could not resolve string resource id " + titleId); + if (mSuppliedDialogInfo != null) { + final int titleId = mSuppliedDialogInfo.getTitleResId(); + final String title = mSuppliedDialogInfo.getTitle(); + if (titleId != ID_NULL && mSuspendingAppResources != null) { + try { + return mSuspendingAppResources.getString(titleId); + } catch (Resources.NotFoundException nfe) { + Slog.e(TAG, "Could not resolve string resource id " + titleId); + } + } else if (title != null) { + return title; } } return getString(R.string.app_suspended_title); @@ -159,13 +163,17 @@ public class SuspendedAppActivity extends AlertActivity Slog.w(TAG, "Unknown neutral button action: " + mNeutralButtonAction); return null; } - final int buttonTextId = (mSuppliedDialogInfo != null) - ? mSuppliedDialogInfo.getNeutralButtonTextResId() : ID_NULL; - if (buttonTextId != ID_NULL && mSuspendingAppResources != null) { - try { - return mSuspendingAppResources.getString(buttonTextId); - } catch (Resources.NotFoundException nfe) { - Slog.e(TAG, "Could not resolve string resource id " + buttonTextId); + if (mSuppliedDialogInfo != null) { + final int buttonTextId = mSuppliedDialogInfo.getNeutralButtonTextResId(); + final String buttonText = mSuppliedDialogInfo.getNeutralButtonText(); + if (buttonTextId != ID_NULL && mSuspendingAppResources != null) { + try { + return mSuspendingAppResources.getString(buttonTextId); + } catch (Resources.NotFoundException nfe) { + Slog.e(TAG, "Could not resolve string resource id " + buttonTextId); + } + } else if (buttonText != null) { + return buttonText; } } return getString(defaultButtonTextId); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index d63a4674a83db..a2311690744e3 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -287,7 +287,7 @@ public class PackageManagerSettingsTests { final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder() .setIcon(0x11220001) - .setTitle(0x11220002) + .setTitle("String Title") .setMessage("1st message") .setNeutralButtonText(0x11220003) .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS) @@ -296,7 +296,7 @@ public class PackageManagerSettingsTests { .setIcon(0x22220001) .setTitle(0x22220002) .setMessage("2nd message") - .setNeutralButtonText(0x22220003) + .setNeutralButtonText("String button text") .setNeutralButtonAction(BUTTON_ACTION_UNSUSPEND) .build(); diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java index 322e448d983f5..826a8d446e1da 100644 --- a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java @@ -57,7 +57,7 @@ public class SuspendDialogInfoTest { } @Test - public void equalsComparesTitle() { + public void equalsComparesTitleIds() { final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder(); final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder(); assertEquals(dialogBuilder1.build(), dialogBuilder2.build()); @@ -67,7 +67,39 @@ public class SuspendDialogInfoTest { } @Test - public void equalsComparesButtonText() { + public void equalsIgnoresTitleStringsWhenIdsSet() { + final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder() + .setTitle(VALID_TEST_RES_ID_1) + .setTitle("1st title"); + final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder() + .setTitle(VALID_TEST_RES_ID_1) + .setTitle("2nd title"); + // String titles different but should get be ignored when resource ids are set + assertEquals(dialogBuilder1.build(), dialogBuilder2.build()); + } + + @Test + public void equalsComparesTitleStringsWhenNoIdsSet() { + final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder() + .setTitle("1st title"); + final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder() + .setTitle("2nd title"); + // Both have different titles, which are not ignored as resource ids aren't set + assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build()); + } + + @Test + public void titleStringClearedWhenResIdSet() { + final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder() + .setTitle(VALID_TEST_RES_ID_2) + .setTitle("Should be cleared on build") + .build(); + assertNull(dialogInfo.getTitle()); + assertEquals(VALID_TEST_RES_ID_2, dialogInfo.getTitleResId()); + } + + @Test + public void equalsComparesButtonTextIds() { final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder(); final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder(); assertEquals(dialogBuilder1.build(), dialogBuilder2.build()); @@ -76,6 +108,38 @@ public class SuspendDialogInfoTest { assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build()); } + @Test + public void equalsIgnoresButtonStringsWhenIdsSet() { + final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder() + .setNeutralButtonText(VALID_TEST_RES_ID_1) + .setNeutralButtonText("1st button text"); + final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder() + .setNeutralButtonText(VALID_TEST_RES_ID_1) + .setNeutralButtonText("2nd button text"); + // Button strings different but should get be ignored when resource ids are set + assertEquals(dialogBuilder1.build(), dialogBuilder2.build()); + } + + @Test + public void equalsComparesButtonStringsWhenNoIdsSet() { + final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder() + .setNeutralButtonText("1st button text"); + final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder() + .setNeutralButtonText("2nd button text"); + // Both have different button texts, which are not ignored as resource ids aren't set + assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build()); + } + + @Test + public void buttonStringClearedWhenResIdSet() { + final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder() + .setNeutralButtonText(VALID_TEST_RES_ID_2) + .setNeutralButtonText("Should be cleared on build") + .build(); + assertNull(dialogInfo.getNeutralButtonText()); + assertEquals(VALID_TEST_RES_ID_2, dialogInfo.getNeutralButtonTextResId()); + } + @Test public void equalsComparesButtonAction() { final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();