AccountServerBaseFragment.java revision 31d9acbf0623872f9d4a2b3210b5970854b654c7
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.emailcommon.provider.EmailContent; 21import com.android.emailcommon.provider.EmailContent.Account; 22import com.android.emailcommon.provider.EmailContent.HostAuth; 23import com.android.emailcommon.utility.Utility; 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 34import java.net.URI; 35import java.net.URISyntaxException; 36 37/** 38 * Common base class for server settings fragments, so they can be more easily manipulated by 39 * AccountSettingsXL. Provides the following common functionality: 40 * 41 * Activity-provided callbacks 42 * Activity callback during onAttach 43 * Present "Next" button and respond to its clicks 44 */ 45public abstract class AccountServerBaseFragment extends Fragment 46 implements AccountCheckSettingsFragment.Callbacks, OnClickListener { 47 48 public static Bundle sSetupModeArgs = null; 49 protected static URI sDefaultUri; 50 51 private final static String BUNDLE_KEY_SETTINGS = "AccountServerBaseFragment.settings"; 52 53 protected Context mContext; 54 protected Callback mCallback = EmptyCallback.INSTANCE; 55 protected boolean mSettingsMode; 56 // The URI that represents this account's currently saved settings 57 protected URI mLoadedUri; 58 59 // This is null in the setup wizard screens, and non-null in AccountSettings mode 60 public Button mProceedButton; 61 // This is used to debounce multiple clicks on the proceed button (which does async work) 62 public boolean mProceedButtonPressed; 63 64 /** 65 * Callback interface that owning activities must provide 66 */ 67 public interface Callback { 68 /** 69 * Called each time the user-entered input transitions between valid and invalid 70 * @param enable true to enable proceed/next button, false to disable 71 */ 72 public void onEnableProceedButtons(boolean enable); 73 74 /** 75 * Called when user clicks "next". Starts account checker. 76 * @param checkMode values from {@link SetupData} 77 * @param target the fragment that requested the check 78 */ 79 public void onProceedNext(int checkMode, AccountServerBaseFragment target); 80 81 /** 82 * Called when account checker completes. Fragments are responsible for saving 83 * own edited data; This is primarily for the activity to do post-check navigation. 84 * @param result check settings result code - success is CHECK_SETTINGS_OK 85 * @param setupMode signals if we were editing or creating 86 */ 87 public void onCheckSettingsComplete(int result, int setupMode); 88 } 89 90 private static class EmptyCallback implements Callback { 91 public static final Callback INSTANCE = new EmptyCallback(); 92 @Override public void onEnableProceedButtons(boolean enable) { } 93 @Override public void onProceedNext(int checkMode, AccountServerBaseFragment target) { } 94 @Override public void onCheckSettingsComplete(int result, int setupMode) { } 95 } 96 97 /** 98 * Get the static arguments bundle that forces a server settings fragment into "settings" mode 99 * (If not included, you'll be in "setup" mode which behaves slightly differently.) 100 */ 101 public static synchronized Bundle getSettingsModeArgs() { 102 if (sSetupModeArgs == null) { 103 sSetupModeArgs = new Bundle(); 104 sSetupModeArgs.putBoolean(BUNDLE_KEY_SETTINGS, true); 105 } 106 return sSetupModeArgs; 107 } 108 109 public AccountServerBaseFragment() { 110 if (sDefaultUri == null) { 111 try { 112 sDefaultUri = new URI(""); 113 } catch (URISyntaxException ignore) { 114 // ignore; will never happen 115 } 116 } 117 } 118 119 /** 120 * At onCreate time, read the fragment arguments 121 */ 122 @Override 123 public void onCreate(Bundle savedInstanceState) { 124 super.onCreate(savedInstanceState); 125 126 // Get arguments, which modally switch us into "settings" mode (different appearance) 127 mSettingsMode = false; 128 if (getArguments() != null) { 129 mSettingsMode = getArguments().getBoolean(BUNDLE_KEY_SETTINGS); 130 } 131 132 mProceedButtonPressed = false; 133 } 134 135 /** 136 * Called from onCreateView, to do settings mode configuration 137 */ 138 protected void onCreateViewSettingsMode(View view) { 139 if (mSettingsMode) { 140 view.findViewById(R.id.cancel).setOnClickListener(this); 141 mProceedButton = (Button) view.findViewById(R.id.done); 142 mProceedButton.setOnClickListener(this); 143 mProceedButton.setEnabled(false); 144 } 145 } 146 147 /** 148 * Called when a fragment is first attached to its activity. 149 * {@link #onCreate(Bundle)} will be called after this. 150 */ 151 @Override 152 public void onAttach(Activity activity) { 153 super.onAttach(activity); 154 mContext = activity; 155 156 // Notify the activity that we're here. 157 if (activity instanceof AccountSettingsXL) { 158 ((AccountSettingsXL)activity).onAttach(this); 159 } 160 } 161 162 /** 163 * Implements OnClickListener 164 */ 165 @Override 166 public void onClick(View v) { 167 switch (v.getId()) { 168 case R.id.cancel: 169 getActivity().onBackPressed(); 170 break; 171 case R.id.done: 172 // Simple debounce - just ignore while checks are underway 173 if (mProceedButtonPressed) { 174 return; 175 } 176 mProceedButtonPressed = true; 177 onNext(); 178 break; 179 } 180 } 181 182 /** 183 * Activity provides callbacks here. 184 */ 185 public void setCallback(Callback callback) { 186 mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback; 187 mContext = getActivity(); 188 } 189 190 /** 191 * Enable/disable the "next" button 192 */ 193 public void enableNextButton(boolean enable) { 194 // If we are in settings "mode" we may be showing our own next button, and we'll 195 // enable it directly, here 196 if (mProceedButton != null) { 197 mProceedButton.setEnabled(enable); 198 } 199 200 // TODO: This supports the phone UX activities and will be removed 201 mCallback.onEnableProceedButtons(enable); 202 } 203 204 /** 205 * Performs async operations as part of saving changes to the settings. 206 * Check for duplicate account 207 * Display dialog if necessary 208 * Else, proceed via mCallback.onProceedNext 209 */ 210 protected void startDuplicateTaskCheck(long accountId, String checkHost, String checkLogin, 211 int checkSettingsMode) { 212 new DuplicateCheckTask(accountId, checkHost, checkLogin, checkSettingsMode).execute(); 213 } 214 215 private class DuplicateCheckTask extends AsyncTask<Void, Void, Account> { 216 217 private final long mAccountId; 218 private final String mCheckHost; 219 private final String mCheckLogin; 220 private final int mCheckSettingsMode; 221 222 public DuplicateCheckTask(long accountId, String checkHost, String checkLogin, 223 int checkSettingsMode) { 224 mAccountId = accountId; 225 mCheckHost = checkHost; 226 mCheckLogin = checkLogin; 227 mCheckSettingsMode = checkSettingsMode; 228 } 229 230 @Override 231 protected Account doInBackground(Void... params) { 232 EmailContent.Account account = Utility.findExistingAccount(mContext, mAccountId, 233 mCheckHost, mCheckLogin); 234 return account; 235 } 236 237 @Override 238 protected void onPostExecute(Account duplicateAccount) { 239 AccountServerBaseFragment fragment = AccountServerBaseFragment.this; 240 if (duplicateAccount != null) { 241 // Show duplicate account warning 242 DuplicateAccountDialogFragment dialogFragment = 243 DuplicateAccountDialogFragment.newInstance(duplicateAccount.mDisplayName); 244 dialogFragment.show(fragment.getFragmentManager(), 245 DuplicateAccountDialogFragment.TAG); 246 } else { 247 // Otherwise, proceed with the save/check 248 mCallback.onProceedNext(mCheckSettingsMode, fragment); 249 } 250 mProceedButtonPressed = false; 251 } 252 } 253 254 /** 255 * Implements AccountCheckSettingsFragment.Callbacks 256 * 257 * Handle OK or error result from check settings. Save settings (async), and then 258 * exit to previous fragment. 259 */ 260 @Override 261 public void onCheckSettingsComplete(final int settingsResult) { 262 new AsyncTask<Void, Void, Void>() { 263 @Override 264 protected Void doInBackground(Void... params) { 265 if (settingsResult == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) { 266 if (SetupData.getFlowMode() == SetupData.FLOW_MODE_EDIT) { 267 saveSettingsAfterEdit(); 268 } else { 269 saveSettingsAfterSetup(); 270 } 271 } 272 return null; 273 } 274 275 @Override 276 protected void onPostExecute(Void result) { 277 // Signal to owning activity that a settings check completed 278 mCallback.onCheckSettingsComplete(settingsResult, SetupData.getFlowMode()); 279 } 280 }.execute(); 281 } 282 283 /** 284 * Implements AccountCheckSettingsFragment.Callbacks 285 * This is overridden only by AccountSetupExchange 286 */ 287 @Override 288 public void onAutoDiscoverComplete(int result, HostAuth hostAuth) { 289 throw new IllegalStateException(); 290 } 291 292 /** 293 * Returns whether or not any settings have changed. 294 */ 295 public boolean haveSettingsChanged() { 296 URI newUri = null; 297 298 try { 299 newUri = getUri(); 300 } catch (URISyntaxException ignore) { 301 // ignore 302 } 303 304 return (mLoadedUri == null) || !mLoadedUri.equals(newUri); 305 } 306 307 /** 308 * Save settings after "OK" result from checker. Concrete classes must implement. 309 * This is called from a worker thread and is allowed to perform DB operations. 310 */ 311 public abstract void saveSettingsAfterEdit(); 312 313 /** 314 * Save settings after "OK" result from checker. Concrete classes must implement. 315 * This is called from a worker thread and is allowed to perform DB operations. 316 */ 317 public abstract void saveSettingsAfterSetup(); 318 319 /** 320 * Respond to a click of the "Next" button. Concrete classes must implement. 321 */ 322 public abstract void onNext(); 323 324 protected abstract URI getUri() throws URISyntaxException; 325 326} 327