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