// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.util;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.UserManager;
import android.speech.RecognizerIntent;

import org.chromium.base.CommandLine;
import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.preferences.DocumentModeManager;
import org.chromium.sync.signin.AccountManagerHelper;
import org.chromium.ui.base.DeviceFormFactor;

import java.util.List;

/**
 * A utility {@code class} meant to help determine whether or not certain features are supported by
 * this device.
 */
public class FeatureUtilities {
    private static Boolean sHasGoogleAccountAuthenticator;
    private static Boolean sHasRecognitionIntentHandler;
    private static Boolean sDocumentModeDisabled;

    /**
     * Determines whether or not the {@link RecognizerIntent#ACTION_WEB_SEARCH} {@link Intent}
     * is handled by any {@link android.app.Activity}s in the system.  The result will be cached for
     * future calls.  Passing {@code false} to {@code useCachedValue} will force it to re-query any
     * {@link android.app.Activity}s that can process the {@link Intent}.
     * @param context        The {@link Context} to use to check to see if the {@link Intent} will
     *                       be handled.
     * @param useCachedValue Whether or not to use the cached value from a previous result.
     * @return {@code true} if recognition is supported.  {@code false} otherwise.
     */
    public static boolean isRecognitionIntentPresent(Context context, boolean useCachedValue) {
        ThreadUtils.assertOnUiThread();
        if (sHasRecognitionIntentHandler == null || !useCachedValue) {
            PackageManager pm = context.getPackageManager();
            List<ResolveInfo> activities = pm.queryIntentActivities(
                    new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
            sHasRecognitionIntentHandler = activities.size() > 0;
        }

        return sHasRecognitionIntentHandler;
    }

    /**
     * Determines whether or not the user has a Google account (so we can sync) or can add one.
     * @param context The {@link Context} that we should check accounts under.
     * @return Whether or not sync is allowed on this device.
     */
    public static boolean canAllowSync(Context context) {
        return (hasGoogleAccountAuthenticator(context) && hasSyncPermissions(context))
                || hasGoogleAccounts(context);
    }

    @VisibleForTesting
    static boolean hasGoogleAccountAuthenticator(Context context) {
        if (sHasGoogleAccountAuthenticator == null) {
            AccountManagerHelper accountHelper = AccountManagerHelper.get(context);
            sHasGoogleAccountAuthenticator = accountHelper.hasGoogleAccountAuthenticator();
        }
        return sHasGoogleAccountAuthenticator;
    }

    @VisibleForTesting
    static boolean hasGoogleAccounts(Context context) {
        return AccountManagerHelper.get(context).hasGoogleAccounts();
    }

    @SuppressLint("InlinedApi")
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private static boolean hasSyncPermissions(Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return true;

        UserManager manager = (UserManager) context.getSystemService(Context.USER_SERVICE);
        Bundle userRestrictions = manager.getUserRestrictions();
        return !userRestrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false);
    }

    /**
     * Check whether Chrome should be running on document mode.
     * @param context The context to use for checking configuration.
     * @return Whether Chrome should be running on document mode.
     */
    public static boolean isDocumentMode(Context context) {
        if (sDocumentModeDisabled == null && CommandLine.getInstance() != null) {
            sDocumentModeDisabled = CommandLine.getInstance().hasSwitch(
                    ChromeSwitches.DISABLE_DOCUMENT_MODE);
        }
        return isDocumentModeEligible(context)
                && !DocumentModeManager.getInstance(context).isOptedOutOfDocumentMode()
                && (sDocumentModeDisabled == null || !sDocumentModeDisabled.booleanValue());
    }

    /**
     * Whether the device could possibly run in Document mode (may return true even
     * if the document mode is turned off).
     * @param context The context to use for checking configuration.
     * @return Whether the device could possibly run in Document mode.
     */
    public static boolean isDocumentModeEligible(Context context) {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
                && !DeviceFormFactor.isTablet(context);
    }

    /**
     * Records the current document mode state with native-side feature utilities.
     * @param enabled Whether the document mode is enabled.
     */
    public static void setDocumentModeEnabled(boolean enabled) {
        nativeSetDocumentModeEnabled(enabled);
    }

    /**
     * Records the current custom tab visibility state with native-side feature utilities.
     * @param visible Whether a custom tab is visible.
     */
    public static void setCustomTabVisible(boolean visible) {
        nativeSetCustomTabVisible(visible);
    }

    /**
     * @return Whether a custom tab is visible.
     */
    public static boolean getCustomTabVisible() {
        return nativeGetCustomTabVisible();
    }

    private static native void nativeSetDocumentModeEnabled(boolean enabled);
    private static native void nativeSetCustomTabVisible(boolean visible);
    private static native boolean nativeGetCustomTabVisible();
}

@@ -1185,58 +1185,8 @@
 ler;
-%0A    private static Boolean sDocumentModeDisabled;
 %0A%0A  
@@ -4196,448 +4196,316 @@
     
-if (sDocumentModeDisabled == null && CommandLine.getInstance() != null) %7B%0A            sDocumentModeDisabled = CommandLine.getInstance().hasSwitch(%0A                    ChromeSwitches.DISABLE_DOCUMENT_MODE);%0A        %7D%0A        return isDocumentModeEligible(context)%0A                && !DocumentModeManager.getInstance(context).isOptedOutOfDocumentMode()%0A                && (sDocumentModeDisabled == null %7C%7C !sDocumentModeDisabled.booleanValue(
+return isDocumentModeEligible(context)%0A                && !DocumentModeManager.getInstance(context).isOptedOutOfDocumentMode()%0A                && ((CommandLine.getInstance() == null)%0A                    %7C%7C !CommandLine.getInstance().hasSwitch(%0A                            ChromeSwitches.DISABLE_DOCUMENT_MODE
 ));%0A

Revert of [Android] Cache document-mode command-line check (patchset #1 id:1 of https://codereview.chromium.org/1255733002/)