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