AccountServerBaseFragment.java revision 983e1ad53b3ca3105655bf6d961713c61060a7f8
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email.activity.setup; 18 19import com.android.email.R; 20import com.android.email.Utility; 21import com.android.email.provider.EmailContent; 22import com.android.email.provider.EmailContent.Account; 23import com.android.email.provider.EmailContent.HostAuth; 24 25import android.app.Activity; 26import android.app.Fragment; 27import android.content.Context; 28import android.os.AsyncTask; 29import android.os.Bundle; 30import android.view.View; 31import android.view.View.OnClickListener; 32import android.widget.Button; 33 34/** 35 * Common base class for server settings fragments, so they can be more easily manipulated by 36 * AccountSettingsXL. Provides the following common functionality: 37 * 38 * Activity-provided callbacks 39 * Activity callback during onAttach 40 * Present "Next" button and respond to its clicks 41 */ 42public abstract class AccountServerBaseFragment extends Fragment 43 implements AccountCheckSettingsFragment.Callbacks, OnClickListener { 44 45 private final static String BUNDLE_KEY_SETTINGS = "AccountServerBaseFragment.settings"; 46 47 protected Context mContext; 48 protected Callback mCallback = EmptyCallback.INSTANCE; 49 protected boolean mSettingsMode; 50 // This is null in the setup wizard screens, and non-null in AccountSettings mode 51 public Button mProceedButton; 52 // This is used to debounce multiple clicks on the proceed button (which does async work) 53 public boolean mProceedButtonPressed; 54 55 public static Bundle sSetupModeArgs = null; 56 57 /** 58 * Callback interface that owning activities must provide 59 */ 60 public interface Callback { 61 /** 62 * Called each time the user-entered input transitions between valid and invalid 63 * @param enable true to enable proceed/next button, false to disable 64 */ 65 public void onEnableProceedButtons(boolean enable); 66 67 /** 68 * Called when user clicks "next". Starts account checker. 69 * @param checkMode values from {@link SetupData} 70 * @param target the fragment that requested the check 71 */ 72 public void onProceedNext(int checkMode, AccountServerBaseFragment target); 73 74 /** 75 * Called when account checker completes. Fragments are responsible for saving 76 * own edited data; This is primarily for the activity to do post-check navigation. 77 * @param result check settings result code - success is CHECK_SETTINGS_OK 78 * @param setupMode signals if we were editing or creating 79 */ 80 public void onCheckSettingsComplete(int result, int setupMode); 81 } 82 83 private static class EmptyCallback implements Callback { 84 public static final Callback INSTANCE = new EmptyCallback(); 85 @Override public void onEnableProceedButtons(boolean enable) { } 86 @Override public void onProceedNext(int checkMode, AccountServerBaseFragment target) { } 87 @Override public void onCheckSettingsComplete(int result, int setupMode) { } 88 } 89 90 /** 91 * Get the static arguments bundle that forces a server settings fragment into "settings" mode 92 * (If not included, you'll be in "setup" mode which behaves slightly differently.) 93 */ 94 public static synchronized Bundle getSettingsModeArgs() { 95 if (sSetupModeArgs == null) { 96 sSetupModeArgs = new Bundle(); 97 sSetupModeArgs.putBoolean(BUNDLE_KEY_SETTINGS, true); 98 } 99 return sSetupModeArgs; 100 } 101 102 /** 103 * At onCreate time, read the fragment arguments 104 */ 105 @Override 106 public void onCreate(Bundle savedInstanceState) { 107 super.onCreate(savedInstanceState); 108 109 // Get arguments, which modally switch us into "settings" mode (different appearance) 110 mSettingsMode = false; 111 if (getArguments() != null) { 112 mSettingsMode = getArguments().getBoolean(BUNDLE_KEY_SETTINGS); 113 } 114 115 mProceedButtonPressed = false; 116 } 117 118 /** 119 * Called from onCreateView, to do settings mode configuration 120 */ 121 protected void onCreateViewSettingsMode(View view) { 122 if (mSettingsMode) { 123 view.findViewById(R.id.cancel).setOnClickListener(this); 124 mProceedButton = (Button) view.findViewById(R.id.done); 125 mProceedButton.setOnClickListener(this); 126 mProceedButton.setEnabled(false); 127 } 128 } 129 130 /** 131 * Called when a fragment is first attached to its activity. 132 * {@link #onCreate(Bundle)} will be called after this. 133 */ 134 @Override 135 public void onAttach(Activity activity) { 136 super.onAttach(activity); 137 mContext = activity; 138 139 // Notify the activity that we're here. 140 if (activity instanceof AccountSettingsXL) { 141 ((AccountSettingsXL)activity).onAttach(this); 142 } 143 } 144 145 /** 146 * Implements OnClickListener 147 */ 148 @Override 149 public void onClick(View v) { 150 switch (v.getId()) { 151 case R.id.cancel: 152 getActivity().onBackPressed(); 153 break; 154 case R.id.done: 155 // Simple debounce - just ignore while checks are underway 156 if (mProceedButtonPressed) { 157 return; 158 } 159 mProceedButtonPressed = true; 160 onNext(); 161 break; 162 } 163 } 164 165 /** 166 * Activity provides callbacks here. 167 */ 168 public void setCallback(Callback callback) { 169 mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback; 170 mContext = getActivity(); 171 } 172 173 /** 174 * Enable/disable the "next" button 175 */ 176 public void enableNextButton(boolean enable) { 177 // If we are in settings "mode" we may be showing our own next button, and we'll 178 // enable it directly, here 179 if (mProceedButton != null) { 180 mProceedButton.setEnabled(enable); 181 } 182 183 // TODO: This supports the phone UX activities and will be removed 184 mCallback.onEnableProceedButtons(enable); 185 } 186 187 /** 188 * Performs async operations as part of saving changes to the settings. 189 * Check for duplicate account 190 * Display dialog if necessary 191 * Else, proceed via mCallback.onProceedNext 192 */ 193 protected void startDuplicateTaskCheck(long accountId, String checkHost, String checkLogin, 194 int checkSettingsMode) { 195 new DuplicateCheckTask(accountId, checkHost, checkLogin, checkSettingsMode).execute(); 196 } 197 198 private class DuplicateCheckTask extends AsyncTask<Void, Void, Account> { 199 200 private final long mAccountId; 201 private final String mCheckHost; 202 private final String mCheckLogin; 203 private final int mCheckSettingsMode; 204 205 public DuplicateCheckTask(long accountId, String checkHost, String checkLogin, 206 int checkSettingsMode) { 207 mAccountId = accountId; 208 mCheckHost = checkHost; 209 mCheckLogin = checkLogin; 210 mCheckSettingsMode = checkSettingsMode; 211 } 212 213 @Override 214 protected Account doInBackground(Void... params) { 215 EmailContent.Account account = Utility.findExistingAccount(mContext, mAccountId, 216 mCheckHost, mCheckLogin); 217 return account; 218 } 219 220 @Override 221 protected void onPostExecute(Account duplicateAccount) { 222 AccountServerBaseFragment fragment = AccountServerBaseFragment.this; 223 if (duplicateAccount != null) { 224 // Show duplicate account warning 225 DuplicateAccountDialogFragment dialogFragment = 226 DuplicateAccountDialogFragment.newInstance(duplicateAccount.mDisplayName); 227 dialogFragment.show(fragment.getFragmentManager(), 228 DuplicateAccountDialogFragment.TAG); 229 } else { 230 // Otherwise, proceed with the save/check 231 mCallback.onProceedNext(mCheckSettingsMode, fragment); 232 } 233 mProceedButtonPressed = false; 234 } 235 } 236 237 /** 238 * Implements AccountCheckSettingsFragment.Callbacks 239 * 240 * Handle OK or error result from check settings. Save settings (async), and then 241 * exit to previous fragment. 242 */ 243 @Override 244 public void onCheckSettingsComplete(final int settingsResult) { 245 new AsyncTask<Void, Void, Void>() { 246 @Override 247 protected Void doInBackground(Void... params) { 248 if (settingsResult == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) { 249 if (SetupData.getFlowMode() == SetupData.FLOW_MODE_EDIT) { 250 saveSettingsAfterEdit(); 251 } else { 252 saveSettingsAfterSetup(); 253 } 254 } 255 return null; 256 } 257 258 @Override 259 protected void onPostExecute(Void result) { 260 // Signal to owning activity that a settings check completed 261 mCallback.onCheckSettingsComplete(settingsResult, SetupData.getFlowMode()); 262 } 263 }.execute(); 264 } 265 266 /** 267 * Implements AccountCheckSettingsFragment.Callbacks 268 * This is overridden only by AccountSetupExchange 269 */ 270 @Override 271 public void onAutoDiscoverComplete(int result, HostAuth hostAuth) { 272 throw new IllegalStateException(); 273 } 274 275 /** 276 * Save settings after "OK" result from checker. Concrete classes must implement. 277 * This is called from a worker thread and is allowed to perform DB operations. 278 */ 279 public abstract void saveSettingsAfterEdit(); 280 281 /** 282 * Save settings after "OK" result from checker. Concrete classes must implement. 283 * This is called from a worker thread and is allowed to perform DB operations. 284 */ 285 public abstract void saveSettingsAfterSetup(); 286 287 /** 288 * Respond to a click of the "Next" button. Concrete classes must implement. 289 */ 290 public abstract void onNext(); 291} 292