diff --git a/src/com/android/settings/CustomListPreference.java b/src/com/android/settings/CustomListPreference.java index ae830137184..e7c7600a19f 100644 --- a/src/com/android/settings/CustomListPreference.java +++ b/src/com/android/settings/CustomListPreference.java @@ -18,8 +18,13 @@ package com.android.settings; import android.app.AlertDialog; import android.app.Dialog; +import android.app.DialogFragment; +import android.app.Fragment; +import android.app.FragmentTransaction; import android.content.Context; import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; import android.os.Bundle; import android.support.v14.preference.ListPreferenceDialogFragment; import android.support.v7.preference.ListPreference; @@ -50,6 +55,18 @@ public class CustomListPreference extends ListPreference { return true; } + /** + * Called when a user is about to choose the given value, to determine if we + * should show a confirmation dialog. + * + * @param value the value the user is about to choose + * @return the message to show in a confirmation dialog, or {@code null} to + * not request confirmation + */ + protected CharSequence getConfirmationMessage(String value) { + return null; + } + protected void onDialogStateRestored(Dialog dialog, Bundle savedInstanceState) { } @@ -82,9 +99,7 @@ public class CustomListPreference extends ListPreference { builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - CustomListPreferenceDialogFragment.this.onClick(dialog, - DialogInterface.BUTTON_POSITIVE); - dialog.dismiss(); + onItemChosen(); } }); } @@ -115,18 +130,11 @@ public class CustomListPreference extends ListPreference { protected DialogInterface.OnClickListener getOnItemClickListener() { return new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int which) { setClickedDialogEntryIndex(which); - - if (getCustomizablePreference().isAutoClosePreference()) { - /* - * Clicking on an item simulates the positive button - * click, and dismisses the dialog. - */ - CustomListPreferenceDialogFragment.this.onClick(dialog, - DialogInterface.BUTTON_POSITIVE); - dialog.dismiss(); + onItemChosen(); } } }; @@ -136,17 +144,74 @@ public class CustomListPreference extends ListPreference { mClickedDialogEntryIndex = which; } + private String getValue() { + final ListPreference preference = getCustomizablePreference(); + if (mClickedDialogEntryIndex >= 0 && preference.getEntryValues() != null) { + return preference.getEntryValues()[mClickedDialogEntryIndex].toString(); + } else { + return null; + } + } + + /** + * Called when user has made a concrete item choice, but we might need + * to make a quick detour to confirm that choice with a second dialog. + */ + protected void onItemChosen() { + final CharSequence message = getCustomizablePreference() + .getConfirmationMessage(getValue()); + if (message != null) { + final Fragment f = new ConfirmDialogFragment(); + final Bundle args = new Bundle(); + args.putCharSequence(Intent.EXTRA_TEXT, message); + f.setArguments(args); + f.setTargetFragment(CustomListPreferenceDialogFragment.this, 0); + final FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.add(f, getTag() + "-Confirm"); + ft.commitAllowingStateLoss(); + } else { + onItemConfirmed(); + } + } + + /** + * Called when user has made a concrete item choice and we've fully + * confirmed they want to move forward (if we took a detour above). + */ + protected void onItemConfirmed() { + onClick(getDialog(), DialogInterface.BUTTON_POSITIVE); + getDialog().dismiss(); + } + @Override public void onDialogClosed(boolean positiveResult) { getCustomizablePreference().onDialogClosed(positiveResult); final ListPreference preference = getCustomizablePreference(); - if (positiveResult && mClickedDialogEntryIndex >= 0 && - preference.getEntryValues() != null) { - String value = preference.getEntryValues()[mClickedDialogEntryIndex].toString(); + final String value = getValue(); + if (positiveResult && value != null) { if (preference.callChangeListener(value)) { preference.setValue(value); } } } } + + public static class ConfirmDialogFragment extends DialogFragment { + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setMessage(getArguments().getCharSequence(Intent.EXTRA_TEXT)) + .setPositiveButton(android.R.string.ok, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final Fragment f = getTargetFragment(); + if (f != null) { + ((CustomListPreferenceDialogFragment) f).onItemConfirmed(); + } + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } + } } diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 90dd9e0aa0e..0725386af73 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -1150,5 +1150,14 @@ public final class Utils extends com.android.settingslib.Utils { } return false; } -} + public static boolean isPackageDirectBootAware(Context context, String packageName) { + try { + final ApplicationInfo ai = context.getPackageManager().getApplicationInfo( + packageName, 0); + return ai.isDirectBootAware() || ai.isPartiallyDirectBootAware(); + } catch (NameNotFoundException ignored) { + } + return false; + } +} diff --git a/src/com/android/settings/applications/DefaultEmergencyPreference.java b/src/com/android/settings/applications/DefaultEmergencyPreference.java index f0a97b1b4cb..dd4dc2ea419 100644 --- a/src/com/android/settings/applications/DefaultEmergencyPreference.java +++ b/src/com/android/settings/applications/DefaultEmergencyPreference.java @@ -16,7 +16,6 @@ package com.android.settings.applications; -import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -30,9 +29,11 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArraySet; import android.util.AttributeSet; -import com.android.internal.telephony.SmsApplication; + import com.android.settings.AppListPreference; +import com.android.settings.R; import com.android.settings.SelfAvailablePreference; +import com.android.settings.Utils; import java.util.List; import java.util.Objects; @@ -56,6 +57,12 @@ public class DefaultEmergencyPreference extends AppListPreference load(); } + @Override + protected CharSequence getConfirmationMessage(String value) { + return Utils.isPackageDirectBootAware(getContext(), value) ? null + : getContext().getText(R.string.direct_boot_unaware_dialog_message); + } + @Override protected boolean persistString(String value) { String previousValue = Settings.Secure.getString(mContentResolver, diff --git a/src/com/android/settings/applications/DefaultPhonePreference.java b/src/com/android/settings/applications/DefaultPhonePreference.java index fdaf7add3fc..5689c838335 100644 --- a/src/com/android/settings/applications/DefaultPhonePreference.java +++ b/src/com/android/settings/applications/DefaultPhonePreference.java @@ -24,22 +24,27 @@ import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.AttributeSet; + import com.android.settings.AppListPreference; +import com.android.settings.R; import com.android.settings.SelfAvailablePreference; +import com.android.settings.Utils; import java.util.List; import java.util.Objects; public class DefaultPhonePreference extends AppListPreference implements SelfAvailablePreference { - private final Context mContext; - public DefaultPhonePreference(Context context, AttributeSet attrs) { super(context, attrs); - - mContext = context.getApplicationContext(); loadDialerApps(); } + @Override + protected CharSequence getConfirmationMessage(String value) { + return Utils.isPackageDirectBootAware(getContext(), value) ? null + : getContext().getText(R.string.direct_boot_unaware_dialog_message); + } + @Override protected boolean persistString(String value) { if (!TextUtils.isEmpty(value) && !Objects.equals(value, getDefaultPackage())) { diff --git a/src/com/android/settings/applications/DefaultSmsPreference.java b/src/com/android/settings/applications/DefaultSmsPreference.java index 9315102015e..96ac9a2898f 100644 --- a/src/com/android/settings/applications/DefaultSmsPreference.java +++ b/src/com/android/settings/applications/DefaultSmsPreference.java @@ -22,19 +22,20 @@ import android.os.UserManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.AttributeSet; + import com.android.internal.telephony.SmsApplication; import com.android.internal.telephony.SmsApplication.SmsApplicationData; import com.android.settings.AppListPreference; +import com.android.settings.R; import com.android.settings.SelfAvailablePreference; +import com.android.settings.Utils; import java.util.Collection; import java.util.Objects; public class DefaultSmsPreference extends AppListPreference implements SelfAvailablePreference { - public DefaultSmsPreference(Context context, AttributeSet attrs) { super(context, attrs); - loadSmsApps(); } @@ -59,6 +60,12 @@ public class DefaultSmsPreference extends AppListPreference implements SelfAvail return null; } + @Override + protected CharSequence getConfirmationMessage(String value) { + return Utils.isPackageDirectBootAware(getContext(), value) ? null + : getContext().getText(R.string.direct_boot_unaware_dialog_message); + } + @Override protected boolean persistString(String value) { if (!TextUtils.isEmpty(value) && !Objects.equals(value, getDefaultPackage())) { diff --git a/src/com/android/settings/inputmethod/InputMethodPreference.java b/src/com/android/settings/inputmethod/InputMethodPreference.java index 1d4fa67dda0..2c277002b28 100755 --- a/src/com/android/settings/inputmethod/InputMethodPreference.java +++ b/src/com/android/settings/inputmethod/InputMethodPreference.java @@ -35,6 +35,7 @@ import android.widget.Toast; import com.android.internal.inputmethod.InputMethodUtils; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedSwitchPreference; @@ -142,18 +143,22 @@ class InputMethodPreference extends RestrictedSwitchPreference implements OnPref } if (isChecked()) { // Disable this IME. - setChecked(false); - mOnSaveListener.onSaveInputMethodPreference(this); + setCheckedInternal(false); return false; } if (InputMethodUtils.isSystemIme(mImi)) { - // Enable a system IME. No need to show a security warning dialog. - setChecked(true); - mOnSaveListener.onSaveInputMethodPreference(this); - return false; + // Enable a system IME. No need to show a security warning dialog, + // but we might need to prompt if it's not Direct Boot aware. + if (Utils.isPackageDirectBootAware(getContext(), mImi.getPackageName())) { + setCheckedInternal(true); + } else { + showDirectBootWarnDialog(); + } + } else { + // Once security is confirmed, we might prompt if the IME isn't + // Direct Boot aware. + showSecurityWarnDialog(); } - // Enable a 3rd party IME. - showSecurityWarnDialog(mImi); return false; } @@ -218,7 +223,13 @@ class InputMethodPreference extends RestrictedSwitchPreference implements OnPref subtypes, getContext(), mImi); } - private void showSecurityWarnDialog(final InputMethodInfo imi) { + private void setCheckedInternal(boolean checked) { + super.setChecked(checked); + mOnSaveListener.onSaveInputMethodPreference(InputMethodPreference.this); + notifyChanged(); + } + + private void showSecurityWarnDialog() { if (mDialog != null && mDialog.isShowing()) { mDialog.dismiss(); } @@ -226,25 +237,50 @@ class InputMethodPreference extends RestrictedSwitchPreference implements OnPref final AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setCancelable(true /* cancelable */); builder.setTitle(android.R.string.dialog_alert_title); - final CharSequence label = imi.getServiceInfo().applicationInfo.loadLabel( + final CharSequence label = mImi.getServiceInfo().applicationInfo.loadLabel( context.getPackageManager()); builder.setMessage(context.getString(R.string.ime_security_warning, label)); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { - // The user confirmed to enable a 3rd party IME. - setChecked(true); - mOnSaveListener.onSaveInputMethodPreference(InputMethodPreference.this); - notifyChanged(); + // The user confirmed to enable a 3rd party IME, but we might + // need to prompt if it's not Direct Boot aware. + if (Utils.isPackageDirectBootAware(getContext(), mImi.getPackageName())) { + setCheckedInternal(true); + } else { + showDirectBootWarnDialog(); + } } }); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { // The user canceled to enable a 3rd party IME. - setChecked(false); - mOnSaveListener.onSaveInputMethodPreference(InputMethodPreference.this); - notifyChanged(); + setCheckedInternal(false); + } + }); + mDialog = builder.create(); + mDialog.show(); + } + + private void showDirectBootWarnDialog() { + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + } + final Context context = getContext(); + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setCancelable(true /* cancelable */); + builder.setMessage(context.getText(R.string.direct_boot_unaware_dialog_message)); + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int which) { + setCheckedInternal(true); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int which) { + setCheckedInternal(false); } }); mDialog = builder.create();