Merge "Suspending app can customize intercepting dialog"
This commit is contained in:
committed by
Android (Google) Code Review
commit
bc9ebba7e5
@@ -55,6 +55,7 @@ import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.SharedLibraryInfo;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.VerifierDeviceIdentity;
|
||||
import android.content.pm.VersionedPackage;
|
||||
import android.content.pm.dex.ArtManager;
|
||||
@@ -85,6 +86,7 @@ import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
import android.system.StructStat;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.util.LauncherIcons;
|
||||
@@ -2255,9 +2257,19 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
|
||||
PersistableBundle appExtras, PersistableBundle launcherExtras,
|
||||
String dialogMessage) {
|
||||
final SuspendDialogInfo dialogInfo = !TextUtils.isEmpty(dialogMessage)
|
||||
? new SuspendDialogInfo.Builder().setMessage(dialogMessage).build()
|
||||
: null;
|
||||
return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, dialogInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
|
||||
PersistableBundle appExtras, PersistableBundle launcherExtras,
|
||||
SuspendDialogInfo dialogInfo) {
|
||||
try {
|
||||
return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
|
||||
launcherExtras, dialogMessage, mContext.getOpPackageName(),
|
||||
launcherExtras, dialogInfo, mContext.getOpPackageName(),
|
||||
getUserId());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.content.pm.PermissionGroupInfo;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.pm.VerifierDeviceIdentity;
|
||||
import android.content.pm.VersionedPackage;
|
||||
@@ -273,7 +274,7 @@ interface IPackageManager {
|
||||
|
||||
String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
|
||||
in PersistableBundle appExtras, in PersistableBundle launcherExtras,
|
||||
String dialogMessage, String callingPackage, int userId);
|
||||
in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
|
||||
|
||||
boolean isPackageSuspendedForUser(String packageName, int userId);
|
||||
|
||||
|
||||
@@ -5664,7 +5664,7 @@ public abstract class PackageManager {
|
||||
* {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
|
||||
*
|
||||
* @param packageNames The names of the packages to set the suspended status.
|
||||
* @param suspended If set to {@code true} than the packages will be suspended, if set to
|
||||
* @param suspended If set to {@code true}, the packages will be suspended, if set to
|
||||
* {@code false}, the packages will be unsuspended.
|
||||
* @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
|
||||
* which will be shared with the apps being suspended. Ignored if
|
||||
@@ -5676,15 +5676,76 @@ public abstract class PackageManager {
|
||||
* suspended app.
|
||||
*
|
||||
* @return an array of package names for which the suspended status could not be set as
|
||||
* requested in this method.
|
||||
* requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
|
||||
*
|
||||
* @deprecated use {@link #setPackagesSuspended(String[], boolean, PersistableBundle,
|
||||
* PersistableBundle, android.content.pm.SuspendDialogInfo)} instead.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Deprecated
|
||||
@RequiresPermission(Manifest.permission.SUSPEND_APPS)
|
||||
@Nullable
|
||||
public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
|
||||
@Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
|
||||
@Nullable String dialogMessage) {
|
||||
throw new UnsupportedOperationException("setPackagesSuspended not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given packages in a suspended state, where attempts at starting activities are
|
||||
* denied.
|
||||
*
|
||||
* <p>The suspended application's notifications and all of its windows will be hidden, any
|
||||
* of its started activities will be stopped and it won't be able to ring the device.
|
||||
* It doesn't remove the data or the actual package file.
|
||||
*
|
||||
* <p>When the user tries to launch a suspended app, a system dialog alerting them that the app
|
||||
* is suspended will be shown instead.
|
||||
* The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object
|
||||
* to this api. This dialog will have a button that starts the
|
||||
* {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an
|
||||
* activity which handles this action.
|
||||
*
|
||||
* <p>The packages being suspended must already be installed. If a package is uninstalled, it
|
||||
* will no longer be suspended.
|
||||
*
|
||||
* <p>Optionally, the suspending app can provide extra information in the form of
|
||||
* {@link PersistableBundle} objects to be shared with the apps being suspended and the
|
||||
* launcher to support customization that they might need to handle the suspended state.
|
||||
*
|
||||
* <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this api.
|
||||
*
|
||||
* @param packageNames The names of the packages to set the suspended status.
|
||||
* @param suspended If set to {@code true}, the packages will be suspended, if set to
|
||||
* {@code false}, the packages will be unsuspended.
|
||||
* @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
|
||||
* which will be shared with the apps being suspended. Ignored if
|
||||
* {@code suspended} is false.
|
||||
* @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
|
||||
* provide which will be shared with the launcher. Ignored if
|
||||
* {@code suspended} is false.
|
||||
* @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
|
||||
* should be shown to the user when they try to launch a suspended app.
|
||||
* Ignored if {@code suspended} is false.
|
||||
*
|
||||
* @return an array of package names for which the suspended status could not be set as
|
||||
* requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
|
||||
*
|
||||
* @see #isPackageSuspended
|
||||
* @see SuspendDialogInfo
|
||||
* @see SuspendDialogInfo.Builder
|
||||
* @see Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(Manifest.permission.SUSPEND_APPS)
|
||||
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
|
||||
@Nullable
|
||||
public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
|
||||
@Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
|
||||
String dialogMessage) {
|
||||
@Nullable SuspendDialogInfo dialogInfo) {
|
||||
throw new UnsupportedOperationException("setPackagesSuspended not implemented");
|
||||
}
|
||||
|
||||
|
||||
@@ -267,14 +267,15 @@ public abstract class PackageManagerInternal {
|
||||
public abstract String getSuspendingPackage(String suspendedPackage, int userId);
|
||||
|
||||
/**
|
||||
* Get the dialog message to be shown to the user when they try to launch a suspended
|
||||
* application.
|
||||
* Get the information describing the dialog to be shown to the user when they try to launch a
|
||||
* suspended application.
|
||||
*
|
||||
* @param suspendedPackage The package that has been suspended.
|
||||
* @param userId The user for which to check.
|
||||
* @return The dialog message to be shown to the user.
|
||||
* @return A {@link SuspendDialogInfo} object describing the dialog to be shown.
|
||||
*/
|
||||
public abstract String getSuspendedDialogMessage(String suspendedPackage, int userId);
|
||||
@Nullable
|
||||
public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId);
|
||||
|
||||
/**
|
||||
* Do a straight uid lookup for the given package/application in the given user.
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.os.BaseBundle;
|
||||
import android.os.PersistableBundle;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -50,7 +51,7 @@ public class PackageUserState {
|
||||
public boolean hidden; // Is the app restricted by owner / admin
|
||||
public boolean suspended;
|
||||
public String suspendingPackage;
|
||||
public String dialogMessage; // Message to show when a suspended package launch attempt is made
|
||||
public SuspendDialogInfo dialogInfo;
|
||||
public PersistableBundle suspendedAppExtras;
|
||||
public PersistableBundle suspendedLauncherExtras;
|
||||
public boolean instantApp;
|
||||
@@ -79,6 +80,7 @@ public class PackageUserState {
|
||||
installReason = PackageManager.INSTALL_REASON_UNKNOWN;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public PackageUserState(PackageUserState o) {
|
||||
ceDataInode = o.ceDataInode;
|
||||
installed = o.installed;
|
||||
@@ -87,7 +89,7 @@ public class PackageUserState {
|
||||
hidden = o.hidden;
|
||||
suspended = o.suspended;
|
||||
suspendingPackage = o.suspendingPackage;
|
||||
dialogMessage = o.dialogMessage;
|
||||
dialogInfo = o.dialogInfo;
|
||||
suspendedAppExtras = o.suspendedAppExtras;
|
||||
suspendedLauncherExtras = o.suspendedLauncherExtras;
|
||||
instantApp = o.instantApp;
|
||||
@@ -217,7 +219,7 @@ public class PackageUserState {
|
||||
|| !suspendingPackage.equals(oldState.suspendingPackage)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(dialogMessage, oldState.dialogMessage)) {
|
||||
if (!Objects.equals(dialogInfo, oldState.dialogInfo)) {
|
||||
return false;
|
||||
}
|
||||
if (!BaseBundle.kindofEquals(suspendedAppExtras,
|
||||
|
||||
18
core/java/android/content/pm/SuspendDialogInfo.aidl
Normal file
18
core/java/android/content/pm/SuspendDialogInfo.aidl
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.content.pm;
|
||||
|
||||
parcelable SuspendDialogInfo;
|
||||
379
core/java/android/content/pm/SuspendDialogInfo.java
Normal file
379
core/java/android/content/pm/SuspendDialogInfo.java
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
import static android.content.res.ResourceId.ID_NULL;
|
||||
|
||||
import android.annotation.DrawableRes;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.StringRes;
|
||||
import android.annotation.SystemApi;
|
||||
import android.content.res.ResourceId;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.PersistableBundle;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A container to describe the dialog to be shown when the user tries to launch a suspended
|
||||
* application.
|
||||
* The suspending app can customize the dialog's following attributes:
|
||||
* <ul>
|
||||
* <li>The dialog icon, by providing a resource id.
|
||||
* <li>The title text, by providing a resource id.
|
||||
* <li>The text of the dialog's body, by providing a resource id or a string.
|
||||
* <li>The text on the neutral button which starts the
|
||||
* {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS SHOW_SUSPENDED_APP_DETAILS}
|
||||
* activity, by providing a resource id.
|
||||
* </ul>
|
||||
* System defaults are used whenever any of these are not provided, or any of the provided resource
|
||||
* ids cannot be resolved at the time of displaying the dialog.
|
||||
*
|
||||
* @hide
|
||||
* @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
|
||||
* SuspendDialogInfo)
|
||||
* @see Builder
|
||||
*/
|
||||
@SystemApi
|
||||
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_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 final int mIconResId;
|
||||
private final int mTitleResId;
|
||||
private final int mDialogMessageResId;
|
||||
private final String mDialogMessage;
|
||||
private final int mNeutralButtonTextResId;
|
||||
|
||||
/**
|
||||
* @return the resource id of the icon to be used with the dialog
|
||||
* @hide
|
||||
*/
|
||||
@DrawableRes
|
||||
public int getIconResId() {
|
||||
return mIconResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the resource id of the title to be used with the dialog
|
||||
* @hide
|
||||
*/
|
||||
@StringRes
|
||||
public int getTitleResId() {
|
||||
return mTitleResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the resource id of the text to be shown in the dialog's body
|
||||
* @hide
|
||||
*/
|
||||
@StringRes
|
||||
public int getDialogMessageResId() {
|
||||
return mDialogMessageResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the text to be shown in the dialog's body. Returns {@code null} if
|
||||
* {@link #getDialogMessageResId()} returns a valid resource id.
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public String getDialogMessage() {
|
||||
return mDialogMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the text to be shown
|
||||
* @hide
|
||||
*/
|
||||
@StringRes
|
||||
public int getNeutralButtonTextResId() {
|
||||
return mNeutralButtonTextResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void saveToXml(XmlSerializer out) throws IOException {
|
||||
if (mIconResId != ID_NULL) {
|
||||
XmlUtils.writeIntAttribute(out, XML_ATTR_ICON_RES_ID, mIconResId);
|
||||
}
|
||||
if (mTitleResId != ID_NULL) {
|
||||
XmlUtils.writeIntAttribute(out, XML_ATTR_TITLE_RES_ID, mTitleResId);
|
||||
}
|
||||
if (mDialogMessageResId != ID_NULL) {
|
||||
XmlUtils.writeIntAttribute(out, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId);
|
||||
} else {
|
||||
XmlUtils.writeStringAttribute(out, XML_ATTR_DIALOG_MESSAGE, mDialogMessage);
|
||||
}
|
||||
if (mNeutralButtonTextResId != ID_NULL) {
|
||||
XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static SuspendDialogInfo restoreFromXml(XmlPullParser in) {
|
||||
final SuspendDialogInfo.Builder dialogInfoBuilder = new SuspendDialogInfo.Builder();
|
||||
try {
|
||||
final int iconId = XmlUtils.readIntAttribute(in, XML_ATTR_ICON_RES_ID, ID_NULL);
|
||||
final int titleId = XmlUtils.readIntAttribute(in, XML_ATTR_TITLE_RES_ID, ID_NULL);
|
||||
final int buttonTextId = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_TEXT_RES_ID,
|
||||
ID_NULL);
|
||||
final int dialogMessageResId = XmlUtils.readIntAttribute(
|
||||
in, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL);
|
||||
final String dialogMessage = XmlUtils.readStringAttribute(in, XML_ATTR_DIALOG_MESSAGE);
|
||||
|
||||
if (iconId != ID_NULL) {
|
||||
dialogInfoBuilder.setIcon(iconId);
|
||||
}
|
||||
if (titleId != ID_NULL) {
|
||||
dialogInfoBuilder.setTitle(titleId);
|
||||
}
|
||||
if (buttonTextId != ID_NULL) {
|
||||
dialogInfoBuilder.setNeutralButtonText(buttonTextId);
|
||||
}
|
||||
if (dialogMessageResId != ID_NULL) {
|
||||
dialogInfoBuilder.setMessage(dialogMessageResId);
|
||||
} else if (dialogMessage != null) {
|
||||
dialogInfoBuilder.setMessage(dialogMessage);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Exception while parsing from xml. Some fields may default", e);
|
||||
}
|
||||
return dialogInfoBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = mIconResId;
|
||||
hashCode = 31 * hashCode + mTitleResId;
|
||||
hashCode = 31 * hashCode + mNeutralButtonTextResId;
|
||||
hashCode = 31 * hashCode + mDialogMessageResId;
|
||||
hashCode = 31 * hashCode + Objects.hashCode(mDialogMessage);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof SuspendDialogInfo)) {
|
||||
return false;
|
||||
}
|
||||
final SuspendDialogInfo otherDialogInfo = (SuspendDialogInfo) obj;
|
||||
return mIconResId == otherDialogInfo.mIconResId
|
||||
&& mTitleResId == otherDialogInfo.mTitleResId
|
||||
&& mDialogMessageResId == otherDialogInfo.mDialogMessageResId
|
||||
&& mNeutralButtonTextResId == otherDialogInfo.mNeutralButtonTextResId
|
||||
&& Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder("SuspendDialogInfo: {");
|
||||
if (mIconResId != ID_NULL) {
|
||||
builder.append("mIconId = 0x");
|
||||
builder.append(Integer.toHexString(mIconResId));
|
||||
builder.append(" ");
|
||||
}
|
||||
if (mTitleResId != ID_NULL) {
|
||||
builder.append("mTitleResId = 0x");
|
||||
builder.append(Integer.toHexString(mTitleResId));
|
||||
builder.append(" ");
|
||||
}
|
||||
if (mNeutralButtonTextResId != ID_NULL) {
|
||||
builder.append("mNeutralButtonTextResId = 0x");
|
||||
builder.append(Integer.toHexString(mNeutralButtonTextResId));
|
||||
builder.append(" ");
|
||||
}
|
||||
if (mDialogMessageResId != ID_NULL) {
|
||||
builder.append("mDialogMessageResId = 0x");
|
||||
builder.append(Integer.toHexString(mDialogMessageResId));
|
||||
builder.append(" ");
|
||||
} else if (mDialogMessage != null) {
|
||||
builder.append("mDialogMessage = \"");
|
||||
builder.append(mDialogMessage);
|
||||
builder.append("\" ");
|
||||
}
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int parcelableFlags) {
|
||||
dest.writeInt(mIconResId);
|
||||
dest.writeInt(mTitleResId);
|
||||
dest.writeInt(mDialogMessageResId);
|
||||
dest.writeString(mDialogMessage);
|
||||
dest.writeInt(mNeutralButtonTextResId);
|
||||
}
|
||||
|
||||
private SuspendDialogInfo(Parcel source) {
|
||||
mIconResId = source.readInt();
|
||||
mTitleResId = source.readInt();
|
||||
mDialogMessageResId = source.readInt();
|
||||
mDialogMessage = source.readString();
|
||||
mNeutralButtonTextResId = source.readInt();
|
||||
}
|
||||
|
||||
SuspendDialogInfo(Builder b) {
|
||||
mIconResId = b.mIconResId;
|
||||
mTitleResId = b.mTitleResId;
|
||||
mDialogMessageResId = b.mDialogMessageResId;
|
||||
mDialogMessage = (mDialogMessageResId == ID_NULL) ? b.mDialogMessage : null;
|
||||
mNeutralButtonTextResId = b.mNeutralButtonTextResId;
|
||||
}
|
||||
|
||||
public static final Creator<SuspendDialogInfo> CREATOR = new Creator<SuspendDialogInfo>() {
|
||||
@Override
|
||||
public SuspendDialogInfo createFromParcel(Parcel source) {
|
||||
return new SuspendDialogInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuspendDialogInfo[] newArray(int size) {
|
||||
return new SuspendDialogInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Builder to build a {@link SuspendDialogInfo} object.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private int mDialogMessageResId = ID_NULL;
|
||||
private String mDialogMessage;
|
||||
private int mTitleResId = ID_NULL;
|
||||
private int mIconResId = ID_NULL;
|
||||
private int mNeutralButtonTextResId = ID_NULL;
|
||||
|
||||
/**
|
||||
* Set the resource id of the icon to be used. If not provided, no icon will be shown.
|
||||
*
|
||||
* @param resId The resource id of the icon.
|
||||
* @return this builder object.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setIcon(@DrawableRes int resId) {
|
||||
Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
|
||||
mIconResId = resId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource id of the title text to be displayed. If this is not provided, the
|
||||
* system will use a default title.
|
||||
*
|
||||
* @param resId The resource id of the title.
|
||||
* @return this builder object.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setTitle(@StringRes int resId) {
|
||||
Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
|
||||
mTitleResId = resId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text to show in the body of the dialog. Ignored if a resource id is set via
|
||||
* {@link #setMessage(int)}.
|
||||
* <p>
|
||||
* The system will use {@link String#format(Locale, String, Object...) String.format} to
|
||||
* insert the suspended app name into the message, so an example format string could be
|
||||
* {@code "The app %1$s is currently suspended"}. This is optional - if the string passed in
|
||||
* {@code message} does not accept an argument, it will be used as is.
|
||||
*
|
||||
* @param message The dialog message.
|
||||
* @return this builder object.
|
||||
* @see #setMessage(int)
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setMessage(@NonNull String message) {
|
||||
Preconditions.checkStringNotEmpty(message, "Message cannot be null or empty");
|
||||
mDialogMessage = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource id of the dialog message to be shown. If no dialog message is provided
|
||||
* via either this method or {@link #setMessage(String)}, the system will use a
|
||||
* default message.
|
||||
* <p>
|
||||
* The system will use {@link android.content.res.Resources#getString(int, Object...)
|
||||
* getString} to insert the suspended app name into the message, so an example format string
|
||||
* could be {@code "The app %1$s is currently suspended"}. This is optional - if the string
|
||||
* referred to by {@code resId} does not accept an argument, it will be used as is.
|
||||
*
|
||||
* @param resId The resource id of the dialog message.
|
||||
* @return this builder object.
|
||||
* @see #setMessage(String)
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setMessage(@StringRes int resId) {
|
||||
Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
|
||||
mDialogMessageResId = resId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource id of text to be shown on the neutral button. Tapping this button starts
|
||||
* the {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} activity. If this is
|
||||
* not provided, the system will use a default text.
|
||||
*
|
||||
* @param resId The resource id of the button text
|
||||
* @return this builder object.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setNeutralButtonText(@StringRes int resId) {
|
||||
Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
|
||||
mNeutralButtonTextResId = resId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the final object based on given inputs.
|
||||
*
|
||||
* @return The {@link SuspendDialogInfo} object built using this builder.
|
||||
*/
|
||||
@NonNull
|
||||
public SuspendDialogInfo build() {
|
||||
return new SuspendDialogInfo(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user