AccountSetupBasics.java revision 6175300474f01460523ccb3efc9f93722cadf2e0
1/*
2 * Copyright (C) 2008 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.Email;
20import com.android.email.R;
21import com.android.email.Utility;
22import com.android.email.VendorPolicyLoader;
23import com.android.email.activity.ActivityHelper;
24import com.android.email.activity.Welcome;
25import com.android.email.provider.EmailContent.Account;
26import com.android.email.provider.EmailContent.HostAuth;
27
28import android.accounts.AccountAuthenticatorResponse;
29import android.accounts.AccountManager;
30import android.app.Activity;
31import android.app.ActivityManager;
32import android.app.FragmentTransaction;
33import android.content.Context;
34import android.content.Intent;
35import android.os.Bundle;
36import android.text.TextUtils;
37import android.util.Log;
38import android.view.View;
39import android.view.View.OnClickListener;
40import android.widget.Button;
41
42/**
43 * Prompts the user for the email address and password. Also prompts for "Use this account as
44 * default" if this is the 2nd+ account being set up.
45 *
46 * If the domain is well-known, the account is configured fully and checked immediately
47 * using AccountCheckSettingsFragment.  If this succeeds we proceed directly to AccountSetupOptions.
48 *
49 * If the domain is not known, or the user selects Manual setup, we invoke the
50 * AccountSetupAccountType activity where the user can begin to manually configure the account.
51 *
52 * === Support for automated testing ==
53 * This activity can also be launched directly via ACTION_CREATE_ACCOUNT.  This is intended
54 * only for use by continuous test systems, and is currently only available when "ro.monkey"
55 * is set.  To use this mode, you must construct an intent which contains all necessary information
56 * to create the account.  No connection checking is done, so the account may or may not actually
57 * work.  Here is a sample command, for a gmail account "test_account" with a password of
58 * "test_password".
59 *
60 *      $ adb shell am start -a com.android.email.CREATE_ACCOUNT \
61 *          -e EMAIL test_account@gmail.com \
62 *          -e USER "Test Account Name" \
63 *          -e INCOMING imap+ssl+://test_account:test_password@imap.gmail.com \
64 *          -e OUTGOING smtp+ssl+://test_account:test_password@smtp.gmail.com
65 *
66 * Note: For accounts that require the full email address in the login, encode the @ as %40.
67 * Note: Exchange accounts that require device security policies cannot be created automatically.
68 */
69public class AccountSetupBasics extends AccountSetupActivity
70        implements AccountSetupBasicsFragment.Callback, AccountCheckSettingsFragment.Callbacks,
71        OnClickListener {
72
73    /**
74     * Direct access for forcing account creation
75     * For use by continuous automated test system (e.g. in conjunction with monkey tests)
76     */
77    private final String ACTION_CREATE_ACCOUNT = "com.android.email.CREATE_ACCOUNT";
78    private final String EXTRA_CREATE_ACCOUNT_EMAIL = "EMAIL";
79    private final String EXTRA_CREATE_ACCOUNT_USER = "USER";
80    private final String EXTRA_CREATE_ACCOUNT_INCOMING = "INCOMING";
81    private final String EXTRA_CREATE_ACCOUNT_OUTGOING = "OUTGOING";
82    private final Boolean DEBUG_ALLOW_NON_MONKEY_CREATION = true;  // STOPSHIP - must be FALSE
83
84    private AccountSetupBasicsFragment mFragment;
85    private boolean mManualButtonDisplayed;
86    private boolean mNextButtonEnabled;
87    private Button mManualButton;
88    private Button mNextButton;
89
90    // Used when this Activity is called as part of account authentification flow,
91    // which requires to do extra work before and after the account creation.
92    // See also AccountAuthenticatorActivity.
93    private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
94    private Bundle mResultBundle = null;
95
96    public static void actionNewAccount(Activity fromActivity) {
97        SetupData.init(SetupData.FLOW_MODE_NORMAL);
98        fromActivity.startActivity(new Intent(fromActivity, AccountSetupBasics.class));
99    }
100
101    /**
102     * This generates setup data that can be used to start a self-contained account creation flow
103     * for exchange accounts.
104     */
105    public static Intent actionSetupExchangeIntent(Context context) {
106        SetupData.init(SetupData.FLOW_MODE_ACCOUNT_MANAGER_EAS);
107        return new Intent(context, AccountSetupBasics.class);
108    }
109
110    /**
111     * This generates setup data that can be used to start a self-contained account creation flow
112     * for pop/imap accounts.
113     */
114    public static Intent actionSetupPopImapIntent(Context context) {
115        SetupData.init(SetupData.FLOW_MODE_ACCOUNT_MANAGER_POP_IMAP);
116        return new Intent(context, AccountSetupBasics.class);
117    }
118
119    public static void actionAccountCreateFinishedAccountFlow(Activity fromActivity) {
120        Intent i= new Intent(fromActivity, AccountSetupBasics.class);
121        // If we're in the "account flow" (from AccountManager), we want to return to the caller
122        // (in the settings app)
123        SetupData.init(SetupData.FLOW_MODE_RETURN_TO_CALLER);
124        i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
125        fromActivity.startActivity(i);
126    }
127
128    public static void actionAccountCreateFinished(final Activity fromActivity,
129            final long accountId) {
130        Utility.runAsync(new Runnable() {
131           public void run() {
132               Intent i = new Intent(fromActivity, AccountSetupBasics.class);
133               // If we're not in the "account flow" (from AccountManager), we want to show the
134               // message list for the new inbox
135               Account account = Account.restoreAccountWithId(fromActivity, accountId);
136               SetupData.init(SetupData.FLOW_MODE_RETURN_TO_MESSAGE_LIST, account);
137               i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
138               fromActivity.startActivity(i);
139            }});
140    }
141
142    @Override
143    public void onCreate(Bundle savedInstanceState) {
144        super.onCreate(savedInstanceState);
145        ActivityHelper.debugSetWindowFlags(this);
146
147        // Check for forced account creation first, as it comes from an externally-generated
148        // intent and won't have any SetupData prepared.
149        String action = getIntent().getAction();
150        if (ACTION_CREATE_ACCOUNT.equals(action)) {
151            SetupData.init(SetupData.FLOW_MODE_FORCE_CREATE);
152        }
153
154        int flowMode = SetupData.getFlowMode();
155        if (flowMode == SetupData.FLOW_MODE_RETURN_TO_CALLER) {
156            // Return to the caller who initiated account creation
157            finish();
158            return;
159        } else if (flowMode == SetupData.FLOW_MODE_RETURN_TO_MESSAGE_LIST) {
160            Account account = SetupData.getAccount();
161            if (account != null && account.mId >= 0) {
162                // Show the message list for the new account
163                Welcome.actionOpenAccountInbox(this, account.mId);
164                finish();
165                return;
166            }
167        }
168
169        setContentView(R.layout.account_setup_basics);
170
171        mFragment = (AccountSetupBasicsFragment)
172                getFragmentManager().findFragmentById(R.id.setup_basics_fragment);
173        mManualButtonDisplayed = true;
174        boolean alternateStrings = false;
175        if (flowMode == SetupData.FLOW_MODE_ACCOUNT_MANAGER_EAS) {
176            // No need for manual button -> next is appropriate
177            mManualButtonDisplayed = false;
178            // Swap welcome text for EAS-specific text
179            alternateStrings = VendorPolicyLoader.getInstance(this).useAlternateExchangeStrings();
180            setTitle(alternateStrings
181                    ? R.string.account_setup_basics_exchange_title_alternate
182                            : R.string.account_setup_basics_exchange_title);
183        }
184
185        // Configure fragment
186        mFragment.setCallback(this, alternateStrings);
187
188        // Configure buttons
189        mManualButton = (Button) findViewById(R.id.manual_setup);
190        mNextButton = (Button) findViewById(R.id.next);
191        mManualButton.setVisibility(mManualButtonDisplayed ? View.VISIBLE : View.INVISIBLE);
192        mManualButton.setOnClickListener(this);
193        mNextButton.setOnClickListener(this);
194        // Force disabled until fragment notifies otherwise
195        mNextButtonEnabled = true;
196        this.onEnableProceedButtons(false);
197
198        mAccountAuthenticatorResponse =
199            getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
200
201        if (mAccountAuthenticatorResponse != null) {
202            mAccountAuthenticatorResponse.onRequestContinued();
203        }
204
205        // Handle force account creation immediately (now that fragment is set up)
206        // This is never allowed in a normal user build and will exit immediately.
207        if (SetupData.getFlowMode() == SetupData.FLOW_MODE_FORCE_CREATE) {
208            if (!DEBUG_ALLOW_NON_MONKEY_CREATION && !ActivityManager.isUserAMonkey()) {
209                Log.e(Email.LOG_TAG, "ERROR: Force account create only allowed for monkeys");
210                finish();
211                return;
212            }
213            Intent intent = getIntent();
214            String email = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_EMAIL);
215            String user = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_USER);
216            String incoming = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_INCOMING);
217            String outgoing = intent.getStringExtra(EXTRA_CREATE_ACCOUNT_OUTGOING);
218            if (TextUtils.isEmpty(email) || TextUtils.isEmpty(user) ||
219                    TextUtils.isEmpty(incoming) || TextUtils.isEmpty(outgoing)) {
220                Log.e(Email.LOG_TAG, "ERROR: Force account create requires extras EMAIL, USER, " +
221                        "INCOMING, OUTGOING");
222                finish();
223                return;
224            }
225            mFragment.forceCreateAccount(email, user, incoming, outgoing);
226            onCheckSettingsComplete(AccountCheckSettingsFragment.CHECK_SETTINGS_OK); // calls finish
227            return;
228        }
229    }
230
231    @Override
232    public void finish() {
233        if (mAccountAuthenticatorResponse != null) {
234            // send the result bundle back if set, otherwise send an error.
235            if (mResultBundle != null) {
236                mAccountAuthenticatorResponse.onResult(mResultBundle);
237            } else {
238                mAccountAuthenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED,
239                        "canceled");
240            }
241            mAccountAuthenticatorResponse = null;
242        }
243        super.finish();
244    }
245
246    /**
247     * Implements AccountCheckSettingsFragment.Callbacks
248     *
249     * This is used in automatic setup mode to jump directly down to the options screen.
250     */
251    @Override
252    public void onCheckSettingsComplete(int result) {
253        if (result == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) {
254            AccountSetupOptions.actionOptions(this);
255            finish();
256        }
257    }
258
259    /**
260     * Implements AccountCheckSettingsFragment.Callbacks
261     * This is overridden only by AccountSetupExchange
262     */
263    @Override
264    public void onAutoDiscoverComplete(int result, HostAuth hostAuth) {
265        throw new IllegalStateException();
266    }
267
268    /**
269     * Implements OnClickListener
270     */
271    @Override
272    public void onClick(View v) {
273        switch (v.getId()) {
274            case R.id.next:
275                mFragment.onNext();
276                break;
277            case R.id.manual_setup:
278                // no AutoDiscover - user clicked "manual"
279                mFragment.onManualSetup(false);
280                break;
281        }
282    }
283
284    /**
285     * Implements AccountSetupBasicsFragment.Callback
286     */
287    @Override
288    public void onEnableProceedButtons(boolean enabled) {
289        boolean wasEnabled = mNextButtonEnabled;
290        mNextButtonEnabled = enabled;
291
292        if (enabled != wasEnabled) {
293            mManualButton.setEnabled(enabled);
294            mNextButton.setEnabled(enabled);
295        }
296    }
297
298    /**
299     * Implements AccountSetupBasicsFragment.Callback
300     *
301     * This is called when auto-setup (from hardcoded server info) is attempted.
302     * Replace the name/password fragment with the account checker, which will begin to
303     * check incoming/outgoing.
304     */
305    @Override
306    public void onProceedAutomatic() {
307        AccountCheckSettingsFragment checkerFragment =
308            AccountCheckSettingsFragment.newInstance(
309                    SetupData.CHECK_INCOMING | SetupData.CHECK_OUTGOING, null);
310        FragmentTransaction transaction = getFragmentManager().openTransaction();
311        transaction.replace(R.id.setup_basics_fragment, checkerFragment);
312        transaction.addToBackStack("back");
313        transaction.commit();
314    }
315
316    /**
317     * Implements AccountSetupBasicsFragment.Callback
318     */
319    @Override
320    public void onProceedDebugSettings() {
321        AccountSettingsXL.actionSettingsWithDebug(this);
322    }
323
324    /**
325     * Implements AccountSetupBasicsFragment.Callback
326     */
327    @Override
328    public void onProceedManual(boolean allowAutoDiscover) {
329        SetupData.setAllowAutodiscover(allowAutoDiscover);
330        AccountSetupAccountType.actionSelectAccountType(this);
331    }
332}
333