1// Copyright 2014 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.chrome.browser.util;
6
7import android.annotation.SuppressLint;
8import android.annotation.TargetApi;
9import android.content.Context;
10import android.content.Intent;
11import android.content.pm.PackageManager;
12import android.content.pm.ResolveInfo;
13import android.os.Build;
14import android.os.Bundle;
15import android.os.UserManager;
16import android.speech.RecognizerIntent;
17
18import com.google.common.annotations.VisibleForTesting;
19
20import org.chromium.base.ThreadUtils;
21import org.chromium.sync.signin.AccountManagerHelper;
22
23import java.util.List;
24
25/**
26 * A utility {@code class} meant to help determine whether or not certain features are supported by
27 * this device.
28 */
29public class FeatureUtilities {
30    private static Boolean sHasGoogleAccountAuthenticator;
31    private static Boolean sHasRecognitionIntentHandler;
32
33    /**
34     * Determines whether or not the {@link RecognizerIntent#ACTION_WEB_SEARCH} {@link Intent}
35     * is handled by any {@link android.app.Activity}s in the system.  The result will be cached for
36     * future calls.  Passing {@code false} to {@code useCachedValue} will force it to re-query any
37     * {@link android.app.Activity}s that can process the {@link Intent}.
38     * @param context        The {@link Context} to use to check to see if the {@link Intent} will
39     *                       be handled.
40     * @param useCachedValue Whether or not to use the cached value from a previous result.
41     * @return {@code true} if recognition is supported.  {@code false} otherwise.
42     */
43    public static boolean isRecognitionIntentPresent(Context context, boolean useCachedValue) {
44        ThreadUtils.assertOnUiThread();
45        if (sHasRecognitionIntentHandler == null || !useCachedValue) {
46            PackageManager pm = context.getPackageManager();
47            List<ResolveInfo> activities = pm.queryIntentActivities(
48                    new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
49            sHasRecognitionIntentHandler = activities.size() > 0;
50        }
51
52        return sHasRecognitionIntentHandler;
53    }
54
55    /**
56     * Determines whether or not the user has a Google account (so we can sync) or can add one.
57     * @param context The {@link Context} that we should check accounts under.
58     * @return Whether or not sync is allowed on this device.
59     */
60    public static boolean canAllowSync(Context context) {
61        return (hasGoogleAccountAuthenticator(context) && hasSyncPermissions(context)) ||
62                hasGoogleAccounts(context);
63    }
64
65    @VisibleForTesting
66    static boolean hasGoogleAccountAuthenticator(Context context) {
67        if (sHasGoogleAccountAuthenticator == null) {
68            AccountManagerHelper accountHelper = AccountManagerHelper.get(context);
69            sHasGoogleAccountAuthenticator = accountHelper.hasGoogleAccountAuthenticator();
70        }
71        return sHasGoogleAccountAuthenticator;
72    }
73
74    @VisibleForTesting
75    static boolean hasGoogleAccounts(Context context) {
76        return AccountManagerHelper.get(context).hasGoogleAccounts();
77    }
78
79    @SuppressLint("InlinedApi")
80    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
81    private static boolean hasSyncPermissions(Context context) {
82        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return true;
83
84        UserManager manager = (UserManager) context.getSystemService(Context.USER_SERVICE);
85        Bundle userRestrictions = manager.getUserRestrictions();
86        return !userRestrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false);
87    }
88}
89