chromium/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java

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

package org.chromium.ui.base;

import android.content.Context;

import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting;

import org.jni_zero.CalledByNative;

import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.ui.R;
import org.chromium.ui.display.DisplayAndroid;
import org.chromium.ui.display.DisplayUtil;

/** UI utilities for accessing form factor information. */
public class DeviceFormFactor {
    /**
     * Minimum screen size in dp to be considered a tablet. Matches the value
     * used by res/ directories. E.g.: res/values-sw600dp/values.xml
     */
    public static final int MINIMUM_TABLET_WIDTH_DP = 600;

    /** Matches the value set in res/values-sw600dp/values.xml */
    @VisibleForTesting public static final int SCREEN_BUCKET_TABLET = 2;

    /** Matches the value set in res/values-sw720dp/values.xml */
    private static final int SCREEN_BUCKET_LARGET_TABLET = 3;

    /**
     * Each activity could be on a different display, and this will just tell you whether the
     * display associated with the application context is "tablet sized".
     * Use {@link #isNonMultiDisplayContextOnTablet} or {@link #isWindowOnTablet} instead.
     */
    @CalledByNative
    @Deprecated
    public static boolean isTablet() {
        return detectScreenWidthBucket(ContextUtils.getApplicationContext())
                >= SCREEN_BUCKET_TABLET;
    }

    /**
     * See {@link DisplayAndroid#getNonMultiDisplay}} for what "NonMultiDisplay" means.
     * When possible, it is generally more correct to use {@link #isWindowOnTablet}.
     * Only Activity instances and Contexts that wrap Activities are meaningfully associated with
     * displays, so care should be taken to pass a context that makes sense.
     *
     * @return Whether the display associated with the given context is large enough to be
     *         considered a tablet and will thus load tablet-specific resources (those in the config
     *         -sw600).
     *         Not affected by Android N multi-window, but can change for external displays.
     *         E.g. http://developer.samsung.com/samsung-dex/testing
     */
    public static boolean isNonMultiDisplayContextOnTablet(Context context) {
        return detectScreenWidthBucket(context) >= SCREEN_BUCKET_TABLET;
    }

    /**
     * @return Whether the display associated with the window is large enough to be
     *         considered a tablet and will thus load tablet-specific resources (those in the config
     *         -sw600).
     *         Not affected by Android N multi-window, but can change for external displays.
     *         E.g. http://developer.samsung.com/samsung-dex/testing
     */
    @UiThread
    public static boolean isWindowOnTablet(WindowAndroid windowAndroid) {
        return detectScreenWidthBucket(windowAndroid) >= SCREEN_BUCKET_TABLET;
    }

    /**
     * @return Whether the display associated with the given context is large enough to be
     *         considered a large tablet and will thus load large-tablet-specific resources (those
     *         in the config -sw720).
     *         Not affected by Android N multi-window, but can change for external displays.
     *         E.g. http://developer.samsung.com/samsung-dex/testing
     */
    public static boolean isNonMultiDisplayContextOnLargeTablet(Context context) {
        return detectScreenWidthBucket(context) == SCREEN_BUCKET_LARGET_TABLET;
    }

    /**
     * Detect the screen width bucket by loading the min_screen_width_bucket value (Android will
     * select the value from the correct directory; values, *-sw600dp, *-sw720dp). We can't use any
     * shortcuts here since there are several devices that are phone or tablet, but load each
     * others' resources (see https://crbug.com/850096 and https://crbug.com/669974 for more info).
     * @param context An Android context to read resources from.
     * @return The screen width bucket the device is in (see constants at the top of this class).
     */
    private static int detectScreenWidthBucket(Context context) {
        return context.getResources().getInteger(R.integer.min_screen_width_bucket);
    }

    private static int detectScreenWidthBucket(WindowAndroid windowAndroid) {
        ThreadUtils.assertOnUiThread();
        Context context = windowAndroid.getContext().get();
        if (context == null) return 0;
        return context.getResources().getInteger(R.integer.min_screen_width_bucket);
    }

    /**
     * @return The minimum width in px at which the display should be treated like a tablet for
     *         layout.
     */
    @UiThread
    public static int getNonMultiDisplayMinimumTabletWidthPx(Context context) {
        return getMinimumTabletWidthPx(DisplayAndroid.getNonMultiDisplay(context));
    }

    /**
     * @return The minimum width in px at which the display should be treated like a tablet for
     *         layout.
     */
    public static int getMinimumTabletWidthPx(DisplayAndroid display) {
        return DisplayUtil.dpToPx(display, DeviceFormFactor.MINIMUM_TABLET_WIDTH_DP);
    }
}