Use standardized no account screen on first launch

- If there are no accounts on the device, then launch
an intent to show the prompt (check that there is no
flag in SharedPreferences preventing the prompt from
being shown).

- Only when the user explicitly selects "Not now" from
the no account screen, do we store a flag that says
we should never show the prompt again.

- Add "Manage accounts" permission in order to
perform AccountManager.addAccount, which gives us access
to the "no account" screen.

Bug: 5250360
Change-Id: I2f925c838bc0e7003a8dbb3280e5a22ed8680670
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a1829d1..f4dcdf6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -23,6 +23,7 @@
     <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 332a953..d2759dc 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -53,6 +53,7 @@
 import com.android.contacts.model.AccountWithDataSet;
 import com.android.contacts.preference.ContactsPreferenceActivity;
 import com.android.contacts.preference.DisplayOptionsPreferenceFragment;
+import com.android.contacts.util.AccountPromptUtils;
 import com.android.contacts.util.AccountSelectionUtil;
 import com.android.contacts.util.AccountsListAdapter;
 import com.android.contacts.util.Constants;
@@ -918,6 +919,16 @@
                 mAllFragment.setEnabled(true);
             }
         } else {
+            // If there are no accounts on the device and we should show the "no account" prompt
+            // (based on {@link SharedPreferences}), then launch the account setup activity so the
+            // user can sign-in or create an account.
+            if (!areAccountsAvailable() && AccountPromptUtils.shouldShowAccountPrompt(this)) {
+                AccountPromptUtils.launchAccountPrompt(this);
+                return;
+            }
+
+            // Otherwise, continue setting up the page so that the user can still use the app
+            // without an account.
             if (mAllFragment != null) {
                 mAllFragment.setEnabled(false);
             }
diff --git a/src/com/android/contacts/util/AccountPromptUtils.java b/src/com/android/contacts/util/AccountPromptUtils.java
new file mode 100644
index 0000000..58865d0
--- /dev/null
+++ b/src/com/android/contacts/util/AccountPromptUtils.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.util;
+
+import com.android.contacts.R;
+import com.android.contacts.model.GoogleAccountType;
+
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Utility class for controlling whether the standard "no account" prompt on launch is shown.
+ */
+public class AccountPromptUtils {
+
+    private static final String TAG = AccountPromptUtils.class.getSimpleName();
+
+    /** {@link SharedPreferences} key for whether or not the "no account" prompt should be shown. */
+    private static final String KEY_SHOW_ACCOUNT_PROMPT = "settings.showAccountPrompt";
+
+    /**
+     * The following intent keys are understood by the {@link AccountManager} and should not be
+     * changed unless the API changes.
+     */
+    private static final String KEY_INTRO_MESSAGE = "introMessage";
+    private static final String KEY_ALLOW_SKIP_ACCOUNT_SETUP = "allowSkip";
+    private static final String KEY_USER_SKIPPED_ACCOUNT_SETUP = "setupSkipped";
+
+    private static SharedPreferences getSharedPreferences(Context context) {
+        return PreferenceManager.getDefaultSharedPreferences(context);
+    }
+
+    /**
+     * Returns true if the "no account" prompt should be shown
+     * (according to {@link SharedPreferences}), otherwise return false.
+     */
+    public static boolean shouldShowAccountPrompt(Context context) {
+        return getSharedPreferences(context).getBoolean(KEY_SHOW_ACCOUNT_PROMPT, true);
+    }
+
+    /**
+     * Remember to never show the "no account" prompt again by saving this to
+     * {@link SharedPreferences}.
+     */
+    public static void neverShowAccountPromptAgain(Context context) {
+        getSharedPreferences(context).edit()
+                .putBoolean(KEY_SHOW_ACCOUNT_PROMPT, false)
+                .apply();
+    }
+
+    /**
+     * Launch the "no account" prompt. (We assume the caller has already verified that the prompt
+     * can be shown, so checking the {@link #KEY_SHOW_ACCOUNT_PROMPT} value in
+     * {@link SharedPreferences} will not be done in this method).
+     */
+    public static void launchAccountPrompt(Activity activity) {
+        Bundle options = new Bundle();
+        options.putCharSequence(KEY_INTRO_MESSAGE, activity.getString(R.string.no_account_prompt));
+        options.putBoolean(KEY_ALLOW_SKIP_ACCOUNT_SETUP, true);
+        AccountManager.get(activity).addAccount(GoogleAccountType.ACCOUNT_TYPE, null, null, options,
+                activity, getAccountManagerCallback(activity), null);
+    }
+
+    private static AccountManagerCallback<Bundle> getAccountManagerCallback(
+            final Activity activity) {
+        return new AccountManagerCallback<Bundle>() {
+            @Override
+            public void run(AccountManagerFuture<Bundle> future) {
+                if (future.isCancelled()) {
+                    // The account creation process was canceled
+                    activity.finish();
+                    return;
+                }
+                try {
+                    Bundle result = future.getResult();
+                    if (result.getBoolean(KEY_USER_SKIPPED_ACCOUNT_SETUP)) {
+                        AccountPromptUtils.neverShowAccountPromptAgain(activity);
+                    }
+                } catch (OperationCanceledException ignore) {
+                    Log.e(TAG, "Account setup error: account creation process canceled");
+                } catch (IOException ignore) {
+                    Log.e(TAG, "Account setup error: No authenticator was registered for this"
+                            + "account type or the authenticator failed to respond");
+                } catch (AuthenticatorException ignore) {
+                    Log.e(TAG, "Account setup error: Authenticator experienced an I/O problem");
+                }
+            }
+        };
+    }
+}