Configuration.java revision 69717ccd13bb5568b912701ab39d603cfa7091cc
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;
22
23import java.util.Locale;
24
25/**
26 * This class describes all device configuration information that can
27 * impact the resources the application retrieves.  This includes both
28 * user-specified configuration options (locale and scaling) as well
29 * as dynamic device configuration (various types of input devices).
30 */
31public final class Configuration implements Parcelable, Comparable<Configuration> {
32    /**
33     * Current user preference for the scaling factor for fonts, relative
34     * to the base density scaling.
35     */
36    public float fontScale;
37
38    /**
39     * IMSI MCC (Mobile Country Code).  0 if undefined.
40     */
41    public int mcc;
42
43    /**
44     * IMSI MNC (Mobile Network Code).  0 if undefined.
45     */
46    public int mnc;
47
48    /**
49     * Current user preference for the locale.
50     */
51    public Locale locale;
52
53    /**
54     * Locale should persist on setting.  This is hidden because it is really
55     * questionable whether this is the right way to expose the functionality.
56     * @hide
57     */
58    public boolean userSetLocale;
59
60    public static final int SCREENLAYOUT_SIZE_MASK = 0x0f;
61    public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0x00;
62    public static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
63    public static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
64    public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
65    /** @hide */
66    public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
67
68    public static final int SCREENLAYOUT_LONG_MASK = 0x30;
69    public static final int SCREENLAYOUT_LONG_UNDEFINED = 0x00;
70    public static final int SCREENLAYOUT_LONG_NO = 0x10;
71    public static final int SCREENLAYOUT_LONG_YES = 0x20;
72
73    /**
74     * Special flag we generate to indicate that the screen layout requires
75     * us to use a compatibility mode for apps that are not modern layout
76     * aware.
77     * @hide
78     */
79    public static final int SCREENLAYOUT_COMPAT_NEEDED = 0x10000000;
80
81    /**
82     * Bit mask of overall layout of the screen.  Currently there are two
83     * fields:
84     * <p>The {@link #SCREENLAYOUT_SIZE_MASK} bits define the overall size
85     * of the screen.  They may be one of
86     * {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL},
87     * {@link #SCREENLAYOUT_SIZE_LARGE}.
88     *
89     * <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
90     * is wider/taller than normal.  They may be one of
91     * {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.
92     */
93    public int screenLayout;
94
95    public static final int TOUCHSCREEN_UNDEFINED = 0;
96    public static final int TOUCHSCREEN_NOTOUCH = 1;
97    public static final int TOUCHSCREEN_STYLUS = 2;
98    public static final int TOUCHSCREEN_FINGER = 3;
99
100    /**
101     * The kind of touch screen attached to the device.
102     * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_STYLUS},
103     * {@link #TOUCHSCREEN_FINGER}.
104     */
105    public int touchscreen;
106
107    public static final int KEYBOARD_UNDEFINED = 0;
108    public static final int KEYBOARD_NOKEYS = 1;
109    public static final int KEYBOARD_QWERTY = 2;
110    public static final int KEYBOARD_12KEY = 3;
111
112    /**
113     * The kind of keyboard attached to the device.
114     * One of: {@link #KEYBOARD_NOKEYS}, {@link #KEYBOARD_QWERTY},
115     * {@link #KEYBOARD_12KEY}.
116     */
117    public int keyboard;
118
119    public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
120    public static final int KEYBOARDHIDDEN_NO = 1;
121    public static final int KEYBOARDHIDDEN_YES = 2;
122    /** Constant matching actual resource implementation. {@hide} */
123    public static final int KEYBOARDHIDDEN_SOFT = 3;
124
125    /**
126     * A flag indicating whether any keyboard is available.  Unlike
127     * {@link #hardKeyboardHidden}, this also takes into account a soft
128     * keyboard, so if the hard keyboard is hidden but there is soft
129     * keyboard available, it will be set to NO.  Value is one of:
130     * {@link #KEYBOARDHIDDEN_NO}, {@link #KEYBOARDHIDDEN_YES}.
131     */
132    public int keyboardHidden;
133
134    public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0;
135    public static final int HARDKEYBOARDHIDDEN_NO = 1;
136    public static final int HARDKEYBOARDHIDDEN_YES = 2;
137
138    /**
139     * A flag indicating whether the hard keyboard has been hidden.  This will
140     * be set on a device with a mechanism to hide the keyboard from the
141     * user, when that mechanism is closed.  One of:
142     * {@link #HARDKEYBOARDHIDDEN_NO}, {@link #HARDKEYBOARDHIDDEN_YES}.
143     */
144    public int hardKeyboardHidden;
145
146    public static final int NAVIGATION_UNDEFINED = 0;
147    public static final int NAVIGATION_NONAV = 1;
148    public static final int NAVIGATION_DPAD = 2;
149    public static final int NAVIGATION_TRACKBALL = 3;
150    public static final int NAVIGATION_WHEEL = 4;
151
152    /**
153     * The kind of navigation method available on the device.
154     * One of: {@link #NAVIGATION_NONAV}, {@link #NAVIGATION_DPAD},
155     * {@link #NAVIGATION_TRACKBALL}, {@link #NAVIGATION_WHEEL}.
156     */
157    public int navigation;
158
159    public static final int NAVIGATIONHIDDEN_UNDEFINED = 0;
160    public static final int NAVIGATIONHIDDEN_NO = 1;
161    public static final int NAVIGATIONHIDDEN_YES = 2;
162
163    /**
164     * A flag indicating whether any 5-way or DPAD navigation available.
165     * This will be set on a device with a mechanism to hide the navigation
166     * controls from the user, when that mechanism is closed.  One of:
167     * {@link #NAVIGATIONHIDDEN_NO}, {@link #NAVIGATIONHIDDEN_YES}.
168     */
169    public int navigationHidden;
170
171    public static final int ORIENTATION_UNDEFINED = 0;
172    public static final int ORIENTATION_PORTRAIT = 1;
173    public static final int ORIENTATION_LANDSCAPE = 2;
174    public static final int ORIENTATION_SQUARE = 3;
175
176    /**
177     * Overall orientation of the screen.  May be one of
178     * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT},
179     * or {@link #ORIENTATION_SQUARE}.
180     */
181    public int orientation;
182
183    public static final int UI_MODE_TYPE_MASK = 0x0f;
184    public static final int UI_MODE_TYPE_UNDEFINED = 0x00;
185    public static final int UI_MODE_TYPE_NORMAL = 0x01;
186    public static final int UI_MODE_TYPE_DESK = 0x02;
187    public static final int UI_MODE_TYPE_CAR = 0x03;
188
189    public static final int UI_MODE_NIGHT_MASK = 0x30;
190    public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
191    public static final int UI_MODE_NIGHT_NO = 0x10;
192    public static final int UI_MODE_NIGHT_YES = 0x20;
193
194    /**
195     * Bit mask of the ui mode.  Currently there are two fields:
196     * <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the
197     * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED},
198     * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK},
199     * or {@link #UI_MODE_TYPE_CAR}.
200     *
201     * <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen
202     * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED},
203     * {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.
204     */
205    public int uiMode;
206
207    /**
208     * @hide Internal book-keeping.
209     */
210    public int seq;
211
212    /**
213     * Construct an invalid Configuration.  You must call {@link #setToDefaults}
214     * for this object to be valid.  {@more}
215     */
216    public Configuration() {
217        setToDefaults();
218    }
219
220    /**
221     * Makes a deep copy suitable for modification.
222     */
223    public Configuration(Configuration o) {
224        setTo(o);
225    }
226
227    public void setTo(Configuration o) {
228        fontScale = o.fontScale;
229        mcc = o.mcc;
230        mnc = o.mnc;
231        if (o.locale != null) {
232            locale = (Locale) o.locale.clone();
233        }
234        userSetLocale = o.userSetLocale;
235        touchscreen = o.touchscreen;
236        keyboard = o.keyboard;
237        keyboardHidden = o.keyboardHidden;
238        hardKeyboardHidden = o.hardKeyboardHidden;
239        navigation = o.navigation;
240        navigationHidden = o.navigationHidden;
241        orientation = o.orientation;
242        screenLayout = o.screenLayout;
243        uiMode = o.uiMode;
244        seq = o.seq;
245    }
246
247    public String toString() {
248        StringBuilder sb = new StringBuilder(128);
249        sb.append("{ scale=");
250        sb.append(fontScale);
251        sb.append(" imsi=");
252        sb.append(mcc);
253        sb.append("/");
254        sb.append(mnc);
255        sb.append(" loc=");
256        sb.append(locale);
257        sb.append(" touch=");
258        sb.append(touchscreen);
259        sb.append(" keys=");
260        sb.append(keyboard);
261        sb.append("/");
262        sb.append(keyboardHidden);
263        sb.append("/");
264        sb.append(hardKeyboardHidden);
265        sb.append(" nav=");
266        sb.append(navigation);
267        sb.append("/");
268        sb.append(navigationHidden);
269        sb.append(" orien=");
270        sb.append(orientation);
271        sb.append(" layout=");
272        sb.append(screenLayout);
273        sb.append(" uiMode=");
274        sb.append(uiMode);
275        if (seq != 0) {
276            sb.append(" seq=");
277            sb.append(seq);
278        }
279        sb.append('}');
280        return sb.toString();
281    }
282
283    /**
284     * Set this object to the system defaults.
285     */
286    public void setToDefaults() {
287        fontScale = 1;
288        mcc = mnc = 0;
289        locale = null;
290        userSetLocale = false;
291        touchscreen = TOUCHSCREEN_UNDEFINED;
292        keyboard = KEYBOARD_UNDEFINED;
293        keyboardHidden = KEYBOARDHIDDEN_UNDEFINED;
294        hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED;
295        navigation = NAVIGATION_UNDEFINED;
296        navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
297        orientation = ORIENTATION_UNDEFINED;
298        screenLayout = SCREENLAYOUT_SIZE_UNDEFINED;
299        uiMode = UI_MODE_TYPE_UNDEFINED;
300        seq = 0;
301    }
302
303    /** {@hide} */
304    @Deprecated public void makeDefault() {
305        setToDefaults();
306    }
307
308    /**
309     * Copy the fields from delta into this Configuration object, keeping
310     * track of which ones have changed.  Any undefined fields in
311     * <var>delta</var> are ignored and not copied in to the current
312     * Configuration.
313     * @return Returns a bit mask of the changed fields, as per
314     * {@link #diff}.
315     */
316    public int updateFrom(Configuration delta) {
317        int changed = 0;
318        if (delta.fontScale > 0 && fontScale != delta.fontScale) {
319            changed |= ActivityInfo.CONFIG_FONT_SCALE;
320            fontScale = delta.fontScale;
321        }
322        if (delta.mcc != 0 && mcc != delta.mcc) {
323            changed |= ActivityInfo.CONFIG_MCC;
324            mcc = delta.mcc;
325        }
326        if (delta.mnc != 0 && mnc != delta.mnc) {
327            changed |= ActivityInfo.CONFIG_MNC;
328            mnc = delta.mnc;
329        }
330        if (delta.locale != null
331                && (locale == null || !locale.equals(delta.locale))) {
332            changed |= ActivityInfo.CONFIG_LOCALE;
333            locale = delta.locale != null
334                    ? (Locale) delta.locale.clone() : null;
335        }
336        if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
337        {
338            userSetLocale = true;
339            changed |= ActivityInfo.CONFIG_LOCALE;
340        }
341        if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
342                && touchscreen != delta.touchscreen) {
343            changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
344            touchscreen = delta.touchscreen;
345        }
346        if (delta.keyboard != KEYBOARD_UNDEFINED
347                && keyboard != delta.keyboard) {
348            changed |= ActivityInfo.CONFIG_KEYBOARD;
349            keyboard = delta.keyboard;
350        }
351        if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
352                && keyboardHidden != delta.keyboardHidden) {
353            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
354            keyboardHidden = delta.keyboardHidden;
355        }
356        if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
357                && hardKeyboardHidden != delta.hardKeyboardHidden) {
358            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
359            hardKeyboardHidden = delta.hardKeyboardHidden;
360        }
361        if (delta.navigation != NAVIGATION_UNDEFINED
362                && navigation != delta.navigation) {
363            changed |= ActivityInfo.CONFIG_NAVIGATION;
364            navigation = delta.navigation;
365        }
366        if (delta.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED
367                && navigationHidden != delta.navigationHidden) {
368            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
369            navigationHidden = delta.navigationHidden;
370        }
371        if (delta.orientation != ORIENTATION_UNDEFINED
372                && orientation != delta.orientation) {
373            changed |= ActivityInfo.CONFIG_ORIENTATION;
374            orientation = delta.orientation;
375        }
376        if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
377                && screenLayout != delta.screenLayout) {
378            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
379            screenLayout = delta.screenLayout;
380        }
381        if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
382                && uiMode != delta.uiMode) {
383            changed |= ActivityInfo.CONFIG_UI_MODE;
384            if ((delta.uiMode&UI_MODE_TYPE_MASK) != UI_MODE_TYPE_UNDEFINED) {
385                uiMode = (uiMode&~UI_MODE_TYPE_MASK)
386                        | (delta.uiMode&UI_MODE_TYPE_MASK);
387            }
388            if ((delta.uiMode&UI_MODE_NIGHT_MASK) != UI_MODE_NIGHT_UNDEFINED) {
389                uiMode = (uiMode&~UI_MODE_NIGHT_MASK)
390                        | (delta.uiMode&UI_MODE_NIGHT_MASK);
391            }
392        }
393
394        if (delta.seq != 0) {
395            seq = delta.seq;
396        }
397
398        return changed;
399    }
400
401    /**
402     * Return a bit mask of the differences between this Configuration
403     * object and the given one.  Does not change the values of either.  Any
404     * undefined fields in <var>delta</var> are ignored.
405     * @return Returns a bit mask indicating which configuration
406     * values has changed, containing any combination of
407     * {@link android.content.pm.ActivityInfo#CONFIG_FONT_SCALE
408     * PackageManager.ActivityInfo.CONFIG_FONT_SCALE},
409     * {@link android.content.pm.ActivityInfo#CONFIG_MCC
410     * PackageManager.ActivityInfo.CONFIG_MCC},
411     * {@link android.content.pm.ActivityInfo#CONFIG_MNC
412     * PackageManager.ActivityInfo.CONFIG_MNC},
413     * {@link android.content.pm.ActivityInfo#CONFIG_LOCALE
414     * PackageManager.ActivityInfo.CONFIG_LOCALE},
415     * {@link android.content.pm.ActivityInfo#CONFIG_TOUCHSCREEN
416     * PackageManager.ActivityInfo.CONFIG_TOUCHSCREEN},
417     * {@link android.content.pm.ActivityInfo#CONFIG_KEYBOARD
418     * PackageManager.ActivityInfo.CONFIG_KEYBOARD},
419     * {@link android.content.pm.ActivityInfo#CONFIG_NAVIGATION
420     * PackageManager.ActivityInfo.CONFIG_NAVIGATION},
421     * {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION
422     * PackageManager.ActivityInfo.CONFIG_ORIENTATION}, or
423     * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT
424     * PackageManager.ActivityInfo.CONFIG_SCREEN_LAYOUT}.
425     */
426    public int diff(Configuration delta) {
427        int changed = 0;
428        if (delta.fontScale > 0 && fontScale != delta.fontScale) {
429            changed |= ActivityInfo.CONFIG_FONT_SCALE;
430        }
431        if (delta.mcc != 0 && mcc != delta.mcc) {
432            changed |= ActivityInfo.CONFIG_MCC;
433        }
434        if (delta.mnc != 0 && mnc != delta.mnc) {
435            changed |= ActivityInfo.CONFIG_MNC;
436        }
437        if (delta.locale != null
438                && (locale == null || !locale.equals(delta.locale))) {
439            changed |= ActivityInfo.CONFIG_LOCALE;
440        }
441        if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
442                && touchscreen != delta.touchscreen) {
443            changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
444        }
445        if (delta.keyboard != KEYBOARD_UNDEFINED
446                && keyboard != delta.keyboard) {
447            changed |= ActivityInfo.CONFIG_KEYBOARD;
448        }
449        if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
450                && keyboardHidden != delta.keyboardHidden) {
451            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
452        }
453        if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
454                && hardKeyboardHidden != delta.hardKeyboardHidden) {
455            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
456        }
457        if (delta.navigation != NAVIGATION_UNDEFINED
458                && navigation != delta.navigation) {
459            changed |= ActivityInfo.CONFIG_NAVIGATION;
460        }
461        if (delta.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED
462                && navigationHidden != delta.navigationHidden) {
463            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
464        }
465        if (delta.orientation != ORIENTATION_UNDEFINED
466                && orientation != delta.orientation) {
467            changed |= ActivityInfo.CONFIG_ORIENTATION;
468        }
469        if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
470                && screenLayout != delta.screenLayout) {
471            changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
472        }
473        if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
474                && uiMode != delta.uiMode) {
475            changed |= ActivityInfo.CONFIG_UI_MODE;
476        }
477
478        return changed;
479    }
480
481    /**
482     * Determine if a new resource needs to be loaded from the bit set of
483     * configuration changes returned by {@link #updateFrom(Configuration)}.
484     *
485     * @param configChanges The mask of changes configurations as returned by
486     * {@link #updateFrom(Configuration)}.
487     * @param interestingChanges The configuration changes that the resource
488     * can handled, as given in {@link android.util.TypedValue#changingConfigurations}.
489     *
490     * @return Return true if the resource needs to be loaded, else false.
491     */
492    public static boolean needNewResources(int configChanges, int interestingChanges) {
493        return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0;
494    }
495
496    /**
497     * @hide Return true if the sequence of 'other' is better than this.  Assumes
498     * that 'this' is your current sequence and 'other' is a new one you have
499     * received some how and want to compare with what you have.
500     */
501    public boolean isOtherSeqNewer(Configuration other) {
502        if (other == null) {
503            // Sanity check.
504            return false;
505        }
506        if (other.seq == 0) {
507            // If the other sequence is not specified, then we must assume
508            // it is newer since we don't know any better.
509            return true;
510        }
511        if (seq == 0) {
512            // If this sequence is not specified, then we also consider the
513            // other is better.  Yes we have a preference for other.  Sue us.
514            return true;
515        }
516        int diff = other.seq - seq;
517        if (diff > 0x10000) {
518            // If there has been a sufficiently large jump, assume the
519            // sequence has wrapped around.
520            return false;
521        }
522        return diff > 0;
523    }
524
525    /**
526     * Parcelable methods
527     */
528    public int describeContents() {
529        return 0;
530    }
531
532    public void writeToParcel(Parcel dest, int flags) {
533        dest.writeFloat(fontScale);
534        dest.writeInt(mcc);
535        dest.writeInt(mnc);
536        if (locale == null) {
537            dest.writeInt(0);
538        } else {
539            dest.writeInt(1);
540            dest.writeString(locale.getLanguage());
541            dest.writeString(locale.getCountry());
542            dest.writeString(locale.getVariant());
543        }
544        if(userSetLocale) {
545            dest.writeInt(1);
546        } else {
547            dest.writeInt(0);
548        }
549        dest.writeInt(touchscreen);
550        dest.writeInt(keyboard);
551        dest.writeInt(keyboardHidden);
552        dest.writeInt(hardKeyboardHidden);
553        dest.writeInt(navigation);
554        dest.writeInt(navigationHidden);
555        dest.writeInt(orientation);
556        dest.writeInt(screenLayout);
557        dest.writeInt(uiMode);
558        dest.writeInt(seq);
559    }
560
561    public void readFromParcel(Parcel source) {
562        fontScale = source.readFloat();
563        mcc = source.readInt();
564        mnc = source.readInt();
565        if (source.readInt() != 0) {
566            locale = new Locale(source.readString(), source.readString(),
567                    source.readString());
568        }
569        userSetLocale = (source.readInt()==1);
570        touchscreen = source.readInt();
571        keyboard = source.readInt();
572        keyboardHidden = source.readInt();
573        hardKeyboardHidden = source.readInt();
574        navigation = source.readInt();
575        navigationHidden = source.readInt();
576        orientation = source.readInt();
577        screenLayout = source.readInt();
578        uiMode = source.readInt();
579        seq = source.readInt();
580    }
581
582    public static final Parcelable.Creator<Configuration> CREATOR
583            = new Parcelable.Creator<Configuration>() {
584        public Configuration createFromParcel(Parcel source) {
585            return new Configuration(source);
586        }
587
588        public Configuration[] newArray(int size) {
589            return new Configuration[size];
590        }
591    };
592
593    /**
594     * Construct this Configuration object, reading from the Parcel.
595     */
596    private Configuration(Parcel source) {
597        readFromParcel(source);
598    }
599
600    public int compareTo(Configuration that) {
601        int n;
602        float a = this.fontScale;
603        float b = that.fontScale;
604        if (a < b) return -1;
605        if (a > b) return 1;
606        n = this.mcc - that.mcc;
607        if (n != 0) return n;
608        n = this.mnc - that.mnc;
609        if (n != 0) return n;
610        if (this.locale == null) {
611            if (that.locale != null) return 1;
612        } else if (that.locale == null) {
613            return -1;
614        } else {
615            n = this.locale.getLanguage().compareTo(that.locale.getLanguage());
616            if (n != 0) return n;
617            n = this.locale.getCountry().compareTo(that.locale.getCountry());
618            if (n != 0) return n;
619            n = this.locale.getVariant().compareTo(that.locale.getVariant());
620            if (n != 0) return n;
621        }
622        n = this.touchscreen - that.touchscreen;
623        if (n != 0) return n;
624        n = this.keyboard - that.keyboard;
625        if (n != 0) return n;
626        n = this.keyboardHidden - that.keyboardHidden;
627        if (n != 0) return n;
628        n = this.hardKeyboardHidden - that.hardKeyboardHidden;
629        if (n != 0) return n;
630        n = this.navigation - that.navigation;
631        if (n != 0) return n;
632        n = this.navigationHidden - that.navigationHidden;
633        if (n != 0) return n;
634        n = this.orientation - that.orientation;
635        if (n != 0) return n;
636        n = this.screenLayout - that.screenLayout;
637        if (n != 0) return n;
638        n = this.uiMode - that.uiMode;
639        //if (n != 0) return n;
640        return n;
641    }
642
643    public boolean equals(Configuration that) {
644        if (that == null) return false;
645        if (that == this) return true;
646        return this.compareTo(that) == 0;
647    }
648
649    public boolean equals(Object that) {
650        try {
651            return equals((Configuration)that);
652        } catch (ClassCastException e) {
653        }
654        return false;
655    }
656
657    public int hashCode() {
658        return ((int)this.fontScale) + this.mcc + this.mnc
659                + (this.locale != null ? this.locale.hashCode() : 0)
660                + this.touchscreen
661                + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden
662                + this.navigation + this.navigationHidden
663                + this.orientation + this.screenLayout + this.uiMode;
664    }
665}
666