1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.sync.signin;
6
7import android.accounts.Account;
8import android.content.Context;
9import android.os.AsyncTask;
10import android.preference.PreferenceManager;
11import android.util.Log;
12
13import com.google.common.annotations.VisibleForTesting;
14import com.google.ipc.invalidation.external.client.contrib.MultiplexingGcmListener;
15
16import org.chromium.base.ObserverList;
17
18public class ChromeSigninController {
19    public interface Listener {
20        /**
21         * Called when the user signs out of Chrome.
22         */
23        void onClearSignedInUser();
24    }
25
26    public static final String TAG = "ChromeSigninController";
27
28    @VisibleForTesting
29    public static final String SIGNED_IN_ACCOUNT_KEY = "google.services.username";
30
31    private static final Object LOCK = new Object();
32
33    private static ChromeSigninController sChromeSigninController;
34
35    private final Context mApplicationContext;
36
37    private final ObserverList<Listener> mListeners = new ObserverList<Listener>();
38
39    private boolean mGcmInitialized;
40
41    private ChromeSigninController(Context context) {
42        mApplicationContext = context.getApplicationContext();
43    }
44
45    /**
46     * A factory method for the ChromeSigninController.
47     *
48     * @param context the ApplicationContext is retrieved from the context used as an argument.
49     * @return a singleton instance of the ChromeSigninController
50     */
51    public static ChromeSigninController get(Context context) {
52        synchronized (LOCK) {
53            if (sChromeSigninController == null) {
54                sChromeSigninController = new ChromeSigninController(context);
55            }
56        }
57        return sChromeSigninController;
58    }
59
60    public Account getSignedInUser() {
61        String syncAccountName = getSignedInAccountName();
62        if (syncAccountName == null) {
63            return null;
64        }
65        return AccountManagerHelper.createAccountFromName(syncAccountName);
66    }
67
68    public boolean isSignedIn() {
69        return getSignedInAccountName() != null;
70    }
71
72    public void setSignedInAccountName(String accountName) {
73        PreferenceManager.getDefaultSharedPreferences(mApplicationContext).edit()
74                .putString(SIGNED_IN_ACCOUNT_KEY, accountName)
75                .apply();
76    }
77
78    public void clearSignedInUser() {
79        Log.d(TAG, "Clearing user signed in to Chrome");
80        setSignedInAccountName(null);
81
82        for (Listener listener : mListeners) {
83            listener.onClearSignedInUser();
84        }
85    }
86
87    public String getSignedInAccountName() {
88        return PreferenceManager.getDefaultSharedPreferences(mApplicationContext)
89                .getString(SIGNED_IN_ACCOUNT_KEY, null);
90    }
91
92    /**
93     * Adds a Listener.
94     * @param listener Listener to add.
95     */
96    public void addListener(Listener listener) {
97        mListeners.addObserver(listener);
98    }
99
100    /**
101     * Removes a Listener.
102     * @param listener Listener to remove from the list.
103     */
104    public void removeListener(Listener listener) {
105        mListeners.removeObserver(listener);
106    }
107
108    /**
109     * Registers for Google Cloud Messaging (GCM) if there is no existing registration.
110     */
111    public void ensureGcmIsInitialized() {
112        if (mGcmInitialized) return;
113        mGcmInitialized = true;
114        new AsyncTask<Void, Void, Void>() {
115            @Override
116            protected Void doInBackground(Void... arg0) {
117                try {
118                    String regId = MultiplexingGcmListener.initializeGcm(mApplicationContext);
119                    if (!regId.isEmpty())
120                        Log.d(TAG, "Already registered with GCM");
121                } catch (IllegalStateException exception) {
122                    Log.w(TAG, "Application manifest does not correctly configure GCM; "
123                            + "sync notifications will not work", exception);
124                } catch (UnsupportedOperationException exception) {
125                    Log.w(TAG, "Device does not support GCM; sync notifications will not work",
126                            exception);
127                }
128                return null;
129            }
130        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
131    }
132}
133