Home | History | Annotate | Download | only in setup
      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 
     17 package com.android.email.activity.setup;
     18 
     19 import android.app.Activity;
     20 import android.app.LoaderManager;
     21 import android.content.Context;
     22 import android.content.Loader;
     23 import android.os.Bundle;
     24 import android.os.Handler;
     25 import android.view.View;
     26 import android.view.View.OnClickListener;
     27 import android.view.View.OnFocusChangeListener;
     28 import android.view.inputmethod.InputMethodManager;
     29 import android.widget.TextView;
     30 
     31 import com.android.email.R;
     32 import com.android.email.activity.UiUtilities;
     33 import com.android.emailcommon.provider.Account;
     34 import com.android.emailcommon.provider.HostAuth;
     35 
     36 /**
     37  * Common base class for server settings fragments, so they can be more easily manipulated by
     38  * AccountSettingsXL.  Provides the following common functionality:
     39  *
     40  * Activity-provided callbacks
     41  * Activity callback during onAttach
     42  * Present "Next" button and respond to its clicks
     43  */
     44 public abstract class AccountServerBaseFragment extends AccountSetupFragment
     45         implements OnClickListener {
     46 
     47     private static final String BUNDLE_KEY_SETTINGS = "AccountServerBaseFragment.settings";
     48     private static final String BUNDLE_KEY_ACTIVITY_TITLE = "AccountServerBaseFragment.title";
     49     private static final String BUNDLE_KEY_SAVING = "AccountServerBaseFragment.saving";
     50     private static final String BUNDLE_KEY_SENDAUTH = "AccountServerBaseFragment.sendAuth";
     51     private static final String BUNDLE_KEY_RECVAUTH = "AccountServerBaseFragment.recvAuth";
     52 
     53     protected Context mAppContext;
     54     /**
     55      * Whether or not we are in "settings mode". We re-use the same screens for both the initial
     56      * account creation as well as subsequent account modification. If this is
     57      * <code>false</code>, we are in account creation mode. Otherwise, we are in account
     58      * modification mode.
     59      */
     60     protected boolean mSettingsMode;
     61     protected HostAuth mLoadedSendAuth;
     62     protected HostAuth mLoadedRecvAuth;
     63 
     64     protected SetupDataFragment mSetupData;
     65 
     66     // This is null in the setup wizard screens, and non-null in AccountSettings mode
     67     private View mProceedButton;
     68     protected String mBaseScheme = "protocol";
     69 
     70     // Set to true if we're in the process of saving
     71     private boolean mSaving;
     72 
     73     /**
     74      // Used to post the callback once we're done saving, since we can't perform fragment
     75      // transactions from {@link LoaderManager.LoaderCallbacks#onLoadFinished(Loader, Object)}
     76      */
     77     private Handler mHandler = new Handler();
     78 
     79     /**
     80      * Callback interface that owning activities must provide
     81      */
     82     public interface Callback extends AccountSetupFragment.Callback {
     83         /**
     84          * Called when user clicks "next".  Starts account checker.
     85          * @param checkMode values from {@link SetupDataFragment}
     86          */
     87         public void onAccountServerUIComplete(int checkMode);
     88         public void onAccountServerSaveComplete();
     89     }
     90 
     91     /**
     92      * Creates and returns a bundle of arguments in the format we expect
     93      *
     94      * @param settingsMode True if we're in settings, false if we're in account creation
     95      * @return Arg bundle
     96      */
     97     public static Bundle getArgs(boolean settingsMode) {
     98         final Bundle setupModeArgs = new Bundle(1);
     99         setupModeArgs.putBoolean(BUNDLE_KEY_SETTINGS, settingsMode);
    100         return setupModeArgs;
    101     }
    102 
    103     public AccountServerBaseFragment() {}
    104 
    105     /**
    106      * At onCreate time, read the fragment arguments
    107      */
    108     @Override
    109     public void onCreate(Bundle savedInstanceState) {
    110         super.onCreate(savedInstanceState);
    111 
    112         // Get arguments, which modally switch us into "settings" mode (different appearance)
    113         mSettingsMode = false;
    114         if (savedInstanceState != null) {
    115             mSettingsMode = savedInstanceState.getBoolean(BUNDLE_KEY_SETTINGS);
    116             mSaving = savedInstanceState.getBoolean(BUNDLE_KEY_SAVING);
    117             mLoadedSendAuth = savedInstanceState.getParcelable(BUNDLE_KEY_SENDAUTH);
    118             mLoadedRecvAuth = savedInstanceState.getParcelable(BUNDLE_KEY_RECVAUTH);
    119         } else if (getArguments() != null) {
    120             mSettingsMode = getArguments().getBoolean(BUNDLE_KEY_SETTINGS);
    121         }
    122         setHasOptionsMenu(true);
    123     }
    124 
    125     /**
    126      * Called from onCreateView, to do settings mode configuration
    127      */
    128     protected void onCreateViewSettingsMode(View view) {
    129         if (mSettingsMode) {
    130             UiUtilities.getView(view, R.id.cancel).setOnClickListener(this);
    131             mProceedButton = UiUtilities.getView(view, R.id.done);
    132             mProceedButton.setOnClickListener(this);
    133             mProceedButton.setEnabled(false);
    134         }
    135     }
    136 
    137     @Override
    138     public void onActivityCreated(Bundle savedInstanceState) {
    139         final Activity activity = getActivity();
    140         mAppContext = activity.getApplicationContext();
    141         if (mSettingsMode && savedInstanceState != null) {
    142             // startPreferencePanel launches this fragment with the right title initially, but
    143             // if the device is rotated we must set the title ourselves
    144             activity.setTitle(savedInstanceState.getString(BUNDLE_KEY_ACTIVITY_TITLE));
    145         }
    146         SetupDataFragment.SetupDataContainer container =
    147                 (SetupDataFragment.SetupDataContainer) activity;
    148         mSetupData = container.getSetupData();
    149 
    150         super.onActivityCreated(savedInstanceState);
    151     }
    152 
    153     @Override
    154     public void onResume() {
    155         super.onResume();
    156         if (mSaving) {
    157             // We need to call this here in case the save completed while we weren't resumed
    158             saveSettings();
    159         }
    160     }
    161 
    162     @Override
    163     public void onSaveInstanceState(Bundle outState) {
    164         super.onSaveInstanceState(outState);
    165         outState.putString(BUNDLE_KEY_ACTIVITY_TITLE, (String) getActivity().getTitle());
    166         outState.putBoolean(BUNDLE_KEY_SETTINGS, mSettingsMode);
    167         outState.putParcelable(BUNDLE_KEY_SENDAUTH, mLoadedSendAuth);
    168         outState.putParcelable(BUNDLE_KEY_RECVAUTH, mLoadedRecvAuth);
    169     }
    170 
    171     @Override
    172     public void onPause() {
    173         // Hide the soft keyboard if we lose focus
    174         final InputMethodManager imm =
    175                 (InputMethodManager) mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE);
    176         imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
    177         super.onPause();
    178     }
    179 
    180     /**
    181      * Implements OnClickListener
    182      */
    183     @Override
    184     public void onClick(View v) {
    185         final int viewId = v.getId();
    186         if (viewId == R.id.cancel) {
    187             collectUserInputInternal();
    188             getActivity().onBackPressed();
    189         } else if (viewId == R.id.done) {
    190             collectUserInput();
    191         } else {
    192             super.onClick(v);
    193         }
    194     }
    195 
    196     /**
    197      * Enable/disable the "next" button
    198      */
    199     public void enableNextButton(boolean enable) {
    200         // If we are in settings "mode" we may be showing our own next button, and we'll
    201         // enable it directly, here
    202         if (mProceedButton != null) {
    203             mProceedButton.setEnabled(enable);
    204         } else {
    205             setNextButtonEnabled(enable);
    206         }
    207     }
    208 
    209     /**
    210      * Make the given text view uneditable. If the text view is ever focused, the specified
    211      * error message will be displayed.
    212      */
    213     protected void makeTextViewUneditable(final TextView view, final String errorMessage) {
    214         // We're editing an existing account; don't allow modification of the user name
    215         if (mSettingsMode) {
    216             view.setKeyListener(null);
    217             view.setFocusable(true);
    218             view.setOnFocusChangeListener(new OnFocusChangeListener() {
    219                 @Override
    220                 public void onFocusChange(View v, boolean hasFocus) {
    221                     if (hasFocus) {
    222                         // Framework will not auto-hide IME; do it ourselves
    223                         InputMethodManager imm = (InputMethodManager) mAppContext.
    224                                 getSystemService(Context.INPUT_METHOD_SERVICE);
    225                         imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
    226                         view.setError(errorMessage);
    227                     } else {
    228                         view.setError(null);
    229                     }
    230                 }
    231             });
    232             view.setOnClickListener(new OnClickListener() {
    233                 @Override
    234                 public void onClick(View v) {
    235                     if (view.getError() == null) {
    236                         view.setError(errorMessage);
    237                     } else {
    238                         view.setError(null);
    239                     }
    240                 }
    241             });
    242         }
    243     }
    244 
    245     /**
    246      * Returns whether or not any settings have changed.
    247      */
    248     public boolean haveSettingsChanged() {
    249         collectUserInputInternal();
    250         final Account account = mSetupData.getAccount();
    251 
    252         final HostAuth sendAuth = account.getOrCreateHostAuthSend(mAppContext);
    253         final boolean sendChanged = (mLoadedSendAuth != null && !mLoadedSendAuth.equals(sendAuth));
    254 
    255         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mAppContext);
    256         final boolean recvChanged = (mLoadedRecvAuth != null && !mLoadedRecvAuth.equals(recvAuth));
    257 
    258         return sendChanged || recvChanged;
    259     }
    260 
    261     public void saveSettings() {
    262         getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Boolean>() {
    263             @Override
    264             public Loader<Boolean> onCreateLoader(int id, Bundle args) {
    265                 return getSaveSettingsLoader();
    266             }
    267 
    268             @Override
    269             public void onLoadFinished(Loader<Boolean> loader, Boolean data) {
    270                 mHandler.post(new Runnable() {
    271                     @Override
    272                     public void run() {
    273                         if (isResumed()) {
    274                             final Callback callback = (Callback) getActivity();
    275                             callback.onAccountServerSaveComplete();
    276                         }
    277                     }
    278                 });
    279             }
    280 
    281             @Override
    282             public void onLoaderReset(Loader<Boolean> loader) {}
    283         });
    284     }
    285 
    286     public abstract Loader<Boolean> getSaveSettingsLoader();
    287 
    288     /**
    289      * Collect the user's input into the setup data object.  Concrete classes must implement.
    290      */
    291     public abstract int collectUserInputInternal();
    292 
    293     public void collectUserInput() {
    294         final int phase = collectUserInputInternal();
    295         final Callback callback = (Callback) getActivity();
    296         callback.onAccountServerUIComplete(phase);
    297     }
    298 }
    299