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.content.pm.ActivityInfo;
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.util.LocaleUtil;
23
24import java.util.Locale;
25
26/**
27 * This class describes all device configuration information that can
28 * impact the resources the application retrieves.  This includes both
29 * user-specified configuration options (locale and scaling) as well
30 * as device configurations (such as input modes, screen size and screen orientation).
31 * <p>You can acquire this object from {@link Resources}, using {@link
32 * Resources#getConfiguration}. Thus, from an activity, you can get it by chaining the request
33 * with {@link android.app.Activity#getResources}:</p>
34 * <pre>Configuration config = getResources().getConfiguration();</pre>
35 */
36public final class Configuration implements Parcelable, Comparable<Configuration> {
37    /**
38     * Current user preference for the scaling factor for fonts, relative
39     * to the base density scaling.
40     */
41    public float fontScale;
42
43    /**
44     * IMSI MCC (Mobile Country Code).  0 if undefined.
45     */
46    public int mcc;
47
48    /**
49     * IMSI MNC (Mobile Network Code).  0 if undefined.
50     */
51    public int mnc;
52
53    /**
54     * Current user preference for the locale.
55     */
56    public Locale locale;
57
58    /**
59     * Locale should persist on setting.  This is hidden because it is really
60     * questionable whether this is the right way to expose the functionality.
61     * @hide
62     */
63    public boolean userSetLocale;
64
65    /** Constant for {@link #screenLayout}: bits that encode the size. */
66    public static final int SCREENLAYOUT_SIZE_MASK = 0x0f;
67    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
68     * value indicating that no size has been set. */
69    public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0x00;
70    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
71     * value indicating the screen is at least approximately 320x426 dp units.
72     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
73     * Multiple Screens</a> for more information. */
74    public static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
75    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
76     * value indicating the screen is at least approximately 320x470 dp units.
77     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
78     * Multiple Screens</a> for more information. */
79    public static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
80    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
81     * value indicating the screen is at least approximately 480x640 dp units.
82     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
83     * Multiple Screens</a> for more information. */
84    public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
85    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
86     * value indicating the screen is at least approximately 720x960 dp units.
87     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
88     * Multiple Screens</a> for more information.*/
89    public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
90
91    public static final int SCREENLAYOUT_LONG_MASK = 0x30;
92    public static final int SCREENLAYOUT_LONG_UNDEFINED = 0x00;
93    public static final int SCREENLAYOUT_LONG_NO = 0x10;
94    public static final int SCREENLAYOUT_LONG_YES = 0x20;
95
96    /**
97     * Special flag we generate to indicate that the screen layout requires
98     * us to use a compatibility mode for apps that are not modern layout
99     * aware.
100     * @hide
101     */
102    public static final int SCREENLAYOUT_COMPAT_NEEDED = 0x10000000;
103
104    /**
105     * Bit mask of overall layout of the screen.  Currently there are two
106     * fields:
107     * <p>The {@link #SCREENLAYOUT_SIZE_MASK} bits define the overall size
108     * of the screen.  They may be one of
109     * {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL},
110     * {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}.
111     *
112     * <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
113     * is wider/taller than normal.  They may be one of
114     * {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.
115     *
116     * <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
117     * Multiple Screens</a> for more information.
118     */
119    public int screenLayout;
120
121    /**
122     * Check if the Configuration's current {@link #screenLayout} is at
123     * least the given size.
124     *
125     * @param size The desired size, either {@link #SCREENLAYOUT_SIZE_SMALL},
126     * {@link #SCREENLAYOUT_SIZE_NORMAL}, {@link #SCREENLAYOUT_SIZE_LARGE}, or
127     * {@link #SCREENLAYOUT_SIZE_XLARGE}.
128     * @return Returns true if the current screen layout size is at least
129     * the given size.
130     */
131    public boolean isLayoutSizeAtLeast(int size) {
132        int cur = screenLayout&SCREENLAYOUT_SIZE_MASK;
133        if (cur == SCREENLAYOUT_SIZE_UNDEFINED) return false;
134        return cur >= size;
135    }
136
137    public static final int TOUCHSCREEN_UNDEFINED = 0;
138    public static final int TOUCHSCREEN_NOTOUCH = 1;
139    public static final int TOUCHSCREEN_STYLUS = 2;
140    public static final int TOUCHSCREEN_FINGER = 3;
141
142    /**
143     * The kind of touch screen attached to the device.
144     * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_STYLUS},
145     * {@link #TOUCHSCREEN_FINGER}.
146     */
147    public int touchscreen;
148
149    public static final int KEYBOARD_UNDEFINED = 0;
150    public static final int KEYBOARD_NOKEYS = 1;
151    public static final int KEYBOARD_QWERTY = 2;
152    public static final int KEYBOARD_12KEY = 3;
153
154    /**
155     * The kind of keyboard attached to the device.
156     * One of: {@link #KEYBOARD_NOKEYS}, {@link #KEYBOARD_QWERTY},
157     * {@link #KEYBOARD_12KEY}.
158     */
159    public int keyboard;
160
161    public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
162    public static final int KEYBOARDHIDDEN_NO = 1;
163    public static final int KEYBOARDHIDDEN_YES = 2;
164    /** Constant matching actual resource implementation. {@hide} */
165    public static final int KEYBOARDHIDDEN_SOFT = 3;
166
167    /**
168     * A flag indicating whether any keyboard is available.  Unlike
169     * {@link #hardKeyboardHidden}, this also takes into account a soft
170     * keyboard, so if the hard keyboard is hidden but there is soft
171     * keyboard available, it will be set to NO.  Value is one of:
172     * {@link #KEYBOARDHIDDEN_NO}, {@link #KEYBOARDHIDDEN_YES}.
173     */
174    public int keyboardHidden;
175
176    public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0;
177    public static final int HARDKEYBOARDHIDDEN_NO = 1;
178    public static final int HARDKEYBOARDHIDDEN_YES = 2;
179
180    /**
181     * A flag indicating whether the hard keyboard has been hidden.  This will
182     * be set on a device with a mechanism to hide the keyboard from the
183     * user, when that mechanism is closed.  One of:
184     * {@link #HARDKEYBOARDHIDDEN_NO}, {@link #HARDKEYBOARDHIDDEN_YES}.
185     */
186    public int hardKeyboardHidden;
187
188    public static final int NAVIGATION_UNDEFINED = 0;
189    public static final int NAVIGATION_NONAV = 1;
190    public static final int NAVIGATION_DPAD = 2;
191    public static final int NAVIGATION_TRACKBALL = 3;
192    public static final int NAVIGATION_WHEEL = 4;
193
194    /**
195     * The kind of navigation method available on the device.
196     * One of: {@link #NAVIGATION_NONAV}, {@link #NAVIGATION_DPAD},
197     * {@link #NAVIGATION_TRACKBALL}, {@link #NAVIGATION_WHEEL}.
198     */
199    public int navigation;
200
201    public static final int NAVIGATIONHIDDEN_UNDEFINED = 0;
202    public static final int NAVIGATIONHIDDEN_NO = 1;
203    public static final int NAVIGATIONHIDDEN_YES = 2;
204
205    /**
206     * A flag indicating whether any 5-way or DPAD navigation available.
207     * This will be set on a device with a mechanism to hide the navigation
208     * controls from the user, when that mechanism is closed.  One of:
209     * {@link #NAVIGATIONHIDDEN_NO}, {@link #NAVIGATIONHIDDEN_YES}.
210     */
211    public int navigationHidden;
212
213    public static final int ORIENTATION_UNDEFINED = 0;
214    public static final int ORIENTATION_PORTRAIT = 1;
215    public static final int ORIENTATION_LANDSCAPE = 2;
216    public static final int ORIENTATION_SQUARE = 3;
217
218    /**
219     * Overall orientation of the screen.  May be one of
220     * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT},
221     * or {@link #ORIENTATION_SQUARE}.
222     */
223    public int orientation;
224
225    public static final int UI_MODE_TYPE_MASK = 0x0f;
226    public static final int UI_MODE_TYPE_UNDEFINED = 0x00;
227    public static final int UI_MODE_TYPE_NORMAL = 0x01;
228    public static final int UI_MODE_TYPE_DESK = 0x02;
229    public static final int UI_MODE_TYPE_CAR = 0x03;
230    public static final int UI_MODE_TYPE_TELEVISION = 0x04;
231
232    public static final int UI_MODE_NIGHT_MASK = 0x30;
233    public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
234    public static final int UI_MODE_NIGHT_NO = 0x10;
235    public static final int UI_MODE_NIGHT_YES = 0x20;
236
237    /**
238     * Bit mask of the ui mode.  Currently there are two fields:
239     * <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the
240     * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED},
241     * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK},
242     * or {@link #UI_MODE_TYPE_CAR}.
243     *
244     * <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen
245     * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED},
246     * {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.
247     */
248    public int uiMode;
249
250    public static final int SCREEN_WIDTH_DP_UNDEFINED = 0;
251
252    /**
253     * The current width of the available screen space, in dp units.
254     */
255    public int screenWidthDp;
256
257    public static final int SCREEN_HEIGHT_DP_UNDEFINED = 0;
258
259    /**
260     * The current height of the available screen space, in dp units.
261     */
262    public int screenHeightDp;
263
264    public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0;
265
266    /**
267     * The smallest screen size an application will see in normal operation.
268     * This is the smallest value of both screenWidthDp and screenHeightDp
269     * in both portrait and landscape.
270     */
271    public int smallestScreenWidthDp;
272
273    /** @hide Hack to get this information from WM to app running in compat mode. */
274    public int compatScreenWidthDp;
275    /** @hide Hack to get this information from WM to app running in compat mode. */
276    public int compatScreenHeightDp;
277    /** @hide Hack to get this information from WM to app running in compat mode. */
278    public int compatSmallestScreenWidthDp;
279
280    /**
281     * @hide The text layout direction associated to the current Locale
282     */
283    public int textLayoutDirection;
284
285    /**
286     * @hide Internal book-keeping.
287     */
288    public int seq;
289
290    /**
291     * Construct an invalid Configuration.  You must call {@link #setToDefaults}
292     * for this object to be valid.  {@more}
293     */
294    public Configuration() {
295        setToDefaults();
296    }
297
298    /**
299     * Makes a deep copy suitable for modification.
300     */
301    public Configuration(Configuration o) {
302        setTo(o);
303    }
304
305    public void setTo(Configuration o) {
306        fontScale = o.fontScale;
307        mcc = o.mcc;
308        mnc = o.mnc;
309        if (o.locale != null) {
310            locale = (Locale) o.locale.clone();
311            textLayoutDirection = o.textLayoutDirection;
312        }
313        userSetLocale = o.userSetLocale;
314        touchscreen = o.touchscreen;
315        keyboard = o.keyboard;
316        keyboardHidden = o.keyboardHidden;
317        hardKeyboardHidden = o.hardKeyboardHidden;
318        navigation = o.navigation;
319        navigationHidden = o.navigationHidden;
320        orientation = o.orientation;
321        screenLayout = o.screenLayout;
322        uiMode = o.uiMode;
323        screenWidthDp = o.screenWidthDp;
324        screenHeightDp = o.screenHeightDp;
325        smallestScreenWidthDp = o.smallestScreenWidthDp;
326        compatScreenWidthDp = o.compatScreenWidthDp;
327        compatScreenHeightDp = o.compatScreenHeightDp;
328        compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp;
329        seq = o.seq;
330    }
331
332    public String toString() {
333        StringBuilder sb = new StringBuilder(128);
334        sb.append("{");
335        sb.append(fontScale);
336        sb.append(" ");
337        sb.append(mcc);
338        sb.append("mcc");
339        sb.append(mnc);
340        sb.append("mnc");
341        if (locale != null) {
342            sb.append(" ");
343            sb.append(locale);
344        } else {
345            sb.append(" (no locale)");
346        }
347        switch (textLayoutDirection) {
348            case LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE: sb.append(" rtl"); break;
349            default: sb.append(" layoutdir="); sb.append(textLayoutDirection); break;
350        }
351        if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
352            sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
353        } else {
354            sb.append(" ?swdp");
355        }
356        if (screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
357            sb.append(" w"); sb.append(screenWidthDp); sb.append("dp");
358        } else {
359            sb.append(" ?wdp");
360        }
361        if (screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
362            sb.append(" h"); sb.append(screenHeightDp); sb.append("dp");
363        } else {
364            sb.append(" ?hdp");
365        }
366        switch ((screenLayout&SCREENLAYOUT_SIZE_MASK)) {
367            case SCREENLAYOUT_SIZE_UNDEFINED: sb.append(" ?lsize"); break;
368            case SCREENLAYOUT_SIZE_SMALL: sb.append(" smll"); break;
369            case SCREENLAYOUT_SIZE_NORMAL: sb.append(" nrml"); break;
370            case SCREENLAYOUT_SIZE_LARGE: sb.append(" lrg"); break;
371            case SCREENLAYOUT_SIZE_XLARGE: sb.append(" xlrg"); break;
372            default: sb.append(" layoutSize=");
373                    sb.append(screenLayout&SCREENLAYOUT_SIZE_MASK); break;
374        }
375        switch ((screenLayout&SCREENLAYOUT_LONG_MASK)) {
376            case SCREENLAYOUT_LONG_UNDEFINED: sb.append(" ?long"); break;
377            case SCREENLAYOUT_LONG_NO: /* not-long is not interesting to print */ break;
378            case SCREENLAYOUT_LONG_YES: sb.append(" long"); break;
379            default: sb.append(" layoutLong=");
380                    sb.append(screenLayout&SCREENLAYOUT_LONG_MASK); break;
381        }
382        switch (orientation) {
383            case ORIENTATION_UNDEFINED: sb.append(" ?orien"); break;
384            case ORIENTATION_LANDSCAPE: sb.append(" land"); break;
385            case ORIENTATION_PORTRAIT: sb.append(" port"); break;
386            default: sb.append(" orien="); sb.append(orientation); break;
387        }
388        switch ((uiMode&UI_MODE_TYPE_MASK)) {
389            case UI_MODE_TYPE_UNDEFINED: sb.append(" ?uimode"); break;
390            case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break;
391            case UI_MODE_TYPE_DESK: sb.append(" desk"); break;
392            case UI_MODE_TYPE_CAR: sb.append(" car"); break;
393            case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break;
394            default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break;
395        }
396        switch ((uiMode&UI_MODE_NIGHT_MASK)) {
397            case UI_MODE_NIGHT_UNDEFINED: sb.append(" ?night"); break;
398            case UI_MODE_NIGHT_NO: /* not-night is not interesting to print */ break;
399            case UI_MODE_NIGHT_YES: sb.append(" night"); break;
400            default: sb.append(" night="); sb.append(uiMode&UI_MODE_NIGHT_MASK); break;
401        }
402        switch (touchscreen) {
403            case TOUCHSCREEN_UNDEFINED: sb.append(" ?touch"); break;
404            case TOUCHSCREEN_NOTOUCH: sb.append(" -touch"); break;
405            case TOUCHSCREEN_STYLUS: sb.append(" stylus"); break;
406            case TOUCHSCREEN_FINGER: sb.append(" finger"); break;
407            default: sb.append(" touch="); sb.append(touchscreen); break;
408        }
409        switch (keyboard) {
410            case KEYBOARD_UNDEFINED: sb.append(" ?keyb"); break;
411            case KEYBOARD_NOKEYS: sb.append(" -keyb"); break;
412            case KEYBOARD_QWERTY: sb.append(" qwerty"); break;
413            case KEYBOARD_12KEY: sb.append(" 12key"); break;
414            default: sb.append(" keys="); sb.append(keyboard); break;
415        }
416        switch (keyboardHidden) {
417            case KEYBOARDHIDDEN_UNDEFINED: sb.append("/?"); break;
418            case KEYBOARDHIDDEN_NO: sb.append("/v"); break;
419            case KEYBOARDHIDDEN_YES: sb.append("/h"); break;
420            case KEYBOARDHIDDEN_SOFT: sb.append("/s"); break;
421            default: sb.append("/"); sb.append(keyboardHidden); break;
422        }
423        switch (hardKeyboardHidden) {
424            case HARDKEYBOARDHIDDEN_UNDEFINED: sb.append("/?"); break;
425            case HARDKEYBOARDHIDDEN_NO: sb.append("/v"); break;
426            case HARDKEYBOARDHIDDEN_YES: sb.append("/h"); break;
427            default: sb.append("/"); sb.append(hardKeyboardHidden); break;
428        }
429        switch (navigation) {
430            case NAVIGATION_UNDEFINED: sb.append(" ?nav"); break;
431            case NAVIGATION_NONAV: sb.append(" -nav"); break;
432            case NAVIGATION_DPAD: sb.append(" dpad"); break;
433            case NAVIGATION_TRACKBALL: sb.append(" tball"); break;
434            case NAVIGATION_WHEEL: sb.append(" wheel"); break;
435            default: sb.append(" nav="); sb.append(navigation); break;
436        }
437        switch (navigationHidden) {
438            case NAVIGATIONHIDDEN_UNDEFINED: sb.append("/?"); break;
439            case NAVIGATIONHIDDEN_NO: sb.append("/v"); break;
440            case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
441            default: sb.append("/"); sb.append(navigationHidden); break;
442        }
443        if (seq != 0) {
444            sb.append(" s.");
445            sb.append(seq);
446        }
447        sb.append('}');
448        return sb.toString();
449    }
450
451    /**
452     * Set this object to the system defaults.
453     */
454    public void setToDefaults() {
455        fontScale = 1;
456        mcc = mnc = 0;
457        locale = null;
458        userSetLocale = false;
459        touchscreen = TOUCHSCREEN_UNDEFINED;
460        keyboard = KEYBOARD_UNDEFINED;
461        keyboardHidden = KEYBOARDHIDDEN_UNDEFINED;
462        hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED;
463        navigation = NAVIGATION_UNDEFINED;
464        navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
465        orientation = ORIENTATION_UNDEFINED;
466        screenLayout = SCREENLAYOUT_SIZE_UNDEFINED;
467        uiMode = UI_MODE_TYPE_UNDEFINED;
468        screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
469        screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
470        smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
471        textLayoutDirection = LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
472        seq = 0;
473    }
474
475    /** {@hide} */
476    @Deprecated public void makeDefault() {
477        setToDefaults();
478    }
479
480    /**
481     * Copy the fields from delta into this Configuration object, keeping
482     * track of which ones have changed.  Any undefined fields in
483     * <var>delta</var> are ignored and not copied in to the current
484     * Configuration.
485     * @return Returns a bit mask of the changed fields, as per
486     * {@link #diff}.
487     */
488    public int updateFrom(Configuration delta) {
489        int changed = 0;
490        if (delta.fontScale > 0 && fontScale != delta.fontScale) {
491            changed |= ActivityInfo.CONFIG_FONT_SCALE;
492            fontScale = delta.fontScale;
493        }
494        if (delta.mcc != 0 && mcc != delta.mcc) {
495            changed |= ActivityInfo.CONFIG_MCC;
496            mcc = delta.mcc;
497        }
498        if (delta.mnc != 0 && mnc != delta.mnc) {
499            changed |= ActivityInfo.CONFIG_MNC;
500            mnc = delta.mnc;
501        }
502        if (delta.locale != null
503                && (locale == null || !locale.equals(delta.locale))) {
504            changed |= ActivityInfo.CONFIG_LOCALE;
505            locale = delta.locale != null
506                    ? (Locale) delta.locale.clone() : null;
507            textLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
508        }
509        if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
510        {
511            userSetLocale = true;
512            changed |= ActivityInfo.CONFIG_LOCALE;
513        }
514        if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
515                && touchscreen != delta.touchscreen) {
516            changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
517            touchscreen = delta.touchscreen;
518        }
519        if (delta.keyboard != KEYBOARD_UNDEFINED
520                && keyboard != delta.keyboard) {
521            changed |= ActivityInfo.CONFIG_KEYBOARD;
522            keyboard = delta.keyboard;
523        }
524        if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
525                && keyboardHidden != delta.keyboardHidden) {
526            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
527            keyboardHidden = delta.keyboardHidden;
528        }
529        if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
530                && hardKeyboardHidden != delta.hardKeyboardHidden) {
531            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
532            hardKeyboardHidden = delta.hardKeyboardHidden;
533        }
534        if (delta.navigation != NAVIGATION_UNDEFINED
535                && navigation != delta.navigation) {
536            changed |= ActivityInfo.CONFIG_NAVIGATION;
537            navigation = delta.navigation;
538        }
539        if (delta.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED
540                && navigationHidden != delta.navigationHidden) {
541            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
542            navigationHidden = delta.navigationHidden;
543        }
544        if (delta.orientation != ORIENTATION_UNDEFINED
545                && orientation != delta.orientation) {
546            changed |= ActivityInfo.CONFIG_ORIENTATION;
547            orientation = delta.orientation;
548        }
549        if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
550                && screenLayout != delta.screenLayout) {
551            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
552            screenLayout = delta.screenLayout;
553        }
554        if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
555                && uiMode != delta.uiMode) {
556            changed |= ActivityInfo.CONFIG_UI_MODE;
557            if ((delta.uiMode&UI_MODE_TYPE_MASK) != UI_MODE_TYPE_UNDEFINED) {
558                uiMode = (uiMode&~UI_MODE_TYPE_MASK)
559                        | (delta.uiMode&UI_MODE_TYPE_MASK);
560            }
561            if ((delta.uiMode&UI_MODE_NIGHT_MASK) != UI_MODE_NIGHT_UNDEFINED) {
562                uiMode = (uiMode&~UI_MODE_NIGHT_MASK)
563                        | (delta.uiMode&UI_MODE_NIGHT_MASK);
564            }
565        }
566        if (delta.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED
567                && screenWidthDp != delta.screenWidthDp) {
568            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
569            screenWidthDp = delta.screenWidthDp;
570        }
571        if (delta.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED
572                && screenHeightDp != delta.screenHeightDp) {
573            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
574            screenHeightDp = delta.screenHeightDp;
575        }
576        if (delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
577            smallestScreenWidthDp = delta.smallestScreenWidthDp;
578        }
579        if (delta.compatScreenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
580            compatScreenWidthDp = delta.compatScreenWidthDp;
581        }
582        if (delta.compatScreenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
583            compatScreenHeightDp = delta.compatScreenHeightDp;
584        }
585        if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
586            compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
587        }
588
589        if (delta.seq != 0) {
590            seq = delta.seq;
591        }
592
593        return changed;
594    }
595
596    /**
597     * Return a bit mask of the differences between this Configuration
598     * object and the given one.  Does not change the values of either.  Any
599     * undefined fields in <var>delta</var> are ignored.
600     * @return Returns a bit mask indicating which configuration
601     * values has changed, containing any combination of
602     * {@link android.content.pm.ActivityInfo#CONFIG_FONT_SCALE
603     * PackageManager.ActivityInfo.CONFIG_FONT_SCALE},
604     * {@link android.content.pm.ActivityInfo#CONFIG_MCC
605     * PackageManager.ActivityInfo.CONFIG_MCC},
606     * {@link android.content.pm.ActivityInfo#CONFIG_MNC
607     * PackageManager.ActivityInfo.CONFIG_MNC},
608     * {@link android.content.pm.ActivityInfo#CONFIG_LOCALE
609     * PackageManager.ActivityInfo.CONFIG_LOCALE},
610     * {@link android.content.pm.ActivityInfo#CONFIG_TOUCHSCREEN
611     * PackageManager.ActivityInfo.CONFIG_TOUCHSCREEN},
612     * {@link android.content.pm.ActivityInfo#CONFIG_KEYBOARD
613     * PackageManager.ActivityInfo.CONFIG_KEYBOARD},
614     * {@link android.content.pm.ActivityInfo#CONFIG_NAVIGATION
615     * PackageManager.ActivityInfo.CONFIG_NAVIGATION},
616     * {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION
617     * PackageManager.ActivityInfo.CONFIG_ORIENTATION},
618     * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT
619     * PackageManager.ActivityInfo.CONFIG_SCREEN_LAYOUT}, or
620     * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_SIZE
621     * PackageManager.ActivityInfo.CONFIG_SCREEN_SIZE}, or
622     * {@link android.content.pm.ActivityInfo#CONFIG_SMALLEST_SCREEN_SIZE
623     * PackageManager.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE}.
624     */
625    public int diff(Configuration delta) {
626        int changed = 0;
627        if (delta.fontScale > 0 && fontScale != delta.fontScale) {
628            changed |= ActivityInfo.CONFIG_FONT_SCALE;
629        }
630        if (delta.mcc != 0 && mcc != delta.mcc) {
631            changed |= ActivityInfo.CONFIG_MCC;
632        }
633        if (delta.mnc != 0 && mnc != delta.mnc) {
634            changed |= ActivityInfo.CONFIG_MNC;
635        }
636        if (delta.locale != null
637                && (locale == null || !locale.equals(delta.locale))) {
638            changed |= ActivityInfo.CONFIG_LOCALE;
639        }
640        if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
641                && touchscreen != delta.touchscreen) {
642            changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
643        }
644        if (delta.keyboard != KEYBOARD_UNDEFINED
645                && keyboard != delta.keyboard) {
646            changed |= ActivityInfo.CONFIG_KEYBOARD;
647        }
648        if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
649                && keyboardHidden != delta.keyboardHidden) {
650            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
651        }
652        if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
653                && hardKeyboardHidden != delta.hardKeyboardHidden) {
654            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
655        }
656        if (delta.navigation != NAVIGATION_UNDEFINED
657                && navigation != delta.navigation) {
658            changed |= ActivityInfo.CONFIG_NAVIGATION;
659        }
660        if (delta.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED
661                && navigationHidden != delta.navigationHidden) {
662            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
663        }
664        if (delta.orientation != ORIENTATION_UNDEFINED
665                && orientation != delta.orientation) {
666            changed |= ActivityInfo.CONFIG_ORIENTATION;
667        }
668        if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
669                && screenLayout != delta.screenLayout) {
670            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
671        }
672        if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
673                && uiMode != delta.uiMode) {
674            changed |= ActivityInfo.CONFIG_UI_MODE;
675        }
676        if (delta.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED
677                && screenWidthDp != delta.screenWidthDp) {
678            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
679        }
680        if (delta.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED
681                && screenHeightDp != delta.screenHeightDp) {
682            changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
683        }
684        if (delta.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
685                && smallestScreenWidthDp != delta.smallestScreenWidthDp) {
686            changed |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
687        }
688
689        return changed;
690    }
691
692    /**
693     * Determine if a new resource needs to be loaded from the bit set of
694     * configuration changes returned by {@link #updateFrom(Configuration)}.
695     *
696     * @param configChanges The mask of changes configurations as returned by
697     * {@link #updateFrom(Configuration)}.
698     * @param interestingChanges The configuration changes that the resource
699     * can handled, as given in {@link android.util.TypedValue#changingConfigurations}.
700     *
701     * @return Return true if the resource needs to be loaded, else false.
702     */
703    public static boolean needNewResources(int configChanges, int interestingChanges) {
704        return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0;
705    }
706
707    /**
708     * @hide Return true if the sequence of 'other' is better than this.  Assumes
709     * that 'this' is your current sequence and 'other' is a new one you have
710     * received some how and want to compare with what you have.
711     */
712    public boolean isOtherSeqNewer(Configuration other) {
713        if (other == null) {
714            // Sanity check.
715            return false;
716        }
717        if (other.seq == 0) {
718            // If the other sequence is not specified, then we must assume
719            // it is newer since we don't know any better.
720            return true;
721        }
722        if (seq == 0) {
723            // If this sequence is not specified, then we also consider the
724            // other is better.  Yes we have a preference for other.  Sue us.
725            return true;
726        }
727        int diff = other.seq - seq;
728        if (diff > 0x10000) {
729            // If there has been a sufficiently large jump, assume the
730            // sequence has wrapped around.
731            return false;
732        }
733        return diff > 0;
734    }
735
736    /**
737     * Parcelable methods
738     */
739    public int describeContents() {
740        return 0;
741    }
742
743    public void writeToParcel(Parcel dest, int flags) {
744        dest.writeFloat(fontScale);
745        dest.writeInt(mcc);
746        dest.writeInt(mnc);
747        if (locale == null) {
748            dest.writeInt(0);
749        } else {
750            dest.writeInt(1);
751            dest.writeString(locale.getLanguage());
752            dest.writeString(locale.getCountry());
753            dest.writeString(locale.getVariant());
754        }
755        if(userSetLocale) {
756            dest.writeInt(1);
757        } else {
758            dest.writeInt(0);
759        }
760        dest.writeInt(touchscreen);
761        dest.writeInt(keyboard);
762        dest.writeInt(keyboardHidden);
763        dest.writeInt(hardKeyboardHidden);
764        dest.writeInt(navigation);
765        dest.writeInt(navigationHidden);
766        dest.writeInt(orientation);
767        dest.writeInt(screenLayout);
768        dest.writeInt(uiMode);
769        dest.writeInt(screenWidthDp);
770        dest.writeInt(screenHeightDp);
771        dest.writeInt(smallestScreenWidthDp);
772        dest.writeInt(compatScreenWidthDp);
773        dest.writeInt(compatScreenHeightDp);
774        dest.writeInt(compatSmallestScreenWidthDp);
775        dest.writeInt(textLayoutDirection);
776        dest.writeInt(seq);
777    }
778
779    public void readFromParcel(Parcel source) {
780        fontScale = source.readFloat();
781        mcc = source.readInt();
782        mnc = source.readInt();
783        if (source.readInt() != 0) {
784            locale = new Locale(source.readString(), source.readString(),
785                    source.readString());
786        }
787        userSetLocale = (source.readInt()==1);
788        touchscreen = source.readInt();
789        keyboard = source.readInt();
790        keyboardHidden = source.readInt();
791        hardKeyboardHidden = source.readInt();
792        navigation = source.readInt();
793        navigationHidden = source.readInt();
794        orientation = source.readInt();
795        screenLayout = source.readInt();
796        uiMode = source.readInt();
797        screenWidthDp = source.readInt();
798        screenHeightDp = source.readInt();
799        smallestScreenWidthDp = source.readInt();
800        compatScreenWidthDp = source.readInt();
801        compatScreenHeightDp = source.readInt();
802        compatSmallestScreenWidthDp = source.readInt();
803        textLayoutDirection = source.readInt();
804        seq = source.readInt();
805    }
806
807    public static final Parcelable.Creator<Configuration> CREATOR
808            = new Parcelable.Creator<Configuration>() {
809        public Configuration createFromParcel(Parcel source) {
810            return new Configuration(source);
811        }
812
813        public Configuration[] newArray(int size) {
814            return new Configuration[size];
815        }
816    };
817
818    /**
819     * Construct this Configuration object, reading from the Parcel.
820     */
821    private Configuration(Parcel source) {
822        readFromParcel(source);
823    }
824
825    public int compareTo(Configuration that) {
826        int n;
827        float a = this.fontScale;
828        float b = that.fontScale;
829        if (a < b) return -1;
830        if (a > b) return 1;
831        n = this.mcc - that.mcc;
832        if (n != 0) return n;
833        n = this.mnc - that.mnc;
834        if (n != 0) return n;
835        if (this.locale == null) {
836            if (that.locale != null) return 1;
837        } else if (that.locale == null) {
838            return -1;
839        } else {
840            n = this.locale.getLanguage().compareTo(that.locale.getLanguage());
841            if (n != 0) return n;
842            n = this.locale.getCountry().compareTo(that.locale.getCountry());
843            if (n != 0) return n;
844            n = this.locale.getVariant().compareTo(that.locale.getVariant());
845            if (n != 0) return n;
846        }
847        n = this.touchscreen - that.touchscreen;
848        if (n != 0) return n;
849        n = this.keyboard - that.keyboard;
850        if (n != 0) return n;
851        n = this.keyboardHidden - that.keyboardHidden;
852        if (n != 0) return n;
853        n = this.hardKeyboardHidden - that.hardKeyboardHidden;
854        if (n != 0) return n;
855        n = this.navigation - that.navigation;
856        if (n != 0) return n;
857        n = this.navigationHidden - that.navigationHidden;
858        if (n != 0) return n;
859        n = this.orientation - that.orientation;
860        if (n != 0) return n;
861        n = this.screenLayout - that.screenLayout;
862        if (n != 0) return n;
863        n = this.uiMode - that.uiMode;
864        if (n != 0) return n;
865        n = this.screenWidthDp - that.screenWidthDp;
866        if (n != 0) return n;
867        n = this.screenHeightDp - that.screenHeightDp;
868        if (n != 0) return n;
869        n = this.smallestScreenWidthDp - that.smallestScreenWidthDp;
870        //if (n != 0) return n;
871        return n;
872    }
873
874    public boolean equals(Configuration that) {
875        if (that == null) return false;
876        if (that == this) return true;
877        return this.compareTo(that) == 0;
878    }
879
880    public boolean equals(Object that) {
881        try {
882            return equals((Configuration)that);
883        } catch (ClassCastException e) {
884        }
885        return false;
886    }
887
888    public int hashCode() {
889        int result = 17;
890        result = 31 * result + Float.floatToIntBits(fontScale);
891        result = 31 * result + mcc;
892        result = 31 * result + mnc;
893        result = 31 * result + (locale != null ? locale.hashCode() : 0);
894        result = 31 * result + touchscreen;
895        result = 31 * result + keyboard;
896        result = 31 * result + keyboardHidden;
897        result = 31 * result + hardKeyboardHidden;
898        result = 31 * result + navigation;
899        result = 31 * result + navigationHidden;
900        result = 31 * result + orientation;
901        result = 31 * result + screenLayout;
902        result = 31 * result + uiMode;
903        result = 31 * result + screenWidthDp;
904        result = 31 * result + screenHeightDp;
905        result = 31 * result + smallestScreenWidthDp;
906        return result;
907    }
908}
909