1/*
2 * Copyright (C) 2011 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.contacts.util;
18
19import android.accounts.AccountManager;
20import android.accounts.AccountManagerCallback;
21import android.accounts.AccountManagerFuture;
22import android.accounts.AuthenticatorDescription;
23import android.accounts.AuthenticatorException;
24import android.accounts.OperationCanceledException;
25import android.app.Activity;
26import android.content.Context;
27import android.content.SharedPreferences;
28import android.os.Bundle;
29import android.preference.PreferenceManager;
30import android.util.Log;
31
32import com.android.contacts.R;
33import com.android.contacts.common.model.account.GoogleAccountType;
34
35import java.io.IOException;
36
37/**
38 * Utility class for controlling whether the standard "no account" prompt on launch is shown.
39 */
40public class AccountPromptUtils {
41
42    private static final String TAG = AccountPromptUtils.class.getSimpleName();
43
44    /** {@link SharedPreferences} key for whether or not the "no account" prompt should be shown. */
45    private static final String KEY_SHOW_ACCOUNT_PROMPT = "settings.showAccountPrompt";
46
47    /**
48     * The following intent keys are understood by the {@link AccountManager} and should not be
49     * changed unless the API changes.
50     */
51    private static final String KEY_INTRO_MESSAGE = "introMessage";
52    private static final String KEY_ALLOW_SKIP_ACCOUNT_SETUP = "allowSkip";
53    private static final String KEY_USER_SKIPPED_ACCOUNT_SETUP = "setupSkipped";
54
55    private static SharedPreferences getSharedPreferences(Context context) {
56        return PreferenceManager.getDefaultSharedPreferences(context);
57    }
58
59    /**
60     * Returns true if the "no account" prompt should be shown
61     * (according to {@link SharedPreferences}), otherwise return false. Since this prompt is
62     * Google-specific for the time being, this method will also return false if the Google
63     * account type is not available from the {@link AccountManager}.
64     */
65    public static boolean shouldShowAccountPrompt(Context context) {
66        // TODO: Remove the filtering of account types once there is an API in
67        // {@link AccountManager} to show a similar account prompt
68        // (see {@link AccountManager#addAccount()} in {@link #launchAccountPrompt()}
69        // for any type of account. Bug: 5375902
70        AuthenticatorDescription[] allTypes =
71                AccountManager.get(context).getAuthenticatorTypes();
72        for (AuthenticatorDescription authenticatorType : allTypes) {
73            if (GoogleAccountType.ACCOUNT_TYPE.equals(authenticatorType.type)) {
74                return getSharedPreferences(context).getBoolean(KEY_SHOW_ACCOUNT_PROMPT, true);
75            }
76        }
77        return false;
78    }
79
80    /**
81     * Remember to never show the "no account" prompt again by saving this to
82     * {@link SharedPreferences}.
83     */
84    public static void neverShowAccountPromptAgain(Context context) {
85        getSharedPreferences(context).edit()
86                .putBoolean(KEY_SHOW_ACCOUNT_PROMPT, false)
87                .apply();
88    }
89
90    /**
91     * Launch the "no account" prompt. (We assume the caller has already verified that the prompt
92     * can be shown, so checking the {@link #KEY_SHOW_ACCOUNT_PROMPT} value in
93     * {@link SharedPreferences} will not be done in this method).
94     */
95    public static void launchAccountPrompt(Activity activity) {
96        Bundle options = new Bundle();
97        options.putCharSequence(KEY_INTRO_MESSAGE, activity.getString(R.string.no_account_prompt));
98        options.putBoolean(KEY_ALLOW_SKIP_ACCOUNT_SETUP, true);
99        AccountManager.get(activity).addAccount(GoogleAccountType.ACCOUNT_TYPE, null, null, options,
100                activity, getAccountManagerCallback(activity), null);
101    }
102
103    private static AccountManagerCallback<Bundle> getAccountManagerCallback(
104            final Activity activity) {
105        return new AccountManagerCallback<Bundle>() {
106            @Override
107            public void run(AccountManagerFuture<Bundle> future) {
108                if (future.isCancelled()) {
109                    // The account creation process was canceled
110                    activity.finish();
111                    return;
112                }
113                try {
114                    Bundle result = future.getResult();
115                    if (result.getBoolean(KEY_USER_SKIPPED_ACCOUNT_SETUP)) {
116                        AccountPromptUtils.neverShowAccountPromptAgain(activity);
117                    }
118                } catch (OperationCanceledException ignore) {
119                    Log.e(TAG, "Account setup error: account creation process canceled");
120                } catch (IOException ignore) {
121                    Log.e(TAG, "Account setup error: No authenticator was registered for this"
122                            + "account type or the authenticator failed to respond");
123                } catch (AuthenticatorException ignore) {
124                    Log.e(TAG, "Account setup error: Authenticator experienced an I/O problem");
125                }
126            }
127        };
128    }
129}
130