1 // Copyright 2017 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.signin; 6 7 import android.app.Dialog; 8 import android.content.DialogInterface; 9 import android.os.Bundle; 10 11 import androidx.appcompat.app.AlertDialog; 12 import androidx.fragment.app.DialogFragment; 13 import androidx.fragment.app.FragmentManager; 14 import androidx.fragment.app.FragmentTransaction; 15 16 import org.chromium.chrome.R; 17 18 /** 19 * Class to decouple ConfirmSyncDataStateMachine from UI code and dialog management. 20 */ 21 public class ConfirmSyncDataStateMachineDelegate { 22 /** 23 * Listener to receive events from progress dialog. If the dialog is not dismissed by showing 24 * other dialog or calling {@link ConfirmSyncDataStateMachineDelegate#dismissAllDialogs}, 25 * then {@link #onCancel} will be called once. 26 */ 27 public interface ProgressDialogListener { 28 /** 29 * This method is called when user cancels the dialog in any way. 30 */ onCancel()31 void onCancel(); 32 } 33 34 /** 35 * Listener to receive events from timeout dialog. If the dialog is not dismissed by showing 36 * other dialog or calling {@link ConfirmSyncDataStateMachineDelegate#dismissAllDialogs}, 37 * then either {@link #onCancel} or {@link #onRetry} will be called once. 38 */ 39 public interface TimeoutDialogListener { 40 /** 41 * This method is called when user cancels the dialog in any way. 42 */ onCancel()43 void onCancel(); 44 45 /** 46 * This method is called when user clicks retry button. 47 */ onRetry()48 void onRetry(); 49 } 50 51 /** 52 * Progress Dialog that is shown while account management policy is being fetched. 53 */ 54 public static class ProgressDialogFragment extends DialogFragment { 55 private ProgressDialogListener mListener; 56 ProgressDialogFragment()57 public ProgressDialogFragment() { 58 // Fragment must have an empty public constructor 59 } 60 61 @Override onCreateDialog(Bundle savedInstanceState)62 public Dialog onCreateDialog(Bundle savedInstanceState) { 63 // If the dialog is being recreated it won't have the listener set and so won't be 64 // functional. Therefore we dismiss, and the user will need to open the dialog again. 65 if (savedInstanceState != null) { 66 dismiss(); 67 } 68 69 return new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog) 70 .setView(R.layout.signin_progress_bar_dialog) 71 .setNegativeButton(R.string.cancel, (dialog, i) -> dialog.cancel()) 72 .create(); 73 } 74 setListener(ProgressDialogListener listener)75 private void setListener(ProgressDialogListener listener) { 76 assert mListener == null; 77 mListener = listener; 78 } 79 create(ProgressDialogListener listener)80 private static ProgressDialogFragment create(ProgressDialogListener listener) { 81 ProgressDialogFragment result = new ProgressDialogFragment(); 82 result.setListener(listener); 83 return result; 84 } 85 86 @Override onCancel(DialogInterface dialog)87 public void onCancel(DialogInterface dialog) { 88 super.onCancel(dialog); 89 mListener.onCancel(); 90 } 91 } 92 93 /** 94 * Timeout Dialog that is shown if account management policy fetch times out. 95 */ 96 public static class TimeoutDialogFragment extends DialogFragment { 97 private TimeoutDialogListener mListener; 98 TimeoutDialogFragment()99 public TimeoutDialogFragment() { 100 // Fragment must have an empty public constructor 101 } 102 103 @Override onCreateDialog(Bundle savedInstanceState)104 public Dialog onCreateDialog(Bundle savedInstanceState) { 105 // If the dialog is being recreated it won't have the listener set and so won't be 106 // functional. Therefore we dismiss, and the user will need to open the dialog again. 107 if (savedInstanceState != null) { 108 dismiss(); 109 } 110 111 return new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog) 112 .setTitle(R.string.sign_in_timeout_title) 113 .setMessage(R.string.sign_in_timeout_message) 114 .setNegativeButton(R.string.cancel, (dialog, which) -> dialog.cancel()) 115 .setPositiveButton(R.string.try_again, (dialog, which) -> mListener.onRetry()) 116 .create(); 117 } 118 setListener(TimeoutDialogListener listener)119 private void setListener(TimeoutDialogListener listener) { 120 assert mListener == null; 121 mListener = listener; 122 } 123 create(TimeoutDialogListener listener)124 private static TimeoutDialogFragment create(TimeoutDialogListener listener) { 125 TimeoutDialogFragment result = new TimeoutDialogFragment(); 126 result.setListener(listener); 127 return result; 128 } 129 130 @Override onCancel(DialogInterface dialog)131 public void onCancel(DialogInterface dialog) { 132 super.onCancel(dialog); 133 mListener.onCancel(); 134 } 135 } 136 137 private static final String PROGRESS_DIALOG_TAG = "ConfirmSyncTimeoutDialog"; 138 private static final String TIMEOUT_DIALOG_TAG = "ConfirmSyncProgressDialog"; 139 private static final String CONFIRM_IMPORT_SYNC_DATA_DIALOG_TAG = "ConfirmImportSyncDataDialog"; 140 private static final String CONFIRM_MANAGED_SYNC_DATA_DIALOG_TAG = 141 "ConfirmManagedSyncDataDialog"; 142 143 private final FragmentManager mFragmentManager; 144 ConfirmSyncDataStateMachineDelegate(FragmentManager fragmentManager)145 public ConfirmSyncDataStateMachineDelegate(FragmentManager fragmentManager) { 146 mFragmentManager = fragmentManager; 147 } 148 149 /** 150 * Shows progress dialog. Will dismiss other dialogs shown, if any. 151 * 152 * @param listener The {@link ProgressDialogListener} that will be notified about user actions. 153 */ showFetchManagementPolicyProgressDialog(ProgressDialogListener listener)154 void showFetchManagementPolicyProgressDialog(ProgressDialogListener listener) { 155 dismissAllDialogs(); 156 showAllowingStateLoss(ProgressDialogFragment.create(listener), PROGRESS_DIALOG_TAG); 157 } 158 159 /** 160 * Shows timeout dialog. Will dismiss other dialogs shown, if any. 161 * 162 * @param listener The {@link TimeoutDialogListener} that will be notified about user actions. 163 */ showFetchManagementPolicyTimeoutDialog(TimeoutDialogListener listener)164 void showFetchManagementPolicyTimeoutDialog(TimeoutDialogListener listener) { 165 dismissAllDialogs(); 166 showAllowingStateLoss(TimeoutDialogFragment.create(listener), TIMEOUT_DIALOG_TAG); 167 } 168 169 /** 170 * Shows ConfirmImportSyncDataDialog that gives the user the option to 171 * merge data between the account they are attempting to sign in to and the 172 * account they were previously signed into, or to keep the data separate. 173 * 174 * @param listener Callback to be called if the user completes the dialog (as opposed to 175 * hitting cancel). 176 * @param oldAccountName The previous sync account name. 177 * @param newAccountName The potential next sync account name. 178 */ showConfirmImportSyncDataDialog(ConfirmImportSyncDataDialog.Listener listener, String oldAccountName, String newAccountName)179 void showConfirmImportSyncDataDialog(ConfirmImportSyncDataDialog.Listener listener, 180 String oldAccountName, String newAccountName) { 181 dismissAllDialogs(); 182 ConfirmImportSyncDataDialog dialog = 183 ConfirmImportSyncDataDialog.create(listener, oldAccountName, newAccountName); 184 showAllowingStateLoss(dialog, CONFIRM_IMPORT_SYNC_DATA_DIALOG_TAG); 185 } 186 187 /** 188 * Shows {@link ConfirmManagedSyncDataDialog} when signing in to a managed account 189 * (either through sign in or when switching accounts). 190 * @param listener Callback for result. 191 * @param domain The domain of the managed account. 192 */ showSignInToManagedAccountDialog( ConfirmManagedSyncDataDialog.Listener listener, String domain)193 void showSignInToManagedAccountDialog( 194 ConfirmManagedSyncDataDialog.Listener listener, String domain) { 195 dismissAllDialogs(); 196 showAllowingStateLoss(ConfirmManagedSyncDataDialog.create(listener, domain), 197 CONFIRM_MANAGED_SYNC_DATA_DIALOG_TAG); 198 } 199 showAllowingStateLoss(DialogFragment dialogFragment, String tag)200 private void showAllowingStateLoss(DialogFragment dialogFragment, String tag) { 201 FragmentTransaction transaction = mFragmentManager.beginTransaction(); 202 transaction.add(dialogFragment, tag); 203 transaction.commitAllowingStateLoss(); 204 } 205 206 /** 207 * Dismisses all dialogs. 208 */ dismissAllDialogs()209 public void dismissAllDialogs() { 210 dismissDialog(PROGRESS_DIALOG_TAG); 211 dismissDialog(TIMEOUT_DIALOG_TAG); 212 dismissDialog(CONFIRM_IMPORT_SYNC_DATA_DIALOG_TAG); 213 dismissDialog(CONFIRM_MANAGED_SYNC_DATA_DIALOG_TAG); 214 } 215 dismissDialog(String tag)216 private void dismissDialog(String tag) { 217 DialogFragment fragment = (DialogFragment) mFragmentManager.findFragmentByTag(tag); 218 if (fragment == null) return; 219 fragment.dismissAllowingStateLoss(); 220 } 221 } 222