Configuration.java revision 658d984f048e846b9039b7a44dc44fd22756c830
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.content.res;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.content.pm.ActivityInfo;
23import android.content.pm.ActivityInfo.Config;
24import android.graphics.Rect;
25import android.os.Build;
26import android.os.LocaleList;
27import android.os.Parcel;
28import android.os.Parcelable;
29import android.text.TextUtils;
30import android.view.DisplayInfo;
31import android.view.View;
32
33import com.android.internal.util.XmlUtils;
34
35import org.xmlpull.v1.XmlPullParser;
36import org.xmlpull.v1.XmlPullParserException;
37import org.xmlpull.v1.XmlSerializer;
38
39import java.io.IOException;
40import java.lang.annotation.Retention;
41import java.lang.annotation.RetentionPolicy;
42import java.util.ArrayList;
43import java.util.Locale;
44
45
46/**
47 * This class describes all device configuration information that can
48 * impact the resources the application retrieves.  This includes both
49 * user-specified configuration options (locale list and scaling) as well
50 * as device configurations (such as input modes, screen size and screen orientation).
51 * <p>You can acquire this object from {@link Resources}, using {@link
52 * Resources#getConfiguration}. Thus, from an activity, you can get it by chaining the request
53 * with {@link android.app.Activity#getResources}:</p>
54 * <pre>Configuration config = getResources().getConfiguration();</pre>
55 */
56public final class Configuration implements Parcelable, Comparable<Configuration> {
57    /** @hide */
58    public static final Configuration EMPTY = new Configuration();
59
60    /**
61     * Current user preference for the scaling factor for fonts, relative
62     * to the base density scaling.
63     */
64    public float fontScale;
65
66    /**
67     * IMSI MCC (Mobile Country Code), corresponding to
68     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
69     * resource qualifier.  0 if undefined.
70     */
71    public int mcc;
72
73    /**
74     * IMSI MNC (Mobile Network Code), corresponding to
75     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
76     * resource qualifier.  0 if undefined. Note that the actual MNC may be 0; in order to check
77     * for this use the {@link #MNC_ZERO} symbol.
78     */
79    public int mnc;
80
81    /**
82     * Constant used to to represent MNC (Mobile Network Code) zero.
83     * 0 cannot be used, since it is used to represent an undefined MNC.
84     */
85    public static final int MNC_ZERO = 0xffff;
86
87    /**
88     * Current user preference for the locale, corresponding to
89     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
90     * resource qualifier.
91     *
92     * @deprecated Do not set or read this directly. Use {@link #getLocales()} and
93     * {@link #setLocales(LocaleList)}. If only the primary locale is needed,
94     * <code>getLocales().get(0)</code> is now the preferred accessor.
95     */
96    @Deprecated public Locale locale;
97
98    private LocaleList mLocaleList;
99
100    /**
101     * Locale should persist on setting.  This is hidden because it is really
102     * questionable whether this is the right way to expose the functionality.
103     * @hide
104     */
105    public boolean userSetLocale;
106
107
108    /** Constant for {@link #colorMode}: bits that encode whether the screen is wide gamut. */
109    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_MASK = 0x3;
110    /**
111     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} value
112     * indicating that it is unknown whether or not the screen is wide gamut.
113     */
114    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED = 0x0;
115    /**
116     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} value
117     * indicating that the screen is not wide gamut.
118     * <p>Corresponds to the <code>-nowidecg</code> resource qualifier.</p>
119     */
120    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_NO = 0x1;
121    /**
122     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} value
123     * indicating that the screen is wide gamut.
124     * <p>Corresponds to the <code>-widecg</code> resource qualifier.</p>
125     */
126    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 0x2;
127
128    /** Constant for {@link #colorMode}: bits that encode the dynamic range of the screen. */
129    public static final int COLOR_MODE_HDR_MASK = 0xc;
130    /** Constant for {@link #colorMode}: bits shift to get the screen dynamic range. */
131    public static final int COLOR_MODE_HDR_SHIFT = 2;
132    /**
133     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_HDR_MASK} value
134     * indicating that it is unknown whether or not the screen is HDR.
135     */
136    public static final int COLOR_MODE_HDR_UNDEFINED = 0x0;
137    /**
138     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_HDR_MASK} value
139     * indicating that the screen is not HDR (low/standard dynamic range).
140     * <p>Corresponds to the <code>-lowdr</code> resource qualifier.</p>
141     */
142    public static final int COLOR_MODE_HDR_NO = 0x1 << COLOR_MODE_HDR_SHIFT;
143    /**
144     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_HDR_MASK} value
145     * indicating that the screen is HDR (dynamic range).
146     * <p>Corresponds to the <code>-highdr</code> resource qualifier.</p>
147     */
148    public static final int COLOR_MODE_HDR_YES = 0x2 << COLOR_MODE_HDR_SHIFT;
149
150    /** Constant for {@link #colorMode}: a value indicating that the color mode is undefined */
151    @SuppressWarnings("PointlessBitwiseExpression")
152    public static final int COLOR_MODE_UNDEFINED = COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED |
153            COLOR_MODE_HDR_UNDEFINED;
154
155    /**
156     * Bit mask of color capabilities of the screen. Currently there are two fields:
157     * <p>The {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} bits define the color gamut of
158     * the screen. They may be one of
159     * {@link #COLOR_MODE_WIDE_COLOR_GAMUT_NO} or {@link #COLOR_MODE_WIDE_COLOR_GAMUT_YES}.</p>
160     *
161     * <p>The {@link #COLOR_MODE_HDR_MASK} defines the dynamic range of the screen. They may be
162     * one of {@link #COLOR_MODE_HDR_NO} or {@link #COLOR_MODE_HDR_YES}.</p>
163     *
164     * <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
165     * Multiple Screens</a> for more information.</p>
166     */
167    public int colorMode;
168
169    /** Constant for {@link #screenLayout}: bits that encode the size. */
170    public static final int SCREENLAYOUT_SIZE_MASK = 0x0f;
171    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
172     * value indicating that no size has been set. */
173    public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0x00;
174    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
175     * value indicating the screen is at least approximately 320x426 dp units,
176     * corresponds to the
177     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
178     * resource qualifier.
179     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
180     * Multiple Screens</a> for more information. */
181    public static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
182    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
183     * value indicating the screen is at least approximately 320x470 dp units,
184     * corresponds to the
185     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
186     * resource qualifier.
187     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
188     * Multiple Screens</a> for more information. */
189    public static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
190    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
191     * value indicating the screen is at least approximately 480x640 dp units,
192     * corresponds to the
193     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
194     * resource qualifier.
195     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
196     * Multiple Screens</a> for more information. */
197    public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
198    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
199     * value indicating the screen is at least approximately 720x960 dp units,
200     * corresponds to the
201     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
202     * resource qualifier.
203     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
204     * Multiple Screens</a> for more information.*/
205    public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
206
207    /** Constant for {@link #screenLayout}: bits that encode the aspect ratio. */
208    public static final int SCREENLAYOUT_LONG_MASK = 0x30;
209    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
210     * value indicating that no size has been set. */
211    public static final int SCREENLAYOUT_LONG_UNDEFINED = 0x00;
212    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
213     * value that corresponds to the
214     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
215     * resource qualifier. */
216    public static final int SCREENLAYOUT_LONG_NO = 0x10;
217    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
218     * value that corresponds to the
219     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
220     * resource qualifier. */
221    public static final int SCREENLAYOUT_LONG_YES = 0x20;
222
223    /** Constant for {@link #screenLayout}: bits that encode the layout direction. */
224    public static final int SCREENLAYOUT_LAYOUTDIR_MASK = 0xC0;
225    /** Constant for {@link #screenLayout}: bits shift to get the layout direction. */
226    public static final int SCREENLAYOUT_LAYOUTDIR_SHIFT = 6;
227    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
228     * value indicating that no layout dir has been set. */
229    public static final int SCREENLAYOUT_LAYOUTDIR_UNDEFINED = 0x00;
230    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
231     * value indicating that a layout dir has been set to LTR. */
232    public static final int SCREENLAYOUT_LAYOUTDIR_LTR = 0x01 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
233    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
234     * value indicating that a layout dir has been set to RTL. */
235    public static final int SCREENLAYOUT_LAYOUTDIR_RTL = 0x02 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
236
237    /** Constant for {@link #screenLayout}: bits that encode roundness of the screen. */
238    public static final int SCREENLAYOUT_ROUND_MASK = 0x300;
239    /** @hide Constant for {@link #screenLayout}: bit shift to get to screen roundness bits */
240    public static final int SCREENLAYOUT_ROUND_SHIFT = 8;
241    /**
242     * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating
243     * that it is unknown whether or not the screen has a round shape.
244     */
245    public static final int SCREENLAYOUT_ROUND_UNDEFINED = 0x00;
246    /**
247     * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating
248     * that the screen does not have a rounded shape.
249     */
250    public static final int SCREENLAYOUT_ROUND_NO = 0x1 << SCREENLAYOUT_ROUND_SHIFT;
251    /**
252     * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating
253     * that the screen has a rounded shape. Corners may not be visible to the user;
254     * developers should pay special attention to the {@link android.view.WindowInsets} delivered
255     * to views for more information about ensuring content is not obscured.
256     *
257     * <p>Corresponds to the <code>-round</code> resource qualifier.</p>
258     */
259    public static final int SCREENLAYOUT_ROUND_YES = 0x2 << SCREENLAYOUT_ROUND_SHIFT;
260
261    /** Constant for {@link #screenLayout}: a value indicating that screenLayout is undefined */
262    public static final int SCREENLAYOUT_UNDEFINED = SCREENLAYOUT_SIZE_UNDEFINED |
263            SCREENLAYOUT_LONG_UNDEFINED | SCREENLAYOUT_LAYOUTDIR_UNDEFINED |
264            SCREENLAYOUT_ROUND_UNDEFINED;
265
266    /**
267     * Special flag we generate to indicate that the screen layout requires
268     * us to use a compatibility mode for apps that are not modern layout
269     * aware.
270     * @hide
271     */
272    public static final int SCREENLAYOUT_COMPAT_NEEDED = 0x10000000;
273
274    /**
275     * Bit mask of overall layout of the screen.  Currently there are four
276     * fields:
277     * <p>The {@link #SCREENLAYOUT_SIZE_MASK} bits define the overall size
278     * of the screen.  They may be one of
279     * {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL},
280     * {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}.</p>
281     *
282     * <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
283     * is wider/taller than normal.  They may be one of
284     * {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.</p>
285     *
286     * <p>The {@link #SCREENLAYOUT_LAYOUTDIR_MASK} defines whether the screen layout
287     * is either LTR or RTL.  They may be one of
288     * {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}.</p>
289     *
290     * <p>The {@link #SCREENLAYOUT_ROUND_MASK} defines whether the screen has a rounded
291     * shape. They may be one of {@link #SCREENLAYOUT_ROUND_NO} or {@link #SCREENLAYOUT_ROUND_YES}.
292     * </p>
293     *
294     * <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
295     * Multiple Screens</a> for more information.</p>
296     */
297    public int screenLayout;
298
299    /**
300     * @hide
301     * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
302     * {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at
303     * the display level. Lower levels can override these values to provide custom bounds to enforce
304     * features such as a max aspect ratio.
305     * TODO(b/36812336): Move appBounds out of {@link Configuration}.
306     */
307    public Rect appBounds;
308
309    /** @hide */
310    static public int resetScreenLayout(int curLayout) {
311        return (curLayout&~(SCREENLAYOUT_LONG_MASK | SCREENLAYOUT_SIZE_MASK
312                        | SCREENLAYOUT_COMPAT_NEEDED))
313                | (SCREENLAYOUT_LONG_YES | SCREENLAYOUT_SIZE_XLARGE);
314    }
315
316    /** @hide */
317    static public int reduceScreenLayout(int curLayout, int longSizeDp, int shortSizeDp) {
318        int screenLayoutSize;
319        boolean screenLayoutLong;
320        boolean screenLayoutCompatNeeded;
321
322        // These semi-magic numbers define our compatibility modes for
323        // applications with different screens.  These are guarantees to
324        // app developers about the space they can expect for a particular
325        // configuration.  DO NOT CHANGE!
326        if (longSizeDp < 470) {
327            // This is shorter than an HVGA normal density screen (which
328            // is 480 pixels on its long side).
329            screenLayoutSize = SCREENLAYOUT_SIZE_SMALL;
330            screenLayoutLong = false;
331            screenLayoutCompatNeeded = false;
332        } else {
333            // What size is this screen screen?
334            if (longSizeDp >= 960 && shortSizeDp >= 720) {
335                // 1.5xVGA or larger screens at medium density are the point
336                // at which we consider it to be an extra large screen.
337                screenLayoutSize = SCREENLAYOUT_SIZE_XLARGE;
338            } else if (longSizeDp >= 640 && shortSizeDp >= 480) {
339                // VGA or larger screens at medium density are the point
340                // at which we consider it to be a large screen.
341                screenLayoutSize = SCREENLAYOUT_SIZE_LARGE;
342            } else {
343                screenLayoutSize = SCREENLAYOUT_SIZE_NORMAL;
344            }
345
346            // If this screen is wider than normal HVGA, or taller
347            // than FWVGA, then for old apps we want to run in size
348            // compatibility mode.
349            if (shortSizeDp > 321 || longSizeDp > 570) {
350                screenLayoutCompatNeeded = true;
351            } else {
352                screenLayoutCompatNeeded = false;
353            }
354
355            // Is this a long screen?
356            if (((longSizeDp*3)/5) >= (shortSizeDp-1)) {
357                // Anything wider than WVGA (5:3) is considering to be long.
358                screenLayoutLong = true;
359            } else {
360                screenLayoutLong = false;
361            }
362        }
363
364        // Now reduce the last screenLayout to not be better than what we
365        // have found.
366        if (!screenLayoutLong) {
367            curLayout = (curLayout&~SCREENLAYOUT_LONG_MASK) | SCREENLAYOUT_LONG_NO;
368        }
369        if (screenLayoutCompatNeeded) {
370            curLayout |= Configuration.SCREENLAYOUT_COMPAT_NEEDED;
371        }
372        int curSize = curLayout&SCREENLAYOUT_SIZE_MASK;
373        if (screenLayoutSize < curSize) {
374            curLayout = (curLayout&~SCREENLAYOUT_SIZE_MASK) | screenLayoutSize;
375        }
376        return curLayout;
377    }
378
379    /** @hide */
380    public static String configurationDiffToString(int diff) {
381        ArrayList<String> list = new ArrayList<>();
382        if ((diff & ActivityInfo.CONFIG_MCC) != 0) {
383            list.add("CONFIG_MCC");
384        }
385        if ((diff & ActivityInfo.CONFIG_MNC) != 0) {
386            list.add("CONFIG_MNC");
387        }
388        if ((diff & ActivityInfo.CONFIG_LOCALE) != 0) {
389            list.add("CONFIG_LOCALE");
390        }
391        if ((diff & ActivityInfo.CONFIG_TOUCHSCREEN) != 0) {
392            list.add("CONFIG_TOUCHSCREEN");
393        }
394        if ((diff & ActivityInfo.CONFIG_KEYBOARD) != 0) {
395            list.add("CONFIG_KEYBOARD");
396        }
397        if ((diff & ActivityInfo.CONFIG_KEYBOARD_HIDDEN) != 0) {
398            list.add("CONFIG_KEYBOARD_HIDDEN");
399        }
400        if ((diff & ActivityInfo.CONFIG_NAVIGATION) != 0) {
401            list.add("CONFIG_NAVIGATION");
402        }
403        if ((diff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
404            list.add("CONFIG_ORIENTATION");
405        }
406        if ((diff & ActivityInfo.CONFIG_SCREEN_LAYOUT) != 0) {
407            list.add("CONFIG_SCREEN_LAYOUT");
408        }
409        if ((diff & ActivityInfo.CONFIG_COLOR_MODE) != 0) {
410            list.add("CONFIG_COLOR_MODE");
411        }
412        if ((diff & ActivityInfo.CONFIG_UI_MODE) != 0) {
413            list.add("CONFIG_UI_MODE");
414        }
415        if ((diff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
416            list.add("CONFIG_SCREEN_SIZE");
417        }
418        if ((diff & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
419            list.add("CONFIG_SMALLEST_SCREEN_SIZE");
420        }
421        if ((diff & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0) {
422            list.add("CONFIG_LAYOUT_DIRECTION");
423        }
424        if ((diff & ActivityInfo.CONFIG_FONT_SCALE) != 0) {
425            list.add("CONFIG_FONT_SCALE");
426        }
427        if ((diff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
428            list.add("CONFIG_ASSETS_PATHS");
429        }
430        StringBuilder builder = new StringBuilder("{");
431        for (int i = 0, n = list.size(); i < n; i++) {
432            builder.append(list.get(i));
433            if (i != n - 1) {
434                builder.append(", ");
435            }
436        }
437        builder.append("}");
438        return builder.toString();
439    }
440
441    /**
442     * Check if the Configuration's current {@link #screenLayout} is at
443     * least the given size.
444     *
445     * @param size The desired size, either {@link #SCREENLAYOUT_SIZE_SMALL},
446     * {@link #SCREENLAYOUT_SIZE_NORMAL}, {@link #SCREENLAYOUT_SIZE_LARGE}, or
447     * {@link #SCREENLAYOUT_SIZE_XLARGE}.
448     * @return Returns true if the current screen layout size is at least
449     * the given size.
450     */
451    public boolean isLayoutSizeAtLeast(int size) {
452        int cur = screenLayout&SCREENLAYOUT_SIZE_MASK;
453        if (cur == SCREENLAYOUT_SIZE_UNDEFINED) return false;
454        return cur >= size;
455    }
456
457    /** Constant for {@link #touchscreen}: a value indicating that no value has been set. */
458    public static final int TOUCHSCREEN_UNDEFINED = 0;
459    /** Constant for {@link #touchscreen}, value corresponding to the
460     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
461     * resource qualifier. */
462    public static final int TOUCHSCREEN_NOTOUCH = 1;
463    /** @deprecated Not currently supported or used. */
464    @Deprecated public static final int TOUCHSCREEN_STYLUS = 2;
465    /** Constant for {@link #touchscreen}, value corresponding to the
466     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
467     * resource qualifier. */
468    public static final int TOUCHSCREEN_FINGER = 3;
469
470    /**
471     * The kind of touch screen attached to the device.
472     * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_FINGER}.
473     */
474    public int touchscreen;
475
476    /** Constant for {@link #keyboard}: a value indicating that no value has been set. */
477    public static final int KEYBOARD_UNDEFINED = 0;
478    /** Constant for {@link #keyboard}, value corresponding to the
479     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
480     * resource qualifier. */
481    public static final int KEYBOARD_NOKEYS = 1;
482    /** Constant for {@link #keyboard}, value corresponding to the
483     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
484     * resource qualifier. */
485    public static final int KEYBOARD_QWERTY = 2;
486    /** Constant for {@link #keyboard}, value corresponding to the
487     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
488     * resource qualifier. */
489    public static final int KEYBOARD_12KEY = 3;
490
491    /**
492     * The kind of keyboard attached to the device.
493     * One of: {@link #KEYBOARD_NOKEYS}, {@link #KEYBOARD_QWERTY},
494     * {@link #KEYBOARD_12KEY}.
495     */
496    public int keyboard;
497
498    /** Constant for {@link #keyboardHidden}: a value indicating that no value has been set. */
499    public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
500    /** Constant for {@link #keyboardHidden}, value corresponding to the
501     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
502     * resource qualifier. */
503    public static final int KEYBOARDHIDDEN_NO = 1;
504    /** Constant for {@link #keyboardHidden}, value corresponding to the
505     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
506     * resource qualifier. */
507    public static final int KEYBOARDHIDDEN_YES = 2;
508    /** Constant matching actual resource implementation. {@hide} */
509    public static final int KEYBOARDHIDDEN_SOFT = 3;
510
511    /**
512     * A flag indicating whether any keyboard is available.  Unlike
513     * {@link #hardKeyboardHidden}, this also takes into account a soft
514     * keyboard, so if the hard keyboard is hidden but there is soft
515     * keyboard available, it will be set to NO.  Value is one of:
516     * {@link #KEYBOARDHIDDEN_NO}, {@link #KEYBOARDHIDDEN_YES}.
517     */
518    public int keyboardHidden;
519
520    /** Constant for {@link #hardKeyboardHidden}: a value indicating that no value has been set. */
521    public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0;
522    /** Constant for {@link #hardKeyboardHidden}, value corresponding to the
523     * physical keyboard being exposed. */
524    public static final int HARDKEYBOARDHIDDEN_NO = 1;
525    /** Constant for {@link #hardKeyboardHidden}, value corresponding to the
526     * physical keyboard being hidden. */
527    public static final int HARDKEYBOARDHIDDEN_YES = 2;
528
529    /**
530     * A flag indicating whether the hard keyboard has been hidden.  This will
531     * be set on a device with a mechanism to hide the keyboard from the
532     * user, when that mechanism is closed.  One of:
533     * {@link #HARDKEYBOARDHIDDEN_NO}, {@link #HARDKEYBOARDHIDDEN_YES}.
534     */
535    public int hardKeyboardHidden;
536
537    /** Constant for {@link #navigation}: a value indicating that no value has been set. */
538    public static final int NAVIGATION_UNDEFINED = 0;
539    /** Constant for {@link #navigation}, value corresponding to the
540     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
541     * resource qualifier. */
542    public static final int NAVIGATION_NONAV = 1;
543    /** Constant for {@link #navigation}, value corresponding to the
544     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
545     * resource qualifier. */
546    public static final int NAVIGATION_DPAD = 2;
547    /** Constant for {@link #navigation}, value corresponding to the
548     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
549     * resource qualifier. */
550    public static final int NAVIGATION_TRACKBALL = 3;
551    /** Constant for {@link #navigation}, value corresponding to the
552     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
553     * resource qualifier. */
554    public static final int NAVIGATION_WHEEL = 4;
555
556    /**
557     * The kind of navigation method available on the device.
558     * One of: {@link #NAVIGATION_NONAV}, {@link #NAVIGATION_DPAD},
559     * {@link #NAVIGATION_TRACKBALL}, {@link #NAVIGATION_WHEEL}.
560     */
561    public int navigation;
562
563    /** Constant for {@link #navigationHidden}: a value indicating that no value has been set. */
564    public static final int NAVIGATIONHIDDEN_UNDEFINED = 0;
565    /** Constant for {@link #navigationHidden}, value corresponding to the
566     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
567     * resource qualifier. */
568    public static final int NAVIGATIONHIDDEN_NO = 1;
569    /** Constant for {@link #navigationHidden}, value corresponding to the
570     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
571     * resource qualifier. */
572    public static final int NAVIGATIONHIDDEN_YES = 2;
573
574    /**
575     * A flag indicating whether any 5-way or DPAD navigation available.
576     * This will be set on a device with a mechanism to hide the navigation
577     * controls from the user, when that mechanism is closed.  One of:
578     * {@link #NAVIGATIONHIDDEN_NO}, {@link #NAVIGATIONHIDDEN_YES}.
579     */
580    public int navigationHidden;
581
582    /** Constant for {@link #orientation}: a value indicating that no value has been set. */
583    public static final int ORIENTATION_UNDEFINED = 0;
584    /** Constant for {@link #orientation}, value corresponding to the
585     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
586     * resource qualifier. */
587    public static final int ORIENTATION_PORTRAIT = 1;
588    /** Constant for {@link #orientation}, value corresponding to the
589     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
590     * resource qualifier. */
591    public static final int ORIENTATION_LANDSCAPE = 2;
592    /** @deprecated Not currently supported or used. */
593    @Deprecated public static final int ORIENTATION_SQUARE = 3;
594
595    /**
596     * Overall orientation of the screen.  May be one of
597     * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}.
598     */
599    public int orientation;
600
601    /** Constant for {@link #uiMode}: bits that encode the mode type. */
602    public static final int UI_MODE_TYPE_MASK = 0x0f;
603    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
604     * value indicating that no mode type has been set. */
605    public static final int UI_MODE_TYPE_UNDEFINED = 0x00;
606    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
607     * value that corresponds to
608     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">no
609     * UI mode</a> resource qualifier specified. */
610    public static final int UI_MODE_TYPE_NORMAL = 0x01;
611    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
612     * value that corresponds to the
613     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a>
614     * resource qualifier. */
615    public static final int UI_MODE_TYPE_DESK = 0x02;
616    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
617     * value that corresponds to the
618     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">car</a>
619     * resource qualifier. */
620    public static final int UI_MODE_TYPE_CAR = 0x03;
621    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
622     * value that corresponds to the
623     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">television</a>
624     * resource qualifier. */
625    public static final int UI_MODE_TYPE_TELEVISION = 0x04;
626    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
627     * value that corresponds to the
628     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a>
629     * resource qualifier. */
630    public static final int UI_MODE_TYPE_APPLIANCE = 0x05;
631    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
632     * value that corresponds to the
633     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a>
634     * resource qualifier. */
635    public static final int UI_MODE_TYPE_WATCH = 0x06;
636    /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
637     * value that corresponds to the
638     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">vrheadset</a>
639     * resource qualifier. */
640    public static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
641
642    /** Constant for {@link #uiMode}: bits that encode the night mode. */
643    public static final int UI_MODE_NIGHT_MASK = 0x30;
644    /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
645     * value indicating that no mode type has been set. */
646    public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
647    /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
648     * value that corresponds to the
649     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NightQualifier">notnight</a>
650     * resource qualifier. */
651    public static final int UI_MODE_NIGHT_NO = 0x10;
652    /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
653     * value that corresponds to the
654     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NightQualifier">night</a>
655     * resource qualifier. */
656    public static final int UI_MODE_NIGHT_YES = 0x20;
657
658    /**
659     * Bit mask of the ui mode.  Currently there are two fields:
660     * <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the
661     * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED},
662     * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK},
663     * {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION},
664     * {@link #UI_MODE_TYPE_APPLIANCE}, {@link #UI_MODE_TYPE_WATCH},
665     * or {@link #UI_MODE_TYPE_VR_HEADSET}.
666     *
667     * <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen
668     * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED},
669     * {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.
670     */
671    public int uiMode;
672
673    /**
674     * Default value for {@link #screenWidthDp} indicating that no width
675     * has been specified.
676     */
677    public static final int SCREEN_WIDTH_DP_UNDEFINED = 0;
678
679    /**
680     * The current width of the available screen space, in dp units,
681     * corresponding to
682     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenWidthQualifier">screen
683     * width</a> resource qualifier.  Set to
684     * {@link #SCREEN_WIDTH_DP_UNDEFINED} if no width is specified.
685     */
686    public int screenWidthDp;
687
688    /**
689     * Default value for {@link #screenHeightDp} indicating that no width
690     * has been specified.
691     */
692    public static final int SCREEN_HEIGHT_DP_UNDEFINED = 0;
693
694    /**
695     * The current height of the available screen space, in dp units,
696     * corresponding to
697     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenHeightQualifier">screen
698     * height</a> resource qualifier.  Set to
699     * {@link #SCREEN_HEIGHT_DP_UNDEFINED} if no height is specified.
700     */
701    public int screenHeightDp;
702
703    /**
704     * Default value for {@link #smallestScreenWidthDp} indicating that no width
705     * has been specified.
706     */
707    public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0;
708
709    /**
710     * The smallest screen size an application will see in normal operation,
711     * corresponding to
712     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest
713     * screen width</a> resource qualifier.
714     * This is the smallest value of both screenWidthDp and screenHeightDp
715     * in both portrait and landscape.  Set to
716     * {@link #SMALLEST_SCREEN_WIDTH_DP_UNDEFINED} if no width is specified.
717     */
718    public int smallestScreenWidthDp;
719
720    /**
721     * Default value for {@link #densityDpi} indicating that no width
722     * has been specified.
723     */
724    public static final int DENSITY_DPI_UNDEFINED = 0;
725
726    /**
727     * Value for {@link #densityDpi} for resources that scale to any density (vector drawables).
728     * {@hide}
729     */
730    public static final int DENSITY_DPI_ANY = 0xfffe;
731
732    /**
733     * Value for {@link #densityDpi} for resources that are not meant to be scaled.
734     * {@hide}
735     */
736    public static final int DENSITY_DPI_NONE = 0xffff;
737
738    /**
739     * The target screen density being rendered to,
740     * corresponding to
741     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#DensityQualifier">density</a>
742     * resource qualifier.  Set to
743     * {@link #DENSITY_DPI_UNDEFINED} if no density is specified.
744     */
745    public int densityDpi;
746
747    /** @hide Hack to get this information from WM to app running in compat mode. */
748    public int compatScreenWidthDp;
749    /** @hide Hack to get this information from WM to app running in compat mode. */
750    public int compatScreenHeightDp;
751    /** @hide Hack to get this information from WM to app running in compat mode. */
752    public int compatSmallestScreenWidthDp;
753
754    /**
755     * An undefined assetsSeq. This will not override an existing assetsSeq.
756     * @hide
757     */
758    public static final int ASSETS_SEQ_UNDEFINED = 0;
759
760    /**
761     * Internal counter that allows us to piggyback off the configuration change mechanism to
762     * signal to apps that the the assets for an Application have changed. A difference in these
763     * between two Configurations will yield a diff flag of
764     * {@link ActivityInfo#CONFIG_ASSETS_PATHS}.
765     * @hide
766     */
767    public int assetsSeq;
768
769    /**
770     * @hide Internal book-keeping.
771     */
772    public int seq;
773
774    /** @hide */
775    @IntDef(flag = true,
776            value = {
777                    NATIVE_CONFIG_MCC,
778                    NATIVE_CONFIG_MNC,
779                    NATIVE_CONFIG_LOCALE,
780                    NATIVE_CONFIG_TOUCHSCREEN,
781                    NATIVE_CONFIG_KEYBOARD,
782                    NATIVE_CONFIG_KEYBOARD_HIDDEN,
783                    NATIVE_CONFIG_NAVIGATION,
784                    NATIVE_CONFIG_ORIENTATION,
785                    NATIVE_CONFIG_DENSITY,
786                    NATIVE_CONFIG_SCREEN_SIZE,
787                    NATIVE_CONFIG_VERSION,
788                    NATIVE_CONFIG_SCREEN_LAYOUT,
789                    NATIVE_CONFIG_UI_MODE,
790                    NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,
791                    NATIVE_CONFIG_LAYOUTDIR,
792                    NATIVE_CONFIG_COLOR_MODE,
793            })
794    @Retention(RetentionPolicy.SOURCE)
795    public @interface NativeConfig {}
796
797    /** @hide Native-specific bit mask for MCC config; DO NOT USE UNLESS YOU ARE SURE. */
798    public static final int NATIVE_CONFIG_MCC = 0x0001;
799    /** @hide Native-specific bit mask for MNC config; DO NOT USE UNLESS YOU ARE SURE. */
800    public static final int NATIVE_CONFIG_MNC = 0x0002;
801    /** @hide Native-specific bit mask for LOCALE config; DO NOT USE UNLESS YOU ARE SURE. */
802    public static final int NATIVE_CONFIG_LOCALE = 0x0004;
803    /** @hide Native-specific bit mask for TOUCHSCREEN config; DO NOT USE UNLESS YOU ARE SURE. */
804    public static final int NATIVE_CONFIG_TOUCHSCREEN = 0x0008;
805    /** @hide Native-specific bit mask for KEYBOARD config; DO NOT USE UNLESS YOU ARE SURE. */
806    public static final int NATIVE_CONFIG_KEYBOARD = 0x0010;
807    /** @hide Native-specific bit mask for KEYBOARD_HIDDEN config; DO NOT USE UNLESS YOU
808     * ARE SURE. */
809    public static final int NATIVE_CONFIG_KEYBOARD_HIDDEN = 0x0020;
810    /** @hide Native-specific bit mask for NAVIGATION config; DO NOT USE UNLESS YOU ARE SURE. */
811    public static final int NATIVE_CONFIG_NAVIGATION = 0x0040;
812    /** @hide Native-specific bit mask for ORIENTATION config; DO NOT USE UNLESS YOU ARE SURE. */
813    public static final int NATIVE_CONFIG_ORIENTATION = 0x0080;
814    /** @hide Native-specific bit mask for DENSITY config; DO NOT USE UNLESS YOU ARE SURE. */
815    public static final int NATIVE_CONFIG_DENSITY = 0x0100;
816    /** @hide Native-specific bit mask for SCREEN_SIZE config; DO NOT USE UNLESS YOU ARE SURE. */
817    public static final int NATIVE_CONFIG_SCREEN_SIZE = 0x0200;
818    /** @hide Native-specific bit mask for VERSION config; DO NOT USE UNLESS YOU ARE SURE. */
819    public static final int NATIVE_CONFIG_VERSION = 0x0400;
820    /** @hide Native-specific bit mask for SCREEN_LAYOUT config; DO NOT USE UNLESS YOU ARE SURE. */
821    public static final int NATIVE_CONFIG_SCREEN_LAYOUT = 0x0800;
822    /** @hide Native-specific bit mask for UI_MODE config; DO NOT USE UNLESS YOU ARE SURE. */
823    public static final int NATIVE_CONFIG_UI_MODE = 0x1000;
824    /** @hide Native-specific bit mask for SMALLEST_SCREEN_SIZE config; DO NOT USE UNLESS YOU
825     * ARE SURE. */
826    public static final int NATIVE_CONFIG_SMALLEST_SCREEN_SIZE = 0x2000;
827    /** @hide Native-specific bit mask for LAYOUTDIR config ; DO NOT USE UNLESS YOU ARE SURE.*/
828    public static final int NATIVE_CONFIG_LAYOUTDIR = 0x4000;
829    /** @hide Native-specific bit mask for COLOR_MODE config ; DO NOT USE UNLESS YOU ARE SURE.*/
830    public static final int NATIVE_CONFIG_COLOR_MODE = 0x10000;
831
832    /**
833     * <p>Construct an invalid Configuration. This state is only suitable for constructing a
834     * Configuration delta that will be applied to some valid Configuration object. In order to
835     * create a valid standalone Configuration, you must call {@link #setToDefaults}. </p>
836     *
837     * <p>Example:</p>
838     * <pre class="prettyprint">
839     *     Configuration validConfig = new Configuration();
840     *     validConfig.setToDefaults();
841     *
842     *     Configuration deltaOnlyConfig = new Configuration();
843     *     deltaOnlyConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
844     *
845     *     validConfig.updateFrom(deltaOnlyConfig);
846     * </pre>
847     */
848    public Configuration() {
849        unset();
850    }
851
852    /**
853     * Makes a deep copy suitable for modification.
854     */
855    public Configuration(Configuration o) {
856        setTo(o);
857    }
858
859    /* This brings mLocaleList in sync with locale in case a user of the older API who doesn't know
860     * about setLocales() has changed locale directly. */
861    private void fixUpLocaleList() {
862        if ((locale == null && !mLocaleList.isEmpty()) ||
863                (locale != null && !locale.equals(mLocaleList.get(0)))) {
864            mLocaleList = locale == null ? LocaleList.getEmptyLocaleList() : new LocaleList(locale);
865        }
866    }
867
868    /**
869     * Sets the fields in this object to those in the given Configuration.
870     *
871     * @param o The Configuration object used to set the values of this Configuration's fields.
872     */
873    public void setTo(Configuration o) {
874        fontScale = o.fontScale;
875        mcc = o.mcc;
876        mnc = o.mnc;
877        locale = o.locale == null ? null : (Locale) o.locale.clone();
878        o.fixUpLocaleList();
879        mLocaleList = o.mLocaleList;
880        userSetLocale = o.userSetLocale;
881        touchscreen = o.touchscreen;
882        keyboard = o.keyboard;
883        keyboardHidden = o.keyboardHidden;
884        hardKeyboardHidden = o.hardKeyboardHidden;
885        navigation = o.navigation;
886        navigationHidden = o.navigationHidden;
887        orientation = o.orientation;
888        screenLayout = o.screenLayout;
889        colorMode = o.colorMode;
890        uiMode = o.uiMode;
891        screenWidthDp = o.screenWidthDp;
892        screenHeightDp = o.screenHeightDp;
893        smallestScreenWidthDp = o.smallestScreenWidthDp;
894        densityDpi = o.densityDpi;
895        compatScreenWidthDp = o.compatScreenWidthDp;
896        compatScreenHeightDp = o.compatScreenHeightDp;
897        compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp;
898        setAppBounds(o.appBounds);
899        assetsSeq = o.assetsSeq;
900        seq = o.seq;
901    }
902
903    public String toString() {
904        StringBuilder sb = new StringBuilder(128);
905        sb.append("{");
906        sb.append(fontScale);
907        sb.append(" ");
908        if (mcc != 0) {
909            sb.append(mcc);
910            sb.append("mcc");
911        } else {
912            sb.append("?mcc");
913        }
914        if (mnc != 0) {
915            sb.append(mnc);
916            sb.append("mnc");
917        } else {
918            sb.append("?mnc");
919        }
920        fixUpLocaleList();
921        if (!mLocaleList.isEmpty()) {
922            sb.append(" ");
923            sb.append(mLocaleList);
924        } else {
925            sb.append(" ?localeList");
926        }
927        int layoutDir = (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK);
928        switch (layoutDir) {
929            case SCREENLAYOUT_LAYOUTDIR_UNDEFINED: sb.append(" ?layoutDir"); break;
930            case SCREENLAYOUT_LAYOUTDIR_LTR: sb.append(" ldltr"); break;
931            case SCREENLAYOUT_LAYOUTDIR_RTL: sb.append(" ldrtl"); break;
932            default: sb.append(" layoutDir=");
933                sb.append(layoutDir >> SCREENLAYOUT_LAYOUTDIR_SHIFT); break;
934        }
935        if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
936            sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
937        } else {
938            sb.append(" ?swdp");
939        }
940        if (screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
941            sb.append(" w"); sb.append(screenWidthDp); sb.append("dp");
942        } else {
943            sb.append(" ?wdp");
944        }
945        if (screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
946            sb.append(" h"); sb.append(screenHeightDp); sb.append("dp");
947        } else {
948            sb.append(" ?hdp");
949        }
950        if (densityDpi != DENSITY_DPI_UNDEFINED) {
951            sb.append(" "); sb.append(densityDpi); sb.append("dpi");
952        } else {
953            sb.append(" ?density");
954        }
955        switch ((screenLayout&SCREENLAYOUT_SIZE_MASK)) {
956            case SCREENLAYOUT_SIZE_UNDEFINED: sb.append(" ?lsize"); break;
957            case SCREENLAYOUT_SIZE_SMALL: sb.append(" smll"); break;
958            case SCREENLAYOUT_SIZE_NORMAL: sb.append(" nrml"); break;
959            case SCREENLAYOUT_SIZE_LARGE: sb.append(" lrg"); break;
960            case SCREENLAYOUT_SIZE_XLARGE: sb.append(" xlrg"); break;
961            default: sb.append(" layoutSize=");
962                    sb.append(screenLayout&SCREENLAYOUT_SIZE_MASK); break;
963        }
964        switch ((screenLayout&SCREENLAYOUT_LONG_MASK)) {
965            case SCREENLAYOUT_LONG_UNDEFINED: sb.append(" ?long"); break;
966            case SCREENLAYOUT_LONG_NO: /* not-long is not interesting to print */ break;
967            case SCREENLAYOUT_LONG_YES: sb.append(" long"); break;
968            default: sb.append(" layoutLong=");
969                    sb.append(screenLayout&SCREENLAYOUT_LONG_MASK); break;
970        }
971        switch ((colorMode &COLOR_MODE_HDR_MASK)) {
972            case COLOR_MODE_HDR_UNDEFINED: sb.append(" ?ldr"); break; // most likely not HDR
973            case COLOR_MODE_HDR_NO: /* ldr is not interesting to print */ break;
974            case COLOR_MODE_HDR_YES: sb.append(" hdr"); break;
975            default: sb.append(" dynamicRange=");
976                sb.append(colorMode &COLOR_MODE_HDR_MASK); break;
977        }
978        switch ((colorMode &COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
979            case COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED: sb.append(" ?wideColorGamut"); break;
980            case COLOR_MODE_WIDE_COLOR_GAMUT_NO: /* not wide is not interesting to print */ break;
981            case COLOR_MODE_WIDE_COLOR_GAMUT_YES: sb.append(" widecg"); break;
982            default: sb.append(" wideColorGamut=");
983                sb.append(colorMode &COLOR_MODE_WIDE_COLOR_GAMUT_MASK); break;
984        }
985        switch (orientation) {
986            case ORIENTATION_UNDEFINED: sb.append(" ?orien"); break;
987            case ORIENTATION_LANDSCAPE: sb.append(" land"); break;
988            case ORIENTATION_PORTRAIT: sb.append(" port"); break;
989            default: sb.append(" orien="); sb.append(orientation); break;
990        }
991        switch ((uiMode&UI_MODE_TYPE_MASK)) {
992            case UI_MODE_TYPE_UNDEFINED: sb.append(" ?uimode"); break;
993            case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break;
994            case UI_MODE_TYPE_DESK: sb.append(" desk"); break;
995            case UI_MODE_TYPE_CAR: sb.append(" car"); break;
996            case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break;
997            case UI_MODE_TYPE_APPLIANCE: sb.append(" appliance"); break;
998            case UI_MODE_TYPE_WATCH: sb.append(" watch"); break;
999            case UI_MODE_TYPE_VR_HEADSET: sb.append(" vrheadset"); break;
1000            default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break;
1001        }
1002        switch ((uiMode&UI_MODE_NIGHT_MASK)) {
1003            case UI_MODE_NIGHT_UNDEFINED: sb.append(" ?night"); break;
1004            case UI_MODE_NIGHT_NO: /* not-night is not interesting to print */ break;
1005            case UI_MODE_NIGHT_YES: sb.append(" night"); break;
1006            default: sb.append(" night="); sb.append(uiMode&UI_MODE_NIGHT_MASK); break;
1007        }
1008        switch (touchscreen) {
1009            case TOUCHSCREEN_UNDEFINED: sb.append(" ?touch"); break;
1010            case TOUCHSCREEN_NOTOUCH: sb.append(" -touch"); break;
1011            case TOUCHSCREEN_STYLUS: sb.append(" stylus"); break;
1012            case TOUCHSCREEN_FINGER: sb.append(" finger"); break;
1013            default: sb.append(" touch="); sb.append(touchscreen); break;
1014        }
1015        switch (keyboard) {
1016            case KEYBOARD_UNDEFINED: sb.append(" ?keyb"); break;
1017            case KEYBOARD_NOKEYS: sb.append(" -keyb"); break;
1018            case KEYBOARD_QWERTY: sb.append(" qwerty"); break;
1019            case KEYBOARD_12KEY: sb.append(" 12key"); break;
1020            default: sb.append(" keys="); sb.append(keyboard); break;
1021        }
1022        switch (keyboardHidden) {
1023            case KEYBOARDHIDDEN_UNDEFINED: sb.append("/?"); break;
1024            case KEYBOARDHIDDEN_NO: sb.append("/v"); break;
1025            case KEYBOARDHIDDEN_YES: sb.append("/h"); break;
1026            case KEYBOARDHIDDEN_SOFT: sb.append("/s"); break;
1027            default: sb.append("/"); sb.append(keyboardHidden); break;
1028        }
1029        switch (hardKeyboardHidden) {
1030            case HARDKEYBOARDHIDDEN_UNDEFINED: sb.append("/?"); break;
1031            case HARDKEYBOARDHIDDEN_NO: sb.append("/v"); break;
1032            case HARDKEYBOARDHIDDEN_YES: sb.append("/h"); break;
1033            default: sb.append("/"); sb.append(hardKeyboardHidden); break;
1034        }
1035        switch (navigation) {
1036            case NAVIGATION_UNDEFINED: sb.append(" ?nav"); break;
1037            case NAVIGATION_NONAV: sb.append(" -nav"); break;
1038            case NAVIGATION_DPAD: sb.append(" dpad"); break;
1039            case NAVIGATION_TRACKBALL: sb.append(" tball"); break;
1040            case NAVIGATION_WHEEL: sb.append(" wheel"); break;
1041            default: sb.append(" nav="); sb.append(navigation); break;
1042        }
1043        switch (navigationHidden) {
1044            case NAVIGATIONHIDDEN_UNDEFINED: sb.append("/?"); break;
1045            case NAVIGATIONHIDDEN_NO: sb.append("/v"); break;
1046            case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
1047            default: sb.append("/"); sb.append(navigationHidden); break;
1048        }
1049        if (appBounds != null) {
1050            sb.append(" appBounds="); sb.append(appBounds);
1051        }
1052        if (assetsSeq != 0) {
1053            sb.append(" as.").append(assetsSeq);
1054        }
1055        if (seq != 0) {
1056            sb.append(" s.").append(seq);
1057        }
1058        sb.append('}');
1059        return sb.toString();
1060    }
1061
1062    /**
1063     * Set this object to the system defaults.
1064     */
1065    public void setToDefaults() {
1066        fontScale = 1;
1067        mcc = mnc = 0;
1068        mLocaleList = LocaleList.getEmptyLocaleList();
1069        locale = null;
1070        userSetLocale = false;
1071        touchscreen = TOUCHSCREEN_UNDEFINED;
1072        keyboard = KEYBOARD_UNDEFINED;
1073        keyboardHidden = KEYBOARDHIDDEN_UNDEFINED;
1074        hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED;
1075        navigation = NAVIGATION_UNDEFINED;
1076        navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
1077        orientation = ORIENTATION_UNDEFINED;
1078        screenLayout = SCREENLAYOUT_UNDEFINED;
1079        colorMode = COLOR_MODE_UNDEFINED;
1080        uiMode = UI_MODE_TYPE_UNDEFINED;
1081        screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
1082        screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
1083        smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
1084        densityDpi = DENSITY_DPI_UNDEFINED;
1085        assetsSeq = ASSETS_SEQ_UNDEFINED;
1086        appBounds = null;
1087        seq = 0;
1088    }
1089
1090    /**
1091     * Set this object to completely undefined.
1092     * @hide
1093     */
1094    public void unset() {
1095        setToDefaults();
1096        fontScale = 0;
1097    }
1098
1099    /** {@hide} */
1100    @Deprecated public void makeDefault() {
1101        setToDefaults();
1102    }
1103
1104    /**
1105     * Copies the fields from delta into this Configuration object, keeping
1106     * track of which ones have changed. Any undefined fields in {@code delta}
1107     * are ignored and not copied in to the current Configuration.
1108     *
1109     * @return a bit mask of the changed fields, as per {@link #diff}
1110     */
1111    public @Config int updateFrom(@NonNull Configuration delta) {
1112        int changed = 0;
1113        if (delta.fontScale > 0 && fontScale != delta.fontScale) {
1114            changed |= ActivityInfo.CONFIG_FONT_SCALE;
1115            fontScale = delta.fontScale;
1116        }
1117        if (delta.mcc != 0 && mcc != delta.mcc) {
1118            changed |= ActivityInfo.CONFIG_MCC;
1119            mcc = delta.mcc;
1120        }
1121        if (delta.mnc != 0 && mnc != delta.mnc) {
1122            changed |= ActivityInfo.CONFIG_MNC;
1123            mnc = delta.mnc;
1124        }
1125        fixUpLocaleList();
1126        delta.fixUpLocaleList();
1127        if (!delta.mLocaleList.isEmpty() && !mLocaleList.equals(delta.mLocaleList)) {
1128            changed |= ActivityInfo.CONFIG_LOCALE;
1129            mLocaleList = delta.mLocaleList;
1130            // delta.locale can't be null, since delta.mLocaleList is not empty.
1131            if (!delta.locale.equals(locale)) {
1132                locale = (Locale) delta.locale.clone();
1133                // If locale has changed, then layout direction is also changed ...
1134                changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
1135                // ... and we need to update the layout direction (represented by the first
1136                // 2 most significant bits in screenLayout).
1137                setLayoutDirection(locale);
1138            }
1139        }
1140        final int deltaScreenLayoutDir = delta.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
1141        if (deltaScreenLayoutDir != SCREENLAYOUT_LAYOUTDIR_UNDEFINED &&
1142                deltaScreenLayoutDir != (screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
1143            screenLayout = (screenLayout & ~SCREENLAYOUT_LAYOUTDIR_MASK) | deltaScreenLayoutDir;
1144            changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
1145        }
1146        if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
1147        {
1148            changed |= ActivityInfo.CONFIG_LOCALE;
1149            userSetLocale = true;
1150        }
1151        if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
1152                && touchscreen != delta.touchscreen) {
1153            changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
1154            touchscreen = delta.touchscreen;
1155        }
1156        if (delta.keyboard != KEYBOARD_UNDEFINED
1157                && keyboard != delta.keyboard) {
1158            changed |= ActivityInfo.CONFIG_KEYBOARD;
1159            keyboard = delta.keyboard;
1160        }
1161        if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
1162                && keyboardHidden != delta.keyboardHidden) {
1163            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
1164            keyboardHidden = delta.keyboardHidden;
1165        }
1166        if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
1167                && hardKeyboardHidden != delta.hardKeyboardHidden) {
1168            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
1169            hardKeyboardHidden = delta.hardKeyboardHidden;
1170        }
1171        if (delta.navigation != NAVIGATION_UNDEFINED
1172                && navigation != delta.navigation) {
1173            changed |= ActivityInfo.CONFIG_NAVIGATION;
1174            navigation = delta.navigation;
1175        }
1176        if (delta.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED
1177                && navigationHidden != delta.navigationHidden) {
1178            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
1179            navigationHidden = delta.navigationHidden;
1180        }
1181        if (delta.orientation != ORIENTATION_UNDEFINED
1182                && orientation != delta.orientation) {
1183            changed |= ActivityInfo.CONFIG_ORIENTATION;
1184            orientation = delta.orientation;
1185        }
1186
1187        if (((delta.screenLayout & SCREENLAYOUT_SIZE_MASK) != SCREENLAYOUT_SIZE_UNDEFINED)
1188                && (delta.screenLayout & SCREENLAYOUT_SIZE_MASK)
1189                != (screenLayout & SCREENLAYOUT_SIZE_MASK)) {
1190            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
1191            screenLayout = (screenLayout & ~SCREENLAYOUT_SIZE_MASK)
1192                    | (delta.screenLayout & SCREENLAYOUT_SIZE_MASK);
1193        }
1194        if (((delta.screenLayout & SCREENLAYOUT_LONG_MASK) != SCREENLAYOUT_LONG_UNDEFINED)
1195                && (delta.screenLayout & SCREENLAYOUT_LONG_MASK)
1196                != (screenLayout & SCREENLAYOUT_LONG_MASK)) {
1197            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
1198            screenLayout = (screenLayout & ~SCREENLAYOUT_LONG_MASK)
1199                    | (delta.screenLayout & SCREENLAYOUT_LONG_MASK);
1200        }
1201        if (((delta.screenLayout & SCREENLAYOUT_ROUND_MASK) != SCREENLAYOUT_ROUND_UNDEFINED)
1202                && (delta.screenLayout & SCREENLAYOUT_ROUND_MASK)
1203                != (screenLayout & SCREENLAYOUT_ROUND_MASK)) {
1204            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
1205            screenLayout = (screenLayout & ~SCREENLAYOUT_ROUND_MASK)
1206                    | (delta.screenLayout & SCREENLAYOUT_ROUND_MASK);
1207        }
1208        if ((delta.screenLayout & SCREENLAYOUT_COMPAT_NEEDED)
1209                != (screenLayout & SCREENLAYOUT_COMPAT_NEEDED)
1210                && delta.screenLayout != 0) {
1211            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
1212            screenLayout = (screenLayout & ~SCREENLAYOUT_COMPAT_NEEDED)
1213                | (delta.screenLayout & SCREENLAYOUT_COMPAT_NEEDED);
1214        }
1215
1216        if (((delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
1217                     COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED)
1218                && (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)
1219                != (colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
1220            changed |= ActivityInfo.CONFIG_COLOR_MODE;
1221            colorMode = (colorMode & ~COLOR_MODE_WIDE_COLOR_GAMUT_MASK)
1222                    | (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK);
1223        }
1224
1225        if (((delta.colorMode & COLOR_MODE_HDR_MASK) != COLOR_MODE_HDR_UNDEFINED)
1226                && (delta.colorMode & COLOR_MODE_HDR_MASK)
1227                != (colorMode & COLOR_MODE_HDR_MASK)) {
1228            changed |= ActivityInfo.CONFIG_COLOR_MODE;
1229            colorMode = (colorMode & ~COLOR_MODE_HDR_MASK)
1230                    | (delta.colorMode & COLOR_MODE_HDR_MASK);
1231        }
1232
1233        if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
1234                && uiMode != delta.uiMode) {
1235            changed |= ActivityInfo.CONFIG_UI_MODE;
1236            if ((delta.uiMode&UI_MODE_TYPE_MASK) != UI_MODE_TYPE_UNDEFINED) {
1237                uiMode = (uiMode&~UI_MODE_TYPE_MASK)
1238                        | (delta.uiMode&UI_MODE_TYPE_MASK);
1239            }
1240            if ((delta.uiMode&UI_MODE_NIGHT_MASK) != UI_MODE_NIGHT_UNDEFINED) {
1241                uiMode = (uiMode&~UI_MODE_NIGHT_MASK)
1242                        | (delta.uiMode&UI_MODE_NIGHT_MASK);
1243            }
1244        }
1245        if (delta.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED
1246                && screenWidthDp != delta.screenWidthDp) {
1247            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
1248            screenWidthDp = delta.screenWidthDp;
1249        }
1250        if (delta.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED
1251                && screenHeightDp != delta.screenHeightDp) {
1252            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
1253            screenHeightDp = delta.screenHeightDp;
1254        }
1255        if (delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
1256                && smallestScreenWidthDp != delta.smallestScreenWidthDp) {
1257            changed |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
1258            smallestScreenWidthDp = delta.smallestScreenWidthDp;
1259        }
1260        if (delta.densityDpi != DENSITY_DPI_UNDEFINED &&
1261                densityDpi != delta.densityDpi) {
1262            changed |= ActivityInfo.CONFIG_DENSITY;
1263            densityDpi = delta.densityDpi;
1264        }
1265        if (delta.compatScreenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
1266            compatScreenWidthDp = delta.compatScreenWidthDp;
1267        }
1268        if (delta.compatScreenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
1269            compatScreenHeightDp = delta.compatScreenHeightDp;
1270        }
1271        if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
1272            compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
1273        }
1274        if (delta.appBounds != null && !delta.appBounds.equals(appBounds)) {
1275            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
1276            setAppBounds(delta.appBounds);
1277        }
1278        if (delta.assetsSeq != ASSETS_SEQ_UNDEFINED && delta.assetsSeq != assetsSeq) {
1279            changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
1280            assetsSeq = delta.assetsSeq;
1281        }
1282        if (delta.seq != 0) {
1283            seq = delta.seq;
1284        }
1285
1286        return changed;
1287    }
1288
1289    /**
1290     * Return a bit mask of the differences between this Configuration
1291     * object and the given one.  Does not change the values of either.  Any
1292     * undefined fields in <var>delta</var> are ignored.
1293     * @return Returns a bit mask indicating which configuration
1294     * values has changed, containing any combination of
1295     * {@link android.content.pm.ActivityInfo#CONFIG_FONT_SCALE
1296     * PackageManager.ActivityInfo.CONFIG_FONT_SCALE},
1297     * {@link android.content.pm.ActivityInfo#CONFIG_MCC
1298     * PackageManager.ActivityInfo.CONFIG_MCC},
1299     * {@link android.content.pm.ActivityInfo#CONFIG_MNC
1300     * PackageManager.ActivityInfo.CONFIG_MNC},
1301     * {@link android.content.pm.ActivityInfo#CONFIG_LOCALE
1302     * PackageManager.ActivityInfo.CONFIG_LOCALE},
1303     * {@link android.content.pm.ActivityInfo#CONFIG_TOUCHSCREEN
1304     * PackageManager.ActivityInfo.CONFIG_TOUCHSCREEN},
1305     * {@link android.content.pm.ActivityInfo#CONFIG_KEYBOARD
1306     * PackageManager.ActivityInfo.CONFIG_KEYBOARD},
1307     * {@link android.content.pm.ActivityInfo#CONFIG_NAVIGATION
1308     * PackageManager.ActivityInfo.CONFIG_NAVIGATION},
1309     * {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION
1310     * PackageManager.ActivityInfo.CONFIG_ORIENTATION},
1311     * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT
1312     * PackageManager.ActivityInfo.CONFIG_SCREEN_LAYOUT}, or
1313     * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_SIZE
1314     * PackageManager.ActivityInfo.CONFIG_SCREEN_SIZE}, or
1315     * {@link android.content.pm.ActivityInfo#CONFIG_SMALLEST_SCREEN_SIZE
1316     * PackageManager.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE}.
1317     * {@link android.content.pm.ActivityInfo#CONFIG_LAYOUT_DIRECTION
1318     * PackageManager.ActivityInfo.CONFIG_LAYOUT_DIRECTION}.
1319     */
1320    public int diff(Configuration delta) {
1321        return diff(delta, false /* compareUndefined */, false /* publicOnly */);
1322    }
1323
1324    /**
1325     * Returns the diff against the provided {@link Configuration} excluding values that would
1326     * publicly be equivalent, such as appBounds.
1327     * @param delta {@link Configuration} to compare to.
1328     *
1329     * TODO(b/36812336): Remove once appBounds has been moved out of Configuration.
1330     * {@hide}
1331     */
1332    public int diffPublicOnly(Configuration delta) {
1333        return diff(delta, false /* compareUndefined */, true /* publicOnly */);
1334    }
1335
1336    /**
1337     * Variation of {@link #diff(Configuration)} with an option to skip checks for undefined values.
1338     *
1339     * @hide
1340     */
1341    public int diff(Configuration delta, boolean compareUndefined, boolean publicOnly) {
1342        int changed = 0;
1343        if ((compareUndefined || delta.fontScale > 0) && fontScale != delta.fontScale) {
1344            changed |= ActivityInfo.CONFIG_FONT_SCALE;
1345        }
1346        if ((compareUndefined || delta.mcc != 0) && mcc != delta.mcc) {
1347            changed |= ActivityInfo.CONFIG_MCC;
1348        }
1349        if ((compareUndefined || delta.mnc != 0) && mnc != delta.mnc) {
1350            changed |= ActivityInfo.CONFIG_MNC;
1351        }
1352        fixUpLocaleList();
1353        delta.fixUpLocaleList();
1354        if ((compareUndefined || !delta.mLocaleList.isEmpty())
1355                && !mLocaleList.equals(delta.mLocaleList)) {
1356            changed |= ActivityInfo.CONFIG_LOCALE;
1357            changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
1358        }
1359        final int deltaScreenLayoutDir = delta.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
1360        if ((compareUndefined || deltaScreenLayoutDir != SCREENLAYOUT_LAYOUTDIR_UNDEFINED)
1361                && deltaScreenLayoutDir != (screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
1362            changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
1363        }
1364        if ((compareUndefined || delta.touchscreen != TOUCHSCREEN_UNDEFINED)
1365                && touchscreen != delta.touchscreen) {
1366            changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
1367        }
1368        if ((compareUndefined || delta.keyboard != KEYBOARD_UNDEFINED)
1369                && keyboard != delta.keyboard) {
1370            changed |= ActivityInfo.CONFIG_KEYBOARD;
1371        }
1372        if ((compareUndefined || delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED)
1373                && keyboardHidden != delta.keyboardHidden) {
1374            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
1375        }
1376        if ((compareUndefined || delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED)
1377                && hardKeyboardHidden != delta.hardKeyboardHidden) {
1378            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
1379        }
1380        if ((compareUndefined || delta.navigation != NAVIGATION_UNDEFINED)
1381                && navigation != delta.navigation) {
1382            changed |= ActivityInfo.CONFIG_NAVIGATION;
1383        }
1384        if ((compareUndefined || delta.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED)
1385                && navigationHidden != delta.navigationHidden) {
1386            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
1387        }
1388        if ((compareUndefined || delta.orientation != ORIENTATION_UNDEFINED)
1389                && orientation != delta.orientation) {
1390            changed |= ActivityInfo.CONFIG_ORIENTATION;
1391        }
1392        if ((compareUndefined || getScreenLayoutNoDirection(delta.screenLayout) !=
1393                (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED))
1394                && getScreenLayoutNoDirection(screenLayout) !=
1395                getScreenLayoutNoDirection(delta.screenLayout)) {
1396            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
1397        }
1398        if ((compareUndefined ||
1399                     (delta.colorMode & COLOR_MODE_HDR_MASK) != COLOR_MODE_HDR_UNDEFINED)
1400                && (colorMode & COLOR_MODE_HDR_MASK) !=
1401                        (delta.colorMode & COLOR_MODE_HDR_MASK)) {
1402            changed |= ActivityInfo.CONFIG_COLOR_MODE;
1403        }
1404        if ((compareUndefined ||
1405                     (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
1406                             COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED)
1407                && (colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
1408                        (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
1409            changed |= ActivityInfo.CONFIG_COLOR_MODE;
1410        }
1411        if ((compareUndefined || delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED))
1412                && uiMode != delta.uiMode) {
1413            changed |= ActivityInfo.CONFIG_UI_MODE;
1414        }
1415        if ((compareUndefined || delta.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED)
1416                && screenWidthDp != delta.screenWidthDp) {
1417            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
1418        }
1419        if ((compareUndefined || delta.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED)
1420                && screenHeightDp != delta.screenHeightDp) {
1421            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
1422        }
1423        if ((compareUndefined || delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED)
1424                && smallestScreenWidthDp != delta.smallestScreenWidthDp) {
1425            changed |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
1426        }
1427        if ((compareUndefined || delta.densityDpi != DENSITY_DPI_UNDEFINED)
1428                && densityDpi != delta.densityDpi) {
1429            changed |= ActivityInfo.CONFIG_DENSITY;
1430        }
1431        if ((compareUndefined || delta.assetsSeq != ASSETS_SEQ_UNDEFINED)
1432                && assetsSeq != delta.assetsSeq) {
1433            changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
1434        }
1435
1436        // Make sure that one of the values is not null and that they are not equal.
1437        if ((compareUndefined || delta.appBounds != null)
1438                && appBounds != delta.appBounds
1439                && (appBounds == null || (!publicOnly && !appBounds.equals(delta.appBounds))
1440                        || (publicOnly && (appBounds.width() != delta.appBounds.width()
1441                                || appBounds.height() != delta.appBounds.height())))) {
1442            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
1443        }
1444
1445        return changed;
1446    }
1447
1448    /**
1449     * Determines if a new resource needs to be loaded from the bit set of
1450     * configuration changes returned by {@link #updateFrom(Configuration)}.
1451     *
1452     * @param configChanges the mask of changes configurations as returned by
1453     *                      {@link #updateFrom(Configuration)}
1454     * @param interestingChanges the configuration changes that the resource
1455     *                           can handle as given in
1456     *                           {@link android.util.TypedValue#changingConfigurations}
1457     * @return {@code true} if the resource needs to be loaded, {@code false}
1458     *         otherwise
1459     */
1460    public static boolean needNewResources(@Config int configChanges,
1461            @Config int interestingChanges) {
1462        // CONFIG_ASSETS_PATHS and CONFIG_FONT_SCALE are higher level configuration changes that
1463        // all resources are subject to change with.
1464        interestingChanges = interestingChanges | ActivityInfo.CONFIG_ASSETS_PATHS
1465                | ActivityInfo.CONFIG_FONT_SCALE;
1466        return (configChanges & interestingChanges) != 0;
1467    }
1468
1469    /**
1470     * @hide Return true if the sequence of 'other' is better than this.  Assumes
1471     * that 'this' is your current sequence and 'other' is a new one you have
1472     * received some how and want to compare with what you have.
1473     */
1474    public boolean isOtherSeqNewer(Configuration other) {
1475        if (other == null) {
1476            // Sanity check.
1477            return false;
1478        }
1479        if (other.seq == 0) {
1480            // If the other sequence is not specified, then we must assume
1481            // it is newer since we don't know any better.
1482            return true;
1483        }
1484        if (seq == 0) {
1485            // If this sequence is not specified, then we also consider the
1486            // other is better.  Yes we have a preference for other.  Sue us.
1487            return true;
1488        }
1489        int diff = other.seq - seq;
1490        if (diff > 0x10000) {
1491            // If there has been a sufficiently large jump, assume the
1492            // sequence has wrapped around.
1493            return false;
1494        }
1495        return diff > 0;
1496    }
1497
1498    /**
1499     * Parcelable methods
1500     */
1501    public int describeContents() {
1502        return 0;
1503    }
1504
1505    public void writeToParcel(Parcel dest, int flags) {
1506        dest.writeFloat(fontScale);
1507        dest.writeInt(mcc);
1508        dest.writeInt(mnc);
1509
1510        fixUpLocaleList();
1511        final int localeListSize = mLocaleList.size();
1512        dest.writeInt(localeListSize);
1513        for (int i = 0; i < localeListSize; ++i) {
1514            final Locale l = mLocaleList.get(i);
1515            dest.writeString(l.toLanguageTag());
1516        }
1517
1518        if(userSetLocale) {
1519            dest.writeInt(1);
1520        } else {
1521            dest.writeInt(0);
1522        }
1523        dest.writeInt(touchscreen);
1524        dest.writeInt(keyboard);
1525        dest.writeInt(keyboardHidden);
1526        dest.writeInt(hardKeyboardHidden);
1527        dest.writeInt(navigation);
1528        dest.writeInt(navigationHidden);
1529        dest.writeInt(orientation);
1530        dest.writeInt(screenLayout);
1531        dest.writeInt(colorMode);
1532        dest.writeInt(uiMode);
1533        dest.writeInt(screenWidthDp);
1534        dest.writeInt(screenHeightDp);
1535        dest.writeInt(smallestScreenWidthDp);
1536        dest.writeInt(densityDpi);
1537        dest.writeInt(compatScreenWidthDp);
1538        dest.writeInt(compatScreenHeightDp);
1539        dest.writeInt(compatSmallestScreenWidthDp);
1540        dest.writeValue(appBounds);
1541        dest.writeInt(assetsSeq);
1542        dest.writeInt(seq);
1543    }
1544
1545    public void readFromParcel(Parcel source) {
1546        fontScale = source.readFloat();
1547        mcc = source.readInt();
1548        mnc = source.readInt();
1549
1550        final int localeListSize = source.readInt();
1551        final Locale[] localeArray = new Locale[localeListSize];
1552        for (int i = 0; i < localeListSize; ++i) {
1553            localeArray[i] = Locale.forLanguageTag(source.readString());
1554        }
1555        mLocaleList = new LocaleList(localeArray);
1556        locale = mLocaleList.get(0);
1557
1558        userSetLocale = (source.readInt()==1);
1559        touchscreen = source.readInt();
1560        keyboard = source.readInt();
1561        keyboardHidden = source.readInt();
1562        hardKeyboardHidden = source.readInt();
1563        navigation = source.readInt();
1564        navigationHidden = source.readInt();
1565        orientation = source.readInt();
1566        screenLayout = source.readInt();
1567        colorMode = source.readInt();
1568        uiMode = source.readInt();
1569        screenWidthDp = source.readInt();
1570        screenHeightDp = source.readInt();
1571        smallestScreenWidthDp = source.readInt();
1572        densityDpi = source.readInt();
1573        compatScreenWidthDp = source.readInt();
1574        compatScreenHeightDp = source.readInt();
1575        compatSmallestScreenWidthDp = source.readInt();
1576        appBounds = (Rect) source.readValue(null);
1577        assetsSeq = source.readInt();
1578        seq = source.readInt();
1579    }
1580
1581    public static final Parcelable.Creator<Configuration> CREATOR
1582            = new Parcelable.Creator<Configuration>() {
1583        public Configuration createFromParcel(Parcel source) {
1584            return new Configuration(source);
1585        }
1586
1587        public Configuration[] newArray(int size) {
1588            return new Configuration[size];
1589        }
1590    };
1591
1592    /**
1593     * Construct this Configuration object, reading from the Parcel.
1594     */
1595    private Configuration(Parcel source) {
1596        readFromParcel(source);
1597    }
1598
1599    public int compareTo(Configuration that) {
1600        int n;
1601        float a = this.fontScale;
1602        float b = that.fontScale;
1603        if (a < b) return -1;
1604        if (a > b) return 1;
1605        n = this.mcc - that.mcc;
1606        if (n != 0) return n;
1607        n = this.mnc - that.mnc;
1608        if (n != 0) return n;
1609
1610        fixUpLocaleList();
1611        that.fixUpLocaleList();
1612        // for backward compatibility, we consider an empty locale list to be greater
1613        // than any non-empty locale list.
1614        if (this.mLocaleList.isEmpty()) {
1615            if (!that.mLocaleList.isEmpty()) return 1;
1616        } else if (that.mLocaleList.isEmpty()) {
1617            return -1;
1618        } else {
1619            final int minSize = Math.min(this.mLocaleList.size(), that.mLocaleList.size());
1620            for (int i = 0; i < minSize; ++i) {
1621                final Locale thisLocale = this.mLocaleList.get(i);
1622                final Locale thatLocale = that.mLocaleList.get(i);
1623                n = thisLocale.getLanguage().compareTo(thatLocale.getLanguage());
1624                if (n != 0) return n;
1625                n = thisLocale.getCountry().compareTo(thatLocale.getCountry());
1626                if (n != 0) return n;
1627                n = thisLocale.getVariant().compareTo(thatLocale.getVariant());
1628                if (n != 0) return n;
1629                n = thisLocale.toLanguageTag().compareTo(thatLocale.toLanguageTag());
1630                if (n != 0) return n;
1631            }
1632            n = this.mLocaleList.size() - that.mLocaleList.size();
1633            if (n != 0) return n;
1634        }
1635
1636        n = this.touchscreen - that.touchscreen;
1637        if (n != 0) return n;
1638        n = this.keyboard - that.keyboard;
1639        if (n != 0) return n;
1640        n = this.keyboardHidden - that.keyboardHidden;
1641        if (n != 0) return n;
1642        n = this.hardKeyboardHidden - that.hardKeyboardHidden;
1643        if (n != 0) return n;
1644        n = this.navigation - that.navigation;
1645        if (n != 0) return n;
1646        n = this.navigationHidden - that.navigationHidden;
1647        if (n != 0) return n;
1648        n = this.orientation - that.orientation;
1649        if (n != 0) return n;
1650        n = this.colorMode - that.colorMode;
1651        if (n != 0) return n;
1652        n = this.screenLayout - that.screenLayout;
1653        if (n != 0) return n;
1654        n = this.uiMode - that.uiMode;
1655        if (n != 0) return n;
1656        n = this.screenWidthDp - that.screenWidthDp;
1657        if (n != 0) return n;
1658        n = this.screenHeightDp - that.screenHeightDp;
1659        if (n != 0) return n;
1660        n = this.smallestScreenWidthDp - that.smallestScreenWidthDp;
1661        if (n != 0) return n;
1662        n = this.densityDpi - that.densityDpi;
1663        if (n != 0) return n;
1664        n = this.assetsSeq - that.assetsSeq;
1665        if (n != 0) return n;
1666
1667        if (this.appBounds == null && that.appBounds != null) {
1668            return 1;
1669        } else if (this.appBounds != null && that.appBounds == null) {
1670            return -1;
1671        } else if (this.appBounds != null && that.appBounds != null) {
1672            n = this.appBounds.left - that.appBounds.left;
1673            if (n != 0) return n;
1674            n = this.appBounds.top - that.appBounds.top;
1675            if (n != 0) return n;
1676            n = this.appBounds.right - that.appBounds.right;
1677            if (n != 0) return n;
1678            n = this.appBounds.bottom - that.appBounds.bottom;
1679            if (n != 0) return n;
1680        }
1681
1682        // if (n != 0) return n;
1683        return n;
1684    }
1685
1686    public boolean equals(Configuration that) {
1687        if (that == null) return false;
1688        if (that == this) return true;
1689        return this.compareTo(that) == 0;
1690    }
1691
1692    public boolean equals(Object that) {
1693        try {
1694            return equals((Configuration)that);
1695        } catch (ClassCastException e) {
1696        }
1697        return false;
1698    }
1699
1700    public int hashCode() {
1701        int result = 17;
1702        result = 31 * result + Float.floatToIntBits(fontScale);
1703        result = 31 * result + mcc;
1704        result = 31 * result + mnc;
1705        result = 31 * result + mLocaleList.hashCode();
1706        result = 31 * result + touchscreen;
1707        result = 31 * result + keyboard;
1708        result = 31 * result + keyboardHidden;
1709        result = 31 * result + hardKeyboardHidden;
1710        result = 31 * result + navigation;
1711        result = 31 * result + navigationHidden;
1712        result = 31 * result + orientation;
1713        result = 31 * result + screenLayout;
1714        result = 31 * result + colorMode;
1715        result = 31 * result + uiMode;
1716        result = 31 * result + screenWidthDp;
1717        result = 31 * result + screenHeightDp;
1718        result = 31 * result + smallestScreenWidthDp;
1719        result = 31 * result + densityDpi;
1720        result = 31 * result + assetsSeq;
1721        return result;
1722    }
1723
1724    /**
1725     * Get the locale list. This is the preferred way for getting the locales (instead of using
1726     * the direct accessor to {@link #locale}, which would only provide the primary locale).
1727     *
1728     * @return The locale list.
1729     */
1730    public @NonNull LocaleList getLocales() {
1731        fixUpLocaleList();
1732        return mLocaleList;
1733    }
1734
1735    /**
1736     * Set the locale list. This is the preferred way for setting up the locales (instead of using
1737     * the direct accessor or {@link #setLocale(Locale)}). This will also set the layout direction
1738     * according to the first locale in the list.
1739     *
1740     * Note that the layout direction will always come from the first locale in the locale list,
1741     * even if the locale is not supported by the resources (the resources may only support
1742     * another locale further down the list which has a different direction).
1743     *
1744     * @param locales The locale list. If null, an empty LocaleList will be assigned.
1745     */
1746    public void setLocales(@Nullable LocaleList locales) {
1747        mLocaleList = locales == null ? LocaleList.getEmptyLocaleList() : locales;
1748        locale = mLocaleList.get(0);
1749        setLayoutDirection(locale);
1750    }
1751
1752    /**
1753     * Set the locale list to a list of just one locale. This will also set the layout direction
1754     * according to the locale.
1755     *
1756     * Note that after this is run, calling <code>.equals()</code> on the input locale and the
1757     * {@link #locale} attribute would return <code>true</code> if they are not null, but there is
1758     * no guarantee that they would be the same object.
1759     *
1760     * See also the note about layout direction in {@link #setLocales(LocaleList)}.
1761     *
1762     * @param loc The locale. Can be null.
1763     */
1764    public void setLocale(@Nullable Locale loc) {
1765        setLocales(loc == null ? LocaleList.getEmptyLocaleList() : new LocaleList(loc));
1766    }
1767
1768    /**
1769     * @hide
1770     *
1771     * Helper method for setting the app bounds.
1772     */
1773    public void setAppBounds(Rect rect) {
1774        if (rect == null) {
1775            appBounds = null;
1776            return;
1777        }
1778
1779        setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
1780    }
1781
1782    /**
1783     * @hide
1784     *
1785     * Helper method for setting the app bounds.
1786     */
1787    public void setAppBounds(int left, int top, int right, int bottom) {
1788        if (appBounds == null) {
1789            appBounds = new Rect();
1790        }
1791
1792        appBounds.set(left, top, right, bottom);
1793    }
1794
1795    /**
1796     * @hide
1797     *
1798     * Clears the locale without changing layout direction.
1799     */
1800    public void clearLocales() {
1801        mLocaleList = LocaleList.getEmptyLocaleList();
1802        locale = null;
1803    }
1804
1805    /**
1806     * Return the layout direction. Will be either {@link View#LAYOUT_DIRECTION_LTR} or
1807     * {@link View#LAYOUT_DIRECTION_RTL}.
1808     *
1809     * @return Returns {@link View#LAYOUT_DIRECTION_RTL} if the configuration
1810     * is {@link #SCREENLAYOUT_LAYOUTDIR_RTL}, otherwise {@link View#LAYOUT_DIRECTION_LTR}.
1811     */
1812    public int getLayoutDirection() {
1813        return (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK) == SCREENLAYOUT_LAYOUTDIR_RTL
1814                ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
1815    }
1816
1817    /**
1818     * Set the layout direction from a Locale.
1819     *
1820     * @param loc The Locale. If null will set the layout direction to
1821     * {@link View#LAYOUT_DIRECTION_LTR}. If not null will set it to the layout direction
1822     * corresponding to the Locale.
1823     *
1824     * @see View#LAYOUT_DIRECTION_LTR
1825     * @see View#LAYOUT_DIRECTION_RTL
1826     */
1827    public void setLayoutDirection(Locale loc) {
1828        // There is a "1" difference between the configuration values for
1829        // layout direction and View constants for layout direction, just add "1".
1830        final int layoutDirection = 1 + TextUtils.getLayoutDirectionFromLocale(loc);
1831        screenLayout = (screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK)|
1832                (layoutDirection << SCREENLAYOUT_LAYOUTDIR_SHIFT);
1833    }
1834
1835    private static int getScreenLayoutNoDirection(int screenLayout) {
1836        return screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK;
1837    }
1838
1839    /**
1840     * Return whether the screen has a round shape. Apps may choose to change styling based
1841     * on this property, such as the alignment or layout of text or informational icons.
1842     *
1843     * @return true if the screen is rounded, false otherwise
1844     */
1845    public boolean isScreenRound() {
1846        return (screenLayout & SCREENLAYOUT_ROUND_MASK) == SCREENLAYOUT_ROUND_YES;
1847    }
1848
1849    /**
1850     * Return whether the screen has a wide color gamut and wide color gamut rendering
1851     * is supported by this device.
1852     *
1853     * @return true if the screen has a wide color gamut and wide color gamut rendering
1854     * is supported, false otherwise
1855     */
1856    public boolean isScreenWideColorGamut() {
1857        return (colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) == COLOR_MODE_WIDE_COLOR_GAMUT_YES;
1858    }
1859
1860    /**
1861     * Return whether the screen has a high dynamic range.
1862     *
1863     * @return true if the screen has a high dynamic range, false otherwise
1864     */
1865    public boolean isScreenHdr() {
1866        return (colorMode & COLOR_MODE_HDR_MASK) == COLOR_MODE_HDR_YES;
1867    }
1868
1869    /**
1870     *
1871     * @hide
1872     */
1873    public static String localesToResourceQualifier(LocaleList locs) {
1874        final StringBuilder sb = new StringBuilder();
1875        for (int i = 0; i < locs.size(); i++) {
1876            final Locale loc = locs.get(i);
1877            final int l = loc.getLanguage().length();
1878            if (l == 0) {
1879                continue;
1880            }
1881            final int s = loc.getScript().length();
1882            final int c = loc.getCountry().length();
1883            final int v = loc.getVariant().length();
1884            // We ignore locale extensions, since they are not supported by AAPT
1885
1886            if (sb.length() != 0) {
1887                sb.append(",");
1888            }
1889            if (l == 2 && s == 0 && (c == 0 || c == 2) && v == 0) {
1890                // Traditional locale format: xx or xx-rYY
1891                sb.append(loc.getLanguage());
1892                if (c == 2) {
1893                    sb.append("-r").append(loc.getCountry());
1894                }
1895            } else {
1896                sb.append("b+");
1897                sb.append(loc.getLanguage());
1898                if (s != 0) {
1899                    sb.append("+");
1900                    sb.append(loc.getScript());
1901                }
1902                if (c != 0) {
1903                    sb.append("+");
1904                    sb.append(loc.getCountry());
1905                }
1906                if (v != 0) {
1907                    sb.append("+");
1908                    sb.append(loc.getVariant());
1909                }
1910            }
1911        }
1912        return sb.toString();
1913    }
1914
1915
1916    /**
1917     * Returns a string representation of the configuration that can be parsed
1918     * by build tools (like AAPT).
1919     *
1920     * @hide
1921     */
1922    public static String resourceQualifierString(Configuration config) {
1923        ArrayList<String> parts = new ArrayList<String>();
1924
1925        if (config.mcc != 0) {
1926            parts.add("mcc" + config.mcc);
1927            if (config.mnc != 0) {
1928                parts.add("mnc" + config.mnc);
1929            }
1930        }
1931
1932        if (!config.mLocaleList.isEmpty()) {
1933            final String resourceQualifier = localesToResourceQualifier(config.mLocaleList);
1934            if (!resourceQualifier.isEmpty()) {
1935                parts.add(resourceQualifier);
1936            }
1937        }
1938
1939        switch (config.screenLayout & Configuration.SCREENLAYOUT_LAYOUTDIR_MASK) {
1940            case Configuration.SCREENLAYOUT_LAYOUTDIR_LTR:
1941                parts.add("ldltr");
1942                break;
1943            case Configuration.SCREENLAYOUT_LAYOUTDIR_RTL:
1944                parts.add("ldrtl");
1945                break;
1946            default:
1947                break;
1948        }
1949
1950        if (config.smallestScreenWidthDp != 0) {
1951            parts.add("sw" + config.smallestScreenWidthDp + "dp");
1952        }
1953
1954        if (config.screenWidthDp != 0) {
1955            parts.add("w" + config.screenWidthDp + "dp");
1956        }
1957
1958        if (config.screenHeightDp != 0) {
1959            parts.add("h" + config.screenHeightDp + "dp");
1960        }
1961
1962        switch (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) {
1963            case Configuration.SCREENLAYOUT_SIZE_SMALL:
1964                parts.add("small");
1965                break;
1966            case Configuration.SCREENLAYOUT_SIZE_NORMAL:
1967                parts.add("normal");
1968                break;
1969            case Configuration.SCREENLAYOUT_SIZE_LARGE:
1970                parts.add("large");
1971                break;
1972            case Configuration.SCREENLAYOUT_SIZE_XLARGE:
1973                parts.add("xlarge");
1974                break;
1975            default:
1976                break;
1977        }
1978
1979        switch (config.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK) {
1980            case Configuration.SCREENLAYOUT_LONG_YES:
1981                parts.add("long");
1982                break;
1983            case Configuration.SCREENLAYOUT_LONG_NO:
1984                parts.add("notlong");
1985                break;
1986            default:
1987                break;
1988        }
1989
1990        switch (config.screenLayout & Configuration.SCREENLAYOUT_ROUND_MASK) {
1991            case Configuration.SCREENLAYOUT_ROUND_YES:
1992                parts.add("round");
1993                break;
1994            case Configuration.SCREENLAYOUT_ROUND_NO:
1995                parts.add("notround");
1996                break;
1997            default:
1998                break;
1999        }
2000
2001        switch (config.colorMode & Configuration.COLOR_MODE_HDR_MASK) {
2002            case Configuration.COLOR_MODE_HDR_YES:
2003                parts.add("highdr");
2004                break;
2005            case Configuration.COLOR_MODE_HDR_NO:
2006                parts.add("lowdr");
2007                break;
2008            default:
2009                break;
2010        }
2011
2012        switch (config.colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK) {
2013            case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES:
2014                parts.add("widecg");
2015                break;
2016            case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO:
2017                parts.add("nowidecg");
2018                break;
2019            default:
2020                break;
2021        }
2022
2023        switch (config.orientation) {
2024            case Configuration.ORIENTATION_LANDSCAPE:
2025                parts.add("land");
2026                break;
2027            case Configuration.ORIENTATION_PORTRAIT:
2028                parts.add("port");
2029                break;
2030            default:
2031                break;
2032        }
2033
2034        switch (config.uiMode & Configuration.UI_MODE_TYPE_MASK) {
2035            case Configuration.UI_MODE_TYPE_APPLIANCE:
2036                parts.add("appliance");
2037                break;
2038            case Configuration.UI_MODE_TYPE_DESK:
2039                parts.add("desk");
2040                break;
2041            case Configuration.UI_MODE_TYPE_TELEVISION:
2042                parts.add("television");
2043                break;
2044            case Configuration.UI_MODE_TYPE_CAR:
2045                parts.add("car");
2046                break;
2047            case Configuration.UI_MODE_TYPE_WATCH:
2048                parts.add("watch");
2049                break;
2050            case Configuration.UI_MODE_TYPE_VR_HEADSET:
2051                parts.add("vrheadset");
2052                break;
2053            default:
2054                break;
2055        }
2056
2057        switch (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) {
2058            case Configuration.UI_MODE_NIGHT_YES:
2059                parts.add("night");
2060                break;
2061            case Configuration.UI_MODE_NIGHT_NO:
2062                parts.add("notnight");
2063                break;
2064            default:
2065                break;
2066        }
2067
2068        switch (config.densityDpi) {
2069            case DENSITY_DPI_UNDEFINED:
2070                break;
2071            case 120:
2072                parts.add("ldpi");
2073                break;
2074            case 160:
2075                parts.add("mdpi");
2076                break;
2077            case 213:
2078                parts.add("tvdpi");
2079                break;
2080            case 240:
2081                parts.add("hdpi");
2082                break;
2083            case 320:
2084                parts.add("xhdpi");
2085                break;
2086            case 480:
2087                parts.add("xxhdpi");
2088                break;
2089            case 640:
2090                parts.add("xxxhdpi");
2091                break;
2092            case DENSITY_DPI_ANY:
2093                parts.add("anydpi");
2094                break;
2095            case DENSITY_DPI_NONE:
2096                parts.add("nodpi");
2097            default:
2098                parts.add(config.densityDpi + "dpi");
2099                break;
2100        }
2101
2102        switch (config.touchscreen) {
2103            case Configuration.TOUCHSCREEN_NOTOUCH:
2104                parts.add("notouch");
2105                break;
2106            case Configuration.TOUCHSCREEN_FINGER:
2107                parts.add("finger");
2108                break;
2109            default:
2110                break;
2111        }
2112
2113        switch (config.keyboardHidden) {
2114            case Configuration.KEYBOARDHIDDEN_NO:
2115                parts.add("keysexposed");
2116                break;
2117            case Configuration.KEYBOARDHIDDEN_YES:
2118                parts.add("keyshidden");
2119                break;
2120            case Configuration.KEYBOARDHIDDEN_SOFT:
2121                parts.add("keyssoft");
2122                break;
2123            default:
2124                break;
2125        }
2126
2127        switch (config.keyboard) {
2128            case Configuration.KEYBOARD_NOKEYS:
2129                parts.add("nokeys");
2130                break;
2131            case Configuration.KEYBOARD_QWERTY:
2132                parts.add("qwerty");
2133                break;
2134            case Configuration.KEYBOARD_12KEY:
2135                parts.add("12key");
2136                break;
2137            default:
2138                break;
2139        }
2140
2141        switch (config.navigationHidden) {
2142            case Configuration.NAVIGATIONHIDDEN_NO:
2143                parts.add("navexposed");
2144                break;
2145            case Configuration.NAVIGATIONHIDDEN_YES:
2146                parts.add("navhidden");
2147                break;
2148            default:
2149                break;
2150        }
2151
2152        switch (config.navigation) {
2153            case Configuration.NAVIGATION_NONAV:
2154                parts.add("nonav");
2155                break;
2156            case Configuration.NAVIGATION_DPAD:
2157                parts.add("dpad");
2158                break;
2159            case Configuration.NAVIGATION_TRACKBALL:
2160                parts.add("trackball");
2161                break;
2162            case Configuration.NAVIGATION_WHEEL:
2163                parts.add("wheel");
2164                break;
2165            default:
2166                break;
2167        }
2168
2169        parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
2170        return TextUtils.join("-", parts);
2171    }
2172
2173    /**
2174     * Generate a delta Configuration between <code>base</code> and <code>change</code>. The
2175     * resulting delta can be used with {@link #updateFrom(Configuration)}.
2176     * <p />
2177     * Caveat: If the any of the Configuration's members becomes undefined, then
2178     * {@link #updateFrom(Configuration)} will treat it as a no-op and not update that member.
2179     *
2180     * This is fine for device configurations as no member is ever undefined.
2181     * {@hide}
2182     */
2183    public static Configuration generateDelta(Configuration base, Configuration change) {
2184        final Configuration delta = new Configuration();
2185        if (base.fontScale != change.fontScale) {
2186            delta.fontScale = change.fontScale;
2187        }
2188
2189        if (base.mcc != change.mcc) {
2190            delta.mcc = change.mcc;
2191        }
2192
2193        if (base.mnc != change.mnc) {
2194            delta.mnc = change.mnc;
2195        }
2196
2197        base.fixUpLocaleList();
2198        change.fixUpLocaleList();
2199        if (!base.mLocaleList.equals(change.mLocaleList))  {
2200            delta.mLocaleList = change.mLocaleList;
2201            delta.locale = change.locale;
2202        }
2203
2204        if (base.touchscreen != change.touchscreen) {
2205            delta.touchscreen = change.touchscreen;
2206        }
2207
2208        if (base.keyboard != change.keyboard) {
2209            delta.keyboard = change.keyboard;
2210        }
2211
2212        if (base.keyboardHidden != change.keyboardHidden) {
2213            delta.keyboardHidden = change.keyboardHidden;
2214        }
2215
2216        if (base.navigation != change.navigation) {
2217            delta.navigation = change.navigation;
2218        }
2219
2220        if (base.navigationHidden != change.navigationHidden) {
2221            delta.navigationHidden = change.navigationHidden;
2222        }
2223
2224        if (base.orientation != change.orientation) {
2225            delta.orientation = change.orientation;
2226        }
2227
2228        if ((base.screenLayout & SCREENLAYOUT_SIZE_MASK) !=
2229                (change.screenLayout & SCREENLAYOUT_SIZE_MASK)) {
2230            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_SIZE_MASK;
2231        }
2232
2233        if ((base.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK) !=
2234                (change.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK)) {
2235            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LAYOUTDIR_MASK;
2236        }
2237
2238        if ((base.screenLayout & SCREENLAYOUT_LONG_MASK) !=
2239                (change.screenLayout & SCREENLAYOUT_LONG_MASK)) {
2240            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LONG_MASK;
2241        }
2242
2243        if ((base.screenLayout & SCREENLAYOUT_ROUND_MASK) !=
2244                (change.screenLayout & SCREENLAYOUT_ROUND_MASK)) {
2245            delta.screenLayout |= change.screenLayout & SCREENLAYOUT_ROUND_MASK;
2246        }
2247
2248        if ((base.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
2249                (change.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
2250            delta.colorMode |= change.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK;
2251        }
2252
2253        if ((base.colorMode & COLOR_MODE_HDR_MASK) !=
2254                (change.colorMode & COLOR_MODE_HDR_MASK)) {
2255            delta.colorMode |= change.colorMode & COLOR_MODE_HDR_MASK;
2256        }
2257
2258        if ((base.uiMode & UI_MODE_TYPE_MASK) != (change.uiMode & UI_MODE_TYPE_MASK)) {
2259            delta.uiMode |= change.uiMode & UI_MODE_TYPE_MASK;
2260        }
2261
2262        if ((base.uiMode & UI_MODE_NIGHT_MASK) != (change.uiMode & UI_MODE_NIGHT_MASK)) {
2263            delta.uiMode |= change.uiMode & UI_MODE_NIGHT_MASK;
2264        }
2265
2266        if (base.screenWidthDp != change.screenWidthDp) {
2267            delta.screenWidthDp = change.screenWidthDp;
2268        }
2269
2270        if (base.screenHeightDp != change.screenHeightDp) {
2271            delta.screenHeightDp = change.screenHeightDp;
2272        }
2273
2274        if (base.smallestScreenWidthDp != change.smallestScreenWidthDp) {
2275            delta.smallestScreenWidthDp = change.smallestScreenWidthDp;
2276        }
2277
2278        if (base.densityDpi != change.densityDpi) {
2279            delta.densityDpi = change.densityDpi;
2280        }
2281
2282        if (base.assetsSeq != change.assetsSeq) {
2283            delta.assetsSeq = change.assetsSeq;
2284        }
2285        return delta;
2286    }
2287
2288    private static final String XML_ATTR_FONT_SCALE = "fs";
2289    private static final String XML_ATTR_MCC = "mcc";
2290    private static final String XML_ATTR_MNC = "mnc";
2291    private static final String XML_ATTR_LOCALES = "locales";
2292    private static final String XML_ATTR_TOUCHSCREEN = "touch";
2293    private static final String XML_ATTR_KEYBOARD = "key";
2294    private static final String XML_ATTR_KEYBOARD_HIDDEN = "keyHid";
2295    private static final String XML_ATTR_HARD_KEYBOARD_HIDDEN = "hardKeyHid";
2296    private static final String XML_ATTR_NAVIGATION = "nav";
2297    private static final String XML_ATTR_NAVIGATION_HIDDEN = "navHid";
2298    private static final String XML_ATTR_ORIENTATION = "ori";
2299    private static final String XML_ATTR_ROTATION = "rot";
2300    private static final String XML_ATTR_SCREEN_LAYOUT = "scrLay";
2301    private static final String XML_ATTR_COLOR_MODE = "clrMod";
2302    private static final String XML_ATTR_UI_MODE = "ui";
2303    private static final String XML_ATTR_SCREEN_WIDTH = "width";
2304    private static final String XML_ATTR_SCREEN_HEIGHT = "height";
2305    private static final String XML_ATTR_SMALLEST_WIDTH = "sw";
2306    private static final String XML_ATTR_DENSITY = "density";
2307    private static final String XML_ATTR_APP_BOUNDS = "app_bounds";
2308
2309    /**
2310     * Reads the attributes corresponding to Configuration member fields from the Xml parser.
2311     * The parser is expected to be on a tag which has Configuration attributes.
2312     *
2313     * @param parser The Xml parser from which to read attributes.
2314     * @param configOut The Configuration to populate from the Xml attributes.
2315     * {@hide}
2316     */
2317    public static void readXmlAttrs(XmlPullParser parser, Configuration configOut)
2318            throws XmlPullParserException, IOException {
2319        configOut.fontScale = Float.intBitsToFloat(
2320                XmlUtils.readIntAttribute(parser, XML_ATTR_FONT_SCALE, 0));
2321        configOut.mcc = XmlUtils.readIntAttribute(parser, XML_ATTR_MCC, 0);
2322        configOut.mnc = XmlUtils.readIntAttribute(parser, XML_ATTR_MNC, 0);
2323
2324        final String localesStr = XmlUtils.readStringAttribute(parser, XML_ATTR_LOCALES);
2325        configOut.mLocaleList = LocaleList.forLanguageTags(localesStr);
2326        configOut.locale = configOut.mLocaleList.get(0);
2327
2328        configOut.touchscreen = XmlUtils.readIntAttribute(parser, XML_ATTR_TOUCHSCREEN,
2329                TOUCHSCREEN_UNDEFINED);
2330        configOut.keyboard = XmlUtils.readIntAttribute(parser, XML_ATTR_KEYBOARD,
2331                KEYBOARD_UNDEFINED);
2332        configOut.keyboardHidden = XmlUtils.readIntAttribute(parser, XML_ATTR_KEYBOARD_HIDDEN,
2333                KEYBOARDHIDDEN_UNDEFINED);
2334        configOut.hardKeyboardHidden =
2335                XmlUtils.readIntAttribute(parser, XML_ATTR_HARD_KEYBOARD_HIDDEN,
2336                        HARDKEYBOARDHIDDEN_UNDEFINED);
2337        configOut.navigation = XmlUtils.readIntAttribute(parser, XML_ATTR_NAVIGATION,
2338                NAVIGATION_UNDEFINED);
2339        configOut.navigationHidden = XmlUtils.readIntAttribute(parser, XML_ATTR_NAVIGATION_HIDDEN,
2340                NAVIGATIONHIDDEN_UNDEFINED);
2341        configOut.orientation = XmlUtils.readIntAttribute(parser, XML_ATTR_ORIENTATION,
2342                ORIENTATION_UNDEFINED);
2343        configOut.screenLayout = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_LAYOUT,
2344                SCREENLAYOUT_UNDEFINED);
2345        configOut.colorMode = XmlUtils.readIntAttribute(parser, XML_ATTR_COLOR_MODE,
2346                COLOR_MODE_UNDEFINED);
2347        configOut.uiMode = XmlUtils.readIntAttribute(parser, XML_ATTR_UI_MODE, 0);
2348        configOut.screenWidthDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_WIDTH,
2349                SCREEN_WIDTH_DP_UNDEFINED);
2350        configOut.screenHeightDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_HEIGHT,
2351                SCREEN_HEIGHT_DP_UNDEFINED);
2352        configOut.smallestScreenWidthDp =
2353                XmlUtils.readIntAttribute(parser, XML_ATTR_SMALLEST_WIDTH,
2354                        SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
2355        configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
2356                DENSITY_DPI_UNDEFINED);
2357        configOut.appBounds =
2358            Rect.unflattenFromString(XmlUtils.readStringAttribute(parser, XML_ATTR_APP_BOUNDS));
2359
2360        // For persistence, we don't care about assetsSeq, so do not read it out.
2361    }
2362
2363
2364    /**
2365     * Writes the Configuration's member fields as attributes into the XmlSerializer.
2366     * The serializer is expected to have already started a tag so that attributes can be
2367     * immediately written.
2368     *
2369     * @param xml The serializer to which to write the attributes.
2370     * @param config The Configuration whose member fields to write.
2371     * {@hide}
2372     */
2373    public static void writeXmlAttrs(XmlSerializer xml, Configuration config) throws IOException {
2374        XmlUtils.writeIntAttribute(xml, XML_ATTR_FONT_SCALE,
2375                Float.floatToIntBits(config.fontScale));
2376        if (config.mcc != 0) {
2377            XmlUtils.writeIntAttribute(xml, XML_ATTR_MCC, config.mcc);
2378        }
2379        if (config.mnc != 0) {
2380            XmlUtils.writeIntAttribute(xml, XML_ATTR_MNC, config.mnc);
2381        }
2382        config.fixUpLocaleList();
2383        if (!config.mLocaleList.isEmpty()) {
2384           XmlUtils.writeStringAttribute(xml, XML_ATTR_LOCALES, config.mLocaleList.toLanguageTags());
2385        }
2386        if (config.touchscreen != TOUCHSCREEN_UNDEFINED) {
2387            XmlUtils.writeIntAttribute(xml, XML_ATTR_TOUCHSCREEN, config.touchscreen);
2388        }
2389        if (config.keyboard != KEYBOARD_UNDEFINED) {
2390            XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD, config.keyboard);
2391        }
2392        if (config.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED) {
2393            XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD_HIDDEN, config.keyboardHidden);
2394        }
2395        if (config.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED) {
2396            XmlUtils.writeIntAttribute(xml, XML_ATTR_HARD_KEYBOARD_HIDDEN,
2397                    config.hardKeyboardHidden);
2398        }
2399        if (config.navigation != NAVIGATION_UNDEFINED) {
2400            XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION, config.navigation);
2401        }
2402        if (config.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED) {
2403            XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION_HIDDEN, config.navigationHidden);
2404        }
2405        if (config.orientation != ORIENTATION_UNDEFINED) {
2406            XmlUtils.writeIntAttribute(xml, XML_ATTR_ORIENTATION, config.orientation);
2407        }
2408        if (config.screenLayout != SCREENLAYOUT_UNDEFINED) {
2409            XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_LAYOUT, config.screenLayout);
2410        }
2411        if (config.colorMode != COLOR_MODE_UNDEFINED) {
2412            XmlUtils.writeIntAttribute(xml, XML_ATTR_COLOR_MODE, config.colorMode);
2413        }
2414        if (config.uiMode != 0) {
2415            XmlUtils.writeIntAttribute(xml, XML_ATTR_UI_MODE, config.uiMode);
2416        }
2417        if (config.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
2418            XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_WIDTH, config.screenWidthDp);
2419        }
2420        if (config.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
2421            XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_HEIGHT, config.screenHeightDp);
2422        }
2423        if (config.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
2424            XmlUtils.writeIntAttribute(xml, XML_ATTR_SMALLEST_WIDTH, config.smallestScreenWidthDp);
2425        }
2426        if (config.densityDpi != DENSITY_DPI_UNDEFINED) {
2427            XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi);
2428        }
2429
2430        if (config.appBounds != null) {
2431            XmlUtils.writeStringAttribute(xml, XML_ATTR_APP_BOUNDS,
2432                config.appBounds.flattenToString());
2433        }
2434
2435        // For persistence, we do not care about assetsSeq, so do not write it out.
2436    }
2437}
2438