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 
5 package org.chromium.ui.base;
6 
7 import android.content.Context;
8 
9 import androidx.annotation.UiThread;
10 
11 import org.chromium.base.ContextUtils;
12 import org.chromium.base.ThreadUtils;
13 import org.chromium.base.annotations.CalledByNative;
14 import org.chromium.ui.R;
15 import org.chromium.ui.display.DisplayAndroid;
16 import org.chromium.ui.display.DisplayUtil;
17 
18 /**
19  * UI utilities for accessing form factor information.
20  */
21 public class DeviceFormFactor {
22     /**
23      * Miniumum screen size in dp to be considered a tablet. Matches the value
24      * used by res/ directories. E.g.: res/values-sw600dp/values.xml
25      */
26     public static final int MINIMUM_TABLET_WIDTH_DP = 600;
27 
28     /**
29      * Matches the value set in res/values-sw600dp/values.xml
30      */
31     private static final int SCREEN_BUCKET_TABLET = 2;
32 
33     /**
34      * Matches the value set in res/values-sw720dp/values.xml
35      */
36     private static final int SCREEN_BUCKET_LARGET_TABLET = 3;
37 
38     /**
39      * Each activity could be on a different display, and this will just tell you whether the
40      * display associated with the application context is "tablet sized".
41      * Use {@link #isNonMultiDisplayContextOnTablet} or {@link #isWindowOnTablet} instead.
42      */
43     @CalledByNative
44     @Deprecated
isTablet()45     public static boolean isTablet() {
46         return detectScreenWidthBucket(ContextUtils.getApplicationContext())
47                 >= SCREEN_BUCKET_TABLET;
48     }
49 
50     /**
51      * See {@link DisplayAndroid#getNonMultiDisplay}} for what "NonMultiDisplay" means.
52      * When possible, it is generally more correct to use {@link #isWindowOnTablet}.
53      * Only Activity instances and Contexts that wrap Activities are meaningfully associated with
54      * displays, so care should be taken to pass a context that makes sense.
55      *
56      * @return Whether the display associated with the given context is large enough to be
57      *         considered a tablet and will thus load tablet-specific resources (those in the config
58      *         -sw600).
59      *         Not affected by Android N multi-window, but can change for external displays.
60      *         E.g. http://developer.samsung.com/samsung-dex/testing
61      */
isNonMultiDisplayContextOnTablet(Context context)62     public static boolean isNonMultiDisplayContextOnTablet(Context context) {
63         return detectScreenWidthBucket(context) >= SCREEN_BUCKET_TABLET;
64     }
65 
66     /**
67      * @return Whether the display associated with the window is large enough to be
68      *         considered a tablet and will thus load tablet-specific resources (those in the config
69      *         -sw600).
70      *         Not affected by Android N multi-window, but can change for external displays.
71      *         E.g. http://developer.samsung.com/samsung-dex/testing
72      */
73     @UiThread
isWindowOnTablet(WindowAndroid windowAndroid)74     public static boolean isWindowOnTablet(WindowAndroid windowAndroid) {
75         return detectScreenWidthBucket(windowAndroid) >= SCREEN_BUCKET_TABLET;
76     }
77 
78     /**
79      * @return Whether the display associated with the given context is large enough to be
80      *         considered a large tablet and will thus load large-tablet-specific resources (those
81      *         in the config -sw720).
82      *         Not affected by Android N multi-window, but can change for external displays.
83      *         E.g. http://developer.samsung.com/samsung-dex/testing
84      */
isNonMultiDisplayContextOnLargeTablet(Context context)85     public static boolean isNonMultiDisplayContextOnLargeTablet(Context context) {
86         return detectScreenWidthBucket(context) == SCREEN_BUCKET_LARGET_TABLET;
87     }
88 
89     /**
90      * Detect the screen width bucket by loading the min_screen_width_bucket value (Android will
91      * select the value from the correct directory; values, *-sw600dp, *-sw720dp). We can't use any
92      * shortcuts here since there are several devices that are phone or tablet, but load each
93      * others' resources (see https://crbug.com/850096 and https://crbug.com/669974 for more info).
94      * @param context An Android context to read resources from.
95      * @return The screen width bucket the device is in (see constants at the top of this class).
96      */
detectScreenWidthBucket(Context context)97     private static int detectScreenWidthBucket(Context context) {
98         return context.getResources().getInteger(R.integer.min_screen_width_bucket);
99     }
100 
detectScreenWidthBucket(WindowAndroid windowAndroid)101     private static int detectScreenWidthBucket(WindowAndroid windowAndroid) {
102         ThreadUtils.assertOnUiThread();
103         Context context = windowAndroid.getContext().get();
104         if (context == null) return 0;
105         return context.getResources().getInteger(R.integer.min_screen_width_bucket);
106     }
107 
108     /**
109      * @return The minimum width in px at which the display should be treated like a tablet for
110      *         layout.
111      */
112     @UiThread
getNonMultiDisplayMinimumTabletWidthPx(Context context)113     public static int getNonMultiDisplayMinimumTabletWidthPx(Context context) {
114         return getMinimumTabletWidthPx(DisplayAndroid.getNonMultiDisplay(context));
115     }
116 
117     /**
118      * @return The minimum width in px at which the display should be treated like a tablet for
119      *         layout.
120      */
getMinimumTabletWidthPx(DisplayAndroid display)121     public static int getMinimumTabletWidthPx(DisplayAndroid display) {
122         return DisplayUtil.dpToPx(display, DeviceFormFactor.MINIMUM_TABLET_WIDTH_DP);
123     }
124 }
125