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