From c1cb8a4c2db9e7f86bba2f99d1fc064488be9385 Mon Sep 17 00:00:00 2001 From: Robin Lee Date: Mon, 6 Feb 2017 18:19:40 +0000 Subject: [PATCH] Don't show multiple wipe warning dialogs Stacking them on top of each other like that does not count as defence in depth and we should not do it. Bug: 32934848 Test: make RunSettingsRoboTests # added one specifically for this Change-Id: I9490652c05a630e7c3f9164a9144fadfc0f41ea1 --- .../ConfirmDeviceCredentialBaseFragment.java | 78 ++++++++++++++----- .../settings/ConfirmCredentialTest.java | 55 +++++++++++++ 2 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/ConfirmCredentialTest.java diff --git a/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java index cabc8050acf..a5f98308775 100644 --- a/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java +++ b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java @@ -14,18 +14,21 @@ * limitations under the License */ +// TODO (b/35202196): move this class out of the root of the package. package com.android.settings; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.FragmentManager; import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.IntentSender; import android.graphics.Point; @@ -289,12 +292,13 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra // Last try final String title = getActivity().getString( R.string.lock_profile_wipe_warning_title); - final String message = getActivity().getString(getLastTryErrorMessage()); - showDialog(title, message, android.R.string.ok, false /* dismiss */); + LastTryDialog.show(getFragmentManager(), title, getLastTryErrorMessage(), + android.R.string.ok, false /* dismiss */); } else if (remainingAttempts <= 0) { // Profile is wiped - final String message = getActivity().getString(R.string.lock_profile_wipe_content); - showDialog(null, message, R.string.lock_profile_wipe_dismiss, true /* dismiss */); + LastTryDialog.show(getFragmentManager(), null /* title */, + R.string.lock_profile_wipe_content, R.string.lock_profile_wipe_dismiss, + true /* dismiss */); } if (mErrorTextView != null) { final String message = getActivity().getString(R.string.lock_profile_wipe_attempts, @@ -328,19 +332,55 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFra showError(getText(msg), timeout); } - private void showDialog(String title, String message, int buttonString, final boolean dismiss) { - final AlertDialog dialog = new AlertDialog.Builder(getActivity()) - .setTitle(title) - .setMessage(message) - .setPositiveButton(buttonString, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (dismiss) { - getActivity().finish(); - } - } - }) - .create(); - dialog.show(); + public static class LastTryDialog extends DialogFragment { + private static final String TAG = LastTryDialog.class.getSimpleName(); + + private static final String ARG_TITLE = "title"; + private static final String ARG_MESSAGE = "message"; + private static final String ARG_BUTTON = "button"; + private static final String ARG_DISMISS = "dismiss"; + + static boolean show(FragmentManager from, String title, int message, int button, + boolean dismiss) { + LastTryDialog existent = (LastTryDialog) from.findFragmentByTag(TAG); + if (existent != null && !existent.isRemoving()) { + return false; + } + Bundle args = new Bundle(); + args.putString(ARG_TITLE, title); + args.putInt(ARG_MESSAGE, message); + args.putInt(ARG_BUTTON, button); + args.putBoolean(ARG_DISMISS, dismiss); + + DialogFragment dialog = new LastTryDialog(); + dialog.setArguments(args); + dialog.show(from, TAG); + return true; + } + + static void hide(FragmentManager from) { + LastTryDialog dialog = (LastTryDialog) from.findFragmentByTag(TAG); + if (dialog != null) { + dialog.dismissAllowingStateLoss(); + from.executePendingTransactions(); + } + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setTitle(getArguments().getString(ARG_TITLE)) + .setMessage(getArguments().getInt(ARG_MESSAGE)) + .setPositiveButton(getArguments().getInt(ARG_BUTTON), null) + .create(); + } + + @Override + public void onDismiss(final DialogInterface dialog) { + super.onDismiss(dialog); + if (getActivity() != null && getArguments().getBoolean(ARG_DISMISS)) { + getActivity().finish(); + } + } } } diff --git a/tests/robotests/src/com/android/settings/ConfirmCredentialTest.java b/tests/robotests/src/com/android/settings/ConfirmCredentialTest.java new file mode 100644 index 00000000000..f771a1ffec4 --- /dev/null +++ b/tests/robotests/src/com/android/settings/ConfirmCredentialTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 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.settings; + +import static com.android.settings.ConfirmDeviceCredentialBaseFragment.LastTryDialog; +import static com.google.common.truth.Truth.assertThat; + +import android.R; +import android.app.Activity; +import android.app.Fragment; +import android.app.FragmentManager; + +import com.android.settings.testutils.shadow.SettingsShadowResources; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config( + manifest = TestConfig.MANIFEST_PATH, + sdk = TestConfig.SDK_VERSION, + shadows = { + SettingsShadowResources.class, + SettingsShadowResources.SettingsShadowTheme.class, + }) +public class ConfirmCredentialTest { + @Test + public void testLastTryDialogShownExactlyOnce() { + FragmentManager fm = Robolectric.buildActivity(Activity.class).get().getFragmentManager(); + + // Launch only one instance at a time. + assertThat(LastTryDialog.show(fm, "title", R.string.yes, R.string.ok, false)).isTrue(); + assertThat(LastTryDialog.show(fm, "title", R.string.yes, R.string.ok, false)).isFalse(); + + // After cancelling, the dialog should be re-shown when asked for. + LastTryDialog.hide(fm); + assertThat(LastTryDialog.show(fm, "title", R.string.yes, R.string.ok, false)).isTrue(); + } +}