Merge "Add simple null checks to StorageWizard*, also listen to..." into mnc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4dcaba7..358eae9 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -633,14 +633,11 @@
                 android:value="true" />
         </activity>
 
-        <!-- TODO: This should also be forwarded, but we can't use cross-profile intent filters -->
         <receiver android:name=".inputmethod.InputMethodDialogReceiver"
                 android:enabled="true">
             <intent-filter>
                 <action android:name="android.settings.SHOW_INPUT_METHOD_PICKER" />
             </intent-filter>
-            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
-                android:value="true" />
         </receiver>
 
         <activity android:name="Settings$UserDictionarySettingsActivity"
@@ -767,7 +764,6 @@
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.notification.ZenModeScheduleRuleSettings" />
@@ -787,7 +783,6 @@
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.notification.ZenModeEventRuleSettings" />
@@ -807,7 +802,6 @@
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="com.android.settings.SHORTCUT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.notification.ZenModeExternalRuleSettings" />
@@ -926,6 +920,19 @@
                 android:value="true" />
         </activity>
 
+        <activity android:name="Settings$MemorySettingsActivity"
+                android:label="@string/memory_settings_title"
+                android:taskAffinity="">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.settings.SHORTCUT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.applications.ProcessStatsSummary" />
+            <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+                android:resource="@id/manage_memory" />
+        </activity>
+
         <activity android:name="Settings$AllApplicationsActivity"
                 android:label="@string/applications_settings"
                 android:taskAffinity="">
@@ -1423,6 +1430,7 @@
         <activity android:name=".fingerprint.FingerprintEnrollFindSensor" android:exported="false"/>
         <activity android:name=".fingerprint.FingerprintEnrollEnrolling" android:exported="false"/>
         <activity android:name=".fingerprint.FingerprintEnrollFinish" android:exported="false"/>
+        <activity android:name=".fingerprint.FingerprintEnrollIntroduction" android:exported="false"/>
 
         <!-- Note this must not be exported since it returns the password in the intent -->
         <activity android:name="ConfirmLockPattern$InternalActivity"
diff --git a/res/drawable-nodpi/fingerprint_indicator.png b/res/drawable-nodpi/fingerprint_indicator.png
deleted file mode 100644
index fc5ef0f..0000000
--- a/res/drawable-nodpi/fingerprint_indicator.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/shortcut_base.png b/res/drawable-xxxhdpi/shortcut_base.png
new file mode 100644
index 0000000..cd509df
--- /dev/null
+++ b/res/drawable-xxxhdpi/shortcut_base.png
Binary files differ
diff --git a/res/drawable/fp_illustration_enrollment.xml b/res/drawable/fp_illustration_enrollment.xml
new file mode 100644
index 0000000..f9b7ed4
--- /dev/null
+++ b/res/drawable/fp_illustration_enrollment.xml
@@ -0,0 +1,36 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="88.0dp"
+        android:height="88.0dp"
+        android:viewportWidth="88.0"
+        android:viewportHeight="88.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M67.74,11.59c-0.41,0.0 -0.82,-0.1 -1.2,-0.31c-7.44,-4.06 -15.0,-6.04 -23.11,-6.04c-7.92,0.0 -14.67,1.85 -21.88,6.01c-1.2,0.69 -2.73,0.28 -3.42,-0.92s-0.28,-2.72 0.92,-3.41c7.9,-4.55 15.65,-6.68 24.37,-6.68c8.97,0.0 17.32,2.17 25.51,6.65c1.21,0.66 1.66,2.18 1.0,3.39C69.48,11.12 68.62,11.59 67.74,11.59z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9.25,34.74c-0.48,0.0 -0.96,-0.14 -1.39,-0.42c-1.15,-0.77 -1.45,-2.32 -0.68,-3.47c4.09,-6.09 9.3,-10.89 15.49,-14.27c6.52,-3.55 13.91,-5.43 21.38,-5.43c7.44,0.0 14.8,1.86 21.3,5.39c6.17,3.35 11.38,8.12 15.47,14.16c0.77,1.14 0.47,2.7 -0.67,3.47c-1.14,0.77 -2.7,0.47 -3.47,-0.67c-3.64,-5.38 -8.25,-9.61 -13.71,-12.57c-5.77,-3.13 -12.31,-4.78 -18.92,-4.78c-6.63,0.0 -13.2,1.67 -18.98,4.82c-5.48,2.99 -10.1,7.25 -13.73,12.66C10.85,34.35 10.06,34.74 9.25,34.74z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M34.76,86.82c-0.67,0.0 -1.33,-0.27 -1.82,-0.79c-3.49,-3.72 -5.51,-6.25 -8.26,-11.45c-2.84,-5.35 -4.34,-11.88 -4.34,-18.86c0.0,-13.02 10.59,-23.61 23.61,-23.61c13.02,0.0 23.61,10.59 23.61,23.61c0.0,1.38 -1.12,2.5 -2.5,2.5s-2.5,-1.12 -2.5,-2.5c0.0,-10.26 -8.35,-18.61 -18.61,-18.61c-10.26,0.0 -18.61,8.35 -18.61,18.61c0.0,6.17 1.3,11.89 3.76,16.52c2.62,4.94 4.37,7.04 7.49,10.37c0.94,1.01 0.89,2.59 -0.11,3.53C35.99,86.6 35.38,86.82 34.76,86.82z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M64.28,78.84c-4.99,0.0 -9.35,-1.32 -12.98,-3.92c-6.17,-4.43 -9.86,-11.6 -9.86,-19.19c0.0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5c0.0,5.98 2.91,11.64 7.77,15.13c2.8,2.01 6.09,2.98 10.06,2.98c0.97,0.0 2.57,-0.11 4.17,-0.4c1.36,-0.25 2.66,0.64 2.92,2.0c0.25,1.36 -0.64,2.66 -2.0,2.92C66.93,78.8 64.86,78.84 64.28,78.84z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M55.92,87.75c-0.23,0.0 -0.46,-0.03 -0.7,-0.1c-6.6,-1.91 -10.92,-4.49 -15.4,-9.2c-5.76,-6.06 -8.94,-14.13 -8.94,-22.72c0.0,-7.2 5.86,-13.05 13.05,-13.05c7.2,0.0 13.05,5.86 13.05,13.05c0.0,4.44 3.61,8.05 8.05,8.05s8.05,-3.61 8.05,-8.05c0.0,-16.08 -13.08,-29.16 -29.16,-29.16c-11.43,0.0 -21.86,6.73 -26.58,17.15c-1.57,3.48 -2.37,7.52 -2.37,12.01c0.0,3.36 0.28,8.62 2.71,15.49c0.46,1.3 -0.22,2.73 -1.52,3.19c-1.3,0.46 -2.73,-0.22 -3.19,-1.52c-2.02,-5.7 -3.0,-11.31 -3.0,-17.16c0.0,-5.21 0.95,-9.94 2.82,-14.07c5.52,-12.2 17.74,-20.09 31.13,-20.09c18.83,0.0 34.16,15.32 34.16,34.16c0.0,7.2 -5.86,13.05 -13.05,13.05S52.0,62.92 52.0,55.73c0.0,-4.44 -3.61,-8.05 -8.05,-8.05s-8.05,3.61 -8.05,8.05c0.0,7.3 2.69,14.15 7.56,19.28c3.86,4.06 7.43,6.18 13.17,7.84c1.33,0.38 2.09,1.77 1.71,3.1C58.01,87.04 57.01,87.75 55.92,87.75z"/>
+</vector>
diff --git a/res/layout-land/fingerprint_enroll_enrolling.xml b/res/layout-land/fingerprint_enroll_enrolling.xml
index c60888b..4d02c07 100644
--- a/res/layout-land/fingerprint_enroll_enrolling.xml
+++ b/res/layout-land/fingerprint_enroll_enrolling.xml
@@ -69,7 +69,8 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginBottom="16dp"
-                android:layout_gravity="center_horizontal|bottom"/>
+                android:layout_gravity="center_horizontal|bottom"
+                android:visibility="invisible"/>
 
         </FrameLayout>
 
diff --git a/res/layout/choose_lock_pattern.xml b/res/layout/choose_lock_pattern.xml
index 50f05d8..94eecef 100644
--- a/res/layout/choose_lock_pattern.xml
+++ b/res/layout/choose_lock_pattern.xml
@@ -21,7 +21,7 @@
     android:layout_height="match_parent">
 
     <!-- takes up all space above button bar at bottom -->
-    <LinearLayout
+    <com.android.settings.widget.MatchParentShrinkingLinearLayout
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="0dip"
@@ -29,10 +29,11 @@
 
         <TextView android:id="@+id/headerText"
             android:layout_width="match_parent"
-            android:layout_height="0dip"
+            android:layout_height="wrap_content"
             android:layout_weight="1"
+            android:minLines="2"
             android:gravity="center"
-            android:textSize="18sp"/>
+            android:textSize="18sp" />
 
         <View
              android:background="@*android:drawable/code_lock_top"
@@ -41,8 +42,7 @@
 
         <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
              android:layout_width="match_parent"
-             android:layout_height="match_parent"
-             android:background="@color/lock_pattern_background" />
+             android:layout_height="match_parent" />
 
         <!-- bottom line looks bad when button bar is their too, omit in this case -->
         <!--View
@@ -57,7 +57,7 @@
             android:gravity="center"
             android:textSize="14sp"/>
 
-    </LinearLayout>
+    </com.android.settings.widget.MatchParentShrinkingLinearLayout>
 
     <LinearLayout
         style="@style/SecurityPreferenceButtonContainer"
diff --git a/res/layout/data_usage_detail.xml b/res/layout/data_usage_detail.xml
index 914e8ca..5113139 100644
--- a/res/layout/data_usage_detail.xml
+++ b/res/layout/data_usage_detail.xml
@@ -116,7 +116,7 @@
             android:layout_weight="0.5" />
         <Button
             android:id="@+id/app_settings"
-            android:layout_width="0dp"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="0.5"
             android:text="@string/data_usage_app_settings" />
diff --git a/res/layout/empty_print_state.xml b/res/layout/empty_print_state.xml
index e97bb85..361bf3c 100644
--- a/res/layout/empty_print_state.xml
+++ b/res/layout/empty_print_state.xml
@@ -44,6 +44,18 @@
             android:textColor="?android:attr/textColorSecondary">
         </TextView>
 
+        <Button android:id="@+id/add_new_service"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:visibility="gone"
+            style="?android:attr/buttonBarButtonStyle"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:text="@string/print_menu_item_add_service"
+            android:textAllCaps="true"
+            />
+
     </LinearLayout>
 
 </FrameLayout>
diff --git a/res/layout/fingerprint_enroll_enrolling_base.xml b/res/layout/fingerprint_enroll_enrolling_base.xml
index 37e10ba..21221cf 100644
--- a/res/layout/fingerprint_enroll_enrolling_base.xml
+++ b/res/layout/fingerprint_enroll_enrolling_base.xml
@@ -66,7 +66,8 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="24dp"
-            android:layout_gravity="center_horizontal"/>
+            android:layout_gravity="center_horizontal"
+            android:visibility="invisible"/>
 
     </LinearLayout>
 
diff --git a/res/layout/fingerprint_enroll_enrolling_content.xml b/res/layout/fingerprint_enroll_enrolling_content.xml
index 9fa503b..b3171a3 100644
--- a/res/layout/fingerprint_enroll_enrolling_content.xml
+++ b/res/layout/fingerprint_enroll_enrolling_content.xml
@@ -23,17 +23,13 @@
     android:layout_gravity="center_horizontal">
 
     <ImageView
-        android:layout_width="88dp"
-        android:layout_height="88dp"
-        android:layout_centerInParent="true"
-        android:src="@drawable/fingerprint_indicator" />
-
-    <ImageView
         android:id="@+id/fingerprint_animator"
         android:layout_width="88dp"
         android:layout_height="88dp"
         android:layout_centerInParent="true"
-        android:src="@drawable/enrollment_fingerprint_isolated_animation" />
+        android:background="@drawable/fp_illustration_enrollment"
+        android:backgroundTint="@color/fingerprint_indicator_background_resting"
+        android:src="@drawable/enrollment_fingerprint_isolated_animation"/>
 
     <ProgressBar
         android:id="@+id/fingerprint_progress_bar"
diff --git a/res/layout/fingerprint_enroll_finish_base.xml b/res/layout/fingerprint_enroll_finish_base.xml
index 981c9f0..1c66103 100644
--- a/res/layout/fingerprint_enroll_finish_base.xml
+++ b/res/layout/fingerprint_enroll_finish_base.xml
@@ -63,7 +63,6 @@
                 android:id="@+id/add_another_button"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_marginEnd="8dp"
                 android:text="@string/fingerprint_enroll_button_add" />
 
             <Button
diff --git a/res/layout/fingerprint_enroll_introduction.xml b/res/layout/fingerprint_enroll_introduction.xml
new file mode 100644
index 0000000..df83bd9
--- /dev/null
+++ b/res/layout/fingerprint_enroll_introduction.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<com.android.setupwizardlib.SetupWizardLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/setup_wizard_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    style="@style/SetupWizardFingerprintStyle">
+
+    <LinearLayout
+        style="@style/SuwContentFrame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:clipToPadding="false"
+        android:clipChildren="false">
+
+        <TextView
+            style="@style/TextAppearance.FingerprintMessage"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/suw_description_margin_top"
+            android:text="@string/security_settings_fingerprint_enroll_introduction_message"/>
+
+        <TextView
+            style="@style/TextAppearance.FingerprintMessage"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="24dp"
+            android:text="@string/security_settings_fingerprint_enroll_introduction_message_warning"/>
+
+        <TextView
+            style="@style/TextAppearance.FingerprintLink"
+            android:id="@+id/learn_more_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="4dp"
+            android:text="@string/security_settings_fingerprint_enroll_introduction_risk_link_text"/>
+
+        <View
+            android:layout_height="0dp"
+            android:layout_width="match_parent"
+            android:layout_weight="1"/>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="-12dp"
+            android:layout_marginBottom="4dp"
+            android:layout_gravity="end"
+            android:orientation="horizontal">
+
+            <Button
+                style="@style/Button.FingerprintButton"
+                android:id="@+id/cancel_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="8dp"
+                android:text="@string/security_settings_fingerprint_enroll_introduction_cancel" />
+
+            <Button
+                style="@style/Button.FingerprintButton"
+                android:id="@+id/next_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="end|center_vertical"
+                android:text="@string/security_settings_fingerprint_enroll_introduction_continue" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</com.android.setupwizardlib.SetupWizardLayout>
diff --git a/res/layout/shortcut_badge.xml b/res/layout/shortcut_badge.xml
new file mode 100644
index 0000000..117b386
--- /dev/null
+++ b/res/layout/shortcut_badge.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/shortcut_size"
+    android:layout_height="@dimen/shortcut_size">
+
+    <ImageView
+        android:layout_width="@dimen/shortcut_size"
+        android:layout_height="@dimen/shortcut_size"
+        android:scaleType="fitXY"
+        android:src="@drawable/shortcut_base" />
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="@dimen/shortcut_size"
+        android:layout_height="@dimen/shortcut_size"
+        android:padding="12dp"
+        android:scaleType="fitXY" />
+
+</RelativeLayout>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index d0ef652..c874970 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -66,6 +66,8 @@
     <color name="fingerprint_message_color">#de000000</color>
     <color name="fingerprint_progress_ring">?android:attr/colorAccent</color>
     <color name="fingerprint_progress_ring_bg">#20000000</color>
+    <color name="fingerprint_indicator_background_resting">#12000000</color>
+    <color name="fingerprint_indicator_background_activated">#80009688</color>
 
     <color name="running_processes_system_ram">#ff384248</color>
     <color name="running_processes_apps_ram">#ff009587</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 5891438..c10e83f 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -231,6 +231,8 @@
     <dimen name="fingerprint_find_sensor_graphic_size">200dp</dimen>
     <item name="fingerprint_illustration_aspect_ratio" format="float" type="dimen">2.6</item>
     <dimen name="fingerprint_decor_padding_top">0dp</dimen>
+    <dimen name="fingerprint_error_text_appear_distance">16dp</dimen>
+    <dimen name="fingerprint_error_text_disappear_distance">-8dp</dimen>
 
     <dimen name="confirm_credentials_security_method_margin">48dp</dimen>
     <dimen name="fab_size">56dp</dimen>
@@ -244,4 +246,7 @@
     <dimen name="mdm_app_info_height">72dp</dimen>
     <dimen name="mdm_app_info_padding_top_bottom">8dp</dimen>
     <dimen name="mdm_app_name_padding_left">16dp</dimen>
+
+    <dimen name="shortcut_size">40dp</dimen>
+    <dimen name="badge_size">10dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6e28007..6a61aa0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -823,6 +823,8 @@
     <string name="security_settings_fingerprint_enroll_disclaimer">In addition to unlocking your phone, you can also use your fingerprint to authorize purchases and app access. <annotation id="url">Learn more</annotation></string>
     <!-- Text shown in fingerprint settings explaining what the fingerprint can be used for in the case unlocking is disabled [CHAR LIMIT=NONE] -->
     <string name="security_settings_fingerprint_enroll_disclaimer_lockscreen_disabled">Screen lock option disabled. You can still use your fingerprint to authorize purchases and app access. <annotation id="url">Learn more</annotation></string>
+    <!-- Text shown in fingerprint enroll when we didn't observe progress for a few seconds. [CHAR LIMIT=100] -->
+    <string name="security_settings_fingerprint_enroll_lift_touch_again">Lift finger, then touch sensor again</string>
 
     <!-- Title of the preferences category for preference items to control encryption -->
     <string name="crypt_keeper_settings_title">Encryption</string>
@@ -1206,6 +1208,8 @@
 
     <!-- Message in pairing dialogs.  [CHAR LIMIT=NONE] -->
     <string name="bluetooth_pairing_will_share_phonebook">Pairing grants access to your contacts and call history when connected.</string>
+    <!-- Checkbox message in pairing dialogs.  [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_pairing_shares_phonebook">Grant access to your contacts and call history when connected.</string>
 
     <!-- Title for BT error dialogs. -->
     <string name="bluetooth_error_title"></string>
@@ -5194,6 +5198,8 @@
     <string name="data_usage_menu_sim_cards">SIM cards</string>
     <!-- Title for menu option to show details for all cellular networks. [CHAR LIMIT=26] -->
     <string name="data_usage_menu_cellular_networks">Cellular networks</string>
+    <!-- Summary String for Cellular data enable toggle. [CHAR LIMIT=26] -->
+    <string name="data_usage_cellular_data_summary">Paused at limit</string>
 
     <!--  Title for menu option to enable global auto-sync of personal account data [CHAR LIMIT=30] -->
     <string name="account_settings_menu_auto_sync">Auto-sync data</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 1d58779..1b7caf7 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -309,6 +309,11 @@
         <item name="android:textColor">?android:attr/colorAccent</item>
     </style>
 
+    <style name="TextAppearance.FingerprintLink"
+           parent="TextAppearance.FingerprintMessage">
+        <item name="android:textColor">?android:attr/colorAccent</item>
+    </style>
+
     <style name="TextAppearance.FingerprintErrorText"
         parent="android:TextAppearance.Material.Caption">
         <item name="android:textColor">@color/warning</item>
diff --git a/src/com/android/settings/CreateShortcut.java b/src/com/android/settings/CreateShortcut.java
index fa2ce7c..637bc6f 100644
--- a/src/com/android/settings/CreateShortcut.java
+++ b/src/com/android/settings/CreateShortcut.java
@@ -17,18 +17,33 @@
 package com.android.settings;
 
 import android.app.LauncherActivity;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.MeasureSpec;
+import android.widget.ImageView;
 import android.widget.ListView;
 
 import com.android.settings.Settings.TetherSettingsActivity;
+import com.android.settings.dashboard.DashboardCategory;
+import com.android.settings.dashboard.DashboardTile;
 import com.android.settingslib.TetherUtil;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class CreateShortcut extends LauncherActivity {
 
+    private static final String TOP_LEVEL_HEADER = "com.android.settings.TOP_LEVEL_HEADER_ID";
+
     @Override
     protected Intent getTargetIntent() {
         Intent targetIntent = new Intent(Intent.ACTION_MAIN, null);
@@ -46,10 +61,45 @@
                 Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher_settings));
         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, itemForPosition(position).label);
+        ResolveInfo resolveInfo = itemForPosition(position).resolveInfo;
+        ActivityInfo activityInfo = resolveInfo.activityInfo;
+        if (activityInfo.metaData != null && activityInfo.metaData.containsKey(TOP_LEVEL_HEADER)) {
+            int topLevelId = activityInfo.metaData.getInt(TOP_LEVEL_HEADER);
+            int resourceId = getDrawableResource(topLevelId);
+            intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, createIcon(resourceId));
+        }
         setResult(RESULT_OK, intent);
         finish();
     }
 
+    private Bitmap createIcon(int resource) {
+        Context context = new ContextThemeWrapper(this, android.R.style.Theme_Material_Light);
+        View view = LayoutInflater.from(context).inflate(R.layout.shortcut_badge, null);
+        ((ImageView) view.findViewById(android.R.id.icon)).setImageResource(resource);
+
+        int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        view.measure(spec, spec);
+        Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(),
+                Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+        view.draw(canvas);
+        return bitmap;
+    }
+
+    private int getDrawableResource(int topLevelId) {
+        ArrayList<DashboardCategory> categories = new ArrayList<>();
+        SettingsActivity.loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
+        for (DashboardCategory category : categories) {
+            for (DashboardTile tile : category.tiles) {
+                if (tile.id == topLevelId) {
+                    return tile.iconRes;
+                }
+            }
+        }
+        return 0;
+    }
+
     @Override
     protected boolean onEvaluateShowIcons() {
         return false;
@@ -60,7 +110,8 @@
      * implementation queries for activities.
      */
     protected List<ResolveInfo> onQueryPackageManager(Intent queryIntent) {
-        List<ResolveInfo> activities = super.onQueryPackageManager(queryIntent);
+        List<ResolveInfo> activities = getPackageManager().queryIntentActivities(queryIntent,
+                PackageManager.GET_META_DATA);
         if (activities == null) return null;
         for (int i = activities.size() - 1; i >= 0; i--) {
             ResolveInfo info = activities.get(i);
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 23f6812..6ab36c1 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -234,15 +234,18 @@
                                 authContext.getDrawable(desc.iconId), userHandle);
                     }
                 } catch (PackageManager.NameNotFoundException e) {
-                    Log.w(TAG, "No icon for account type " + desc.type);
+                    Log.w(TAG, "Bad package name for account type " + desc.type);
+                } catch (Resources.NotFoundException e) {
+                    Log.w(TAG, "Invalid icon id for account type " + desc.type, e);
+                }
+                if (icon == null) {
+                    icon = context.getPackageManager().getDefaultActivityIcon();
                 }
 
                 TextView child = (TextView)inflater.inflate(R.layout.master_clear_account,
                         contents, false);
                 child.setText(account.name);
-                if (icon != null) {
-                    child.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
-                }
+                child.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
                 contents.addView(child);
             }
         }
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index 8f6c849..93bf1e0 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -17,8 +17,6 @@
 package com.android.settings;
 
 
-import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.admin.DevicePolicyManager;
@@ -54,8 +52,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.TrustAgentUtils.TrustAgentComponentInfo;
-import com.android.settings.fingerprint.FingerprintEnrollFindSensor;
-import com.android.settings.fingerprint.FingerprintEnrollOnboard;
+import com.android.settings.fingerprint.FingerprintEnrollIntroduction;
 import com.android.settings.fingerprint.FingerprintSettings;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Index;
@@ -65,6 +62,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+
 /**
  * Gesture lock pattern settings.
  */
@@ -358,12 +357,9 @@
                     R.plurals.security_settings_fingerprint_preference_summary,
                     fingerprintCount, fingerprintCount));
             clazz = FingerprintSettings.class.getName();
-        } else if (!hasPassword) {
-            // No fingerprints registered, launch into enrollment wizard.
-            clazz = FingerprintEnrollOnboard.class.getName();
         } else {
-            // Lock thingy is already set up, launch directly into find sensor step from wizard.
-            clazz = FingerprintEnrollFindSensor.class.getName();
+            clazz = FingerprintEnrollIntroduction.class.getName();
+            intent.putExtra(FingerprintEnrollIntroduction.EXTRA_HAS_PASSWORD, hasPassword);
         }
         intent.setClassName("com.android.settings", clazz);
         fingerprintPreference.setIntent(intent);
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 5fb94f0..ea4f77a 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -113,5 +113,6 @@
     public static class TopLevelSettings extends SettingsActivity { /* empty */ }
     public static class ApnSettingsActivity extends SettingsActivity { /* empty */ }
     public static class WifiCallingSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class MemorySettingsActivity extends SettingsActivity { /* empty */ }
 }
 
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 89231df..f376644 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -1039,7 +1039,7 @@
      */
     private void buildDashboardCategories(List<DashboardCategory> categories) {
         categories.clear();
-        loadCategoriesFromResource(R.xml.dashboard_categories, categories);
+        loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
         updateTilesList(categories);
     }
 
@@ -1050,10 +1050,11 @@
      * @param resid The XML resource to load and parse.
      * @param target The list in which the parsed categories and tiles should be placed.
      */
-    private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) {
+    public static void loadCategoriesFromResource(int resid, List<DashboardCategory> target,
+            Context context) {
         XmlResourceParser parser = null;
         try {
-            parser = getResources().getXml(resid);
+            parser = context.getResources().getXml(resid);
             AttributeSet attrs = Xml.asAttributeSet(parser);
 
             int type;
@@ -1082,7 +1083,7 @@
                 if ("dashboard-category".equals(nodeName)) {
                     DashboardCategory category = new DashboardCategory();
 
-                    TypedArray sa = obtainStyledAttributes(
+                    TypedArray sa = context.obtainStyledAttributes(
                             attrs, com.android.internal.R.styleable.PreferenceHeader);
                     category.id = sa.getResourceId(
                             com.android.internal.R.styleable.PreferenceHeader_id,
@@ -1098,12 +1099,13 @@
                         }
                     }
                     sa.recycle();
-                    sa = obtainStyledAttributes(attrs, com.android.internal.R.styleable.Preference);
+                    sa = context.obtainStyledAttributes(attrs,
+                            com.android.internal.R.styleable.Preference);
                     tv = sa.peekValue(
                             com.android.internal.R.styleable.Preference_key);
                     if (tv != null && tv.type == TypedValue.TYPE_STRING) {
                         if (tv.resourceId != 0) {
-                            category.key = getString(tv.resourceId);
+                            category.key = context.getString(tv.resourceId);
                         } else {
                             category.key = tv.string.toString();
                         }
@@ -1121,7 +1123,7 @@
                         if (innerNodeName.equals("dashboard-tile")) {
                             DashboardTile tile = new DashboardTile();
 
-                            sa = obtainStyledAttributes(
+                            sa = context.obtainStyledAttributes(
                                     attrs, com.android.internal.R.styleable.PreferenceHeader);
                             tile.id = sa.getResourceId(
                                     com.android.internal.R.styleable.PreferenceHeader_id,
@@ -1163,11 +1165,13 @@
 
                                 String innerNodeName2 = parser.getName();
                                 if (innerNodeName2.equals("extra")) {
-                                    getResources().parseBundleExtra("extra", attrs, curBundle);
+                                    context.getResources().parseBundleExtra("extra", attrs,
+                                            curBundle);
                                     XmlUtils.skipCurrentTag(parser);
 
                                 } else if (innerNodeName2.equals("intent")) {
-                                    tile.intent = Intent.parseIntent(getResources(), parser, attrs);
+                                    tile.intent = Intent.parseIntent(context.getResources(), parser,
+                                            attrs);
 
                                 } else {
                                     XmlUtils.skipCurrentTag(parser);
@@ -1180,7 +1184,7 @@
                             }
 
                             // Show the SIM Cards setting if there are more than 2 SIMs installed.
-                            if(tile.id != R.id.sim_settings || Utils.showSimCardTile(this)){
+                            if(tile.id != R.id.sim_settings || Utils.showSimCardTile(context)){
                                 category.addTile(tile);
                             }
 
diff --git a/src/com/android/settings/accounts/AuthenticatorHelper.java b/src/com/android/settings/accounts/AuthenticatorHelper.java
index 86e0da5..56a689c 100644
--- a/src/com/android/settings/accounts/AuthenticatorHelper.java
+++ b/src/com/android/settings/accounts/AuthenticatorHelper.java
@@ -96,7 +96,8 @@
     /**
      * Gets an icon associated with a particular account type. If none found, return null.
      * @param accountType the type of account
-     * @return a drawable for the icon or null if one cannot be found.
+     * @return a drawable for the icon or a default icon returned by
+     * {@link PackageManager#getDefaultActivityIcon} if one cannot be found.
      */
     public Drawable getDrawableForType(Context context, final String accountType) {
         Drawable icon = null;
diff --git a/src/com/android/settings/accounts/ChooseAccountActivity.java b/src/com/android/settings/accounts/ChooseAccountActivity.java
index c4dace8..12077af 100644
--- a/src/com/android/settings/accounts/ChooseAccountActivity.java
+++ b/src/com/android/settings/accounts/ChooseAccountActivity.java
@@ -214,7 +214,8 @@
     /**
      * Gets an icon associated with a particular account type. If none found, return null.
      * @param accountType the type of account
-     * @return a drawable for the icon or null if one cannot be found.
+     * @return a drawable for the icon or a default icon returned by
+     * {@link PackageManager#getDefaultActivityIcon} if one cannot be found.
      */
     protected Drawable getDrawableForType(final String accountType) {
         Drawable icon = null;
@@ -225,14 +226,16 @@
                 icon = getPackageManager().getUserBadgedIcon(
                         authContext.getDrawable(desc.iconId), mUserHandle);
             } catch (PackageManager.NameNotFoundException e) {
-                // TODO: place holder icon for missing account icons?
                 Log.w(TAG, "No icon name for account type " + accountType);
             } catch (Resources.NotFoundException e) {
-                // TODO: place holder icon for missing account icons?
                 Log.w(TAG, "No icon resource for account type " + accountType);
             }
         }
-        return icon;
+        if (icon != null) {
+            return icon;
+        } else {
+            return getPackageManager().getDefaultActivityIcon();
+        }
     }
 
     /**
diff --git a/src/com/android/settings/deviceinfo/MigrateEstimateTask.java b/src/com/android/settings/deviceinfo/MigrateEstimateTask.java
index bc8ff92..34a44ac 100644
--- a/src/com/android/settings/deviceinfo/MigrateEstimateTask.java
+++ b/src/com/android/settings/deviceinfo/MigrateEstimateTask.java
@@ -104,6 +104,7 @@
     protected void onPostExecute(Long result) {
         mSizeBytes = result;
         mTimeMillis = (mSizeBytes * DateUtils.SECOND_IN_MILLIS) / SPEED_ESTIMATE_BPS;
+        mTimeMillis = Math.max(mTimeMillis, DateUtils.SECOND_IN_MILLIS);
 
         final String size = Formatter.formatFileSize(mContext, mSizeBytes);
         final String time = DateUtils.formatDuration(mTimeMillis).toString();
diff --git a/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
index 886cdb2..49c5e82 100644
--- a/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
@@ -17,12 +17,15 @@
 package com.android.settings.fingerprint;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.res.ColorStateList;
 import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
@@ -50,6 +53,12 @@
     private static final int FINISH_DELAY = 250;
 
     /**
+     * If we don't see progress during this time, we show an error message to remind the user that
+     * he needs to lift the finger and touch again.
+     */
+    private static final int HINT_TIMEOUT_DURATION = 2500;
+
+    /**
      * How long the user needs to touch the icon until we show the dialog.
      */
     private static final long ICON_TOUCH_DURATION_UNTIL_DIALOG_SHOWN = 500;
@@ -67,10 +76,15 @@
     private TextView mRepeatMessage;
     private TextView mErrorText;
     private Interpolator mFastOutSlowInInterpolator;
+    private Interpolator mLinearOutSlowInInterpolator;
+    private Interpolator mFastOutLinearInInterpolator;
     private int mIconTouchCount;
     private FingerprintEnrollSidecar mSidecar;
     private boolean mAnimationCancelled;
     private AnimatedVectorDrawable mIconAnimationDrawable;
+    private int mIndicatorBackgroundRestingColor;
+    private int mIndicatorBackgroundActivatedColor;
+    private boolean mRestoring;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -86,6 +100,10 @@
         mIconAnimationDrawable.registerAnimationCallback(mIconAnimationCallback);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
                 this, android.R.interpolator.fast_out_slow_in);
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                this, android.R.interpolator.linear_out_slow_in);
+        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
+                this, android.R.interpolator.fast_out_linear_in);
         mFingerprintAnimator.setOnTouchListener(new View.OnTouchListener() {
             @Override
             public boolean onTouch(View v, MotionEvent event) {
@@ -104,6 +122,11 @@
                 return true;
             }
         });
+        mIndicatorBackgroundRestingColor
+                = getColor(R.color.fingerprint_indicator_background_resting);
+        mIndicatorBackgroundActivatedColor
+                = getColor(R.color.fingerprint_indicator_background_activated);
+        mRestoring = savedInstanceState != null;
     }
 
     @Override
@@ -117,6 +140,9 @@
         mSidecar.setListener(this);
         updateProgress(false /* animate */);
         updateDescription();
+        if (mRestoring) {
+            startIconAnimation();
+        }
     }
 
     @Override
@@ -158,6 +184,34 @@
         mProgressAnim = anim;
     }
 
+    private void animateFlash() {
+        ValueAnimator anim = ValueAnimator.ofArgb(mIndicatorBackgroundRestingColor,
+                mIndicatorBackgroundActivatedColor);
+        final ValueAnimator.AnimatorUpdateListener listener =
+                new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                mFingerprintAnimator.setBackgroundTintList(ColorStateList.valueOf(
+                        (Integer) animation.getAnimatedValue()));
+            }
+        };
+        anim.addUpdateListener(listener);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                ValueAnimator anim = ValueAnimator.ofArgb(mIndicatorBackgroundActivatedColor,
+                        mIndicatorBackgroundRestingColor);
+                anim.addUpdateListener(listener);
+                anim.setDuration(300);
+                anim.setInterpolator(mLinearOutSlowInInterpolator);
+                anim.start();
+            }
+        });
+        anim.setInterpolator(mFastOutSlowInInterpolator);
+        anim.setDuration(300);
+        anim.start();
+    }
+
     private void launchFinish(byte[] token) {
         Intent intent = new Intent();
         intent.setClassName("com.android.settings", FingerprintEnrollFinish.class.getName());
@@ -187,7 +241,7 @@
 
     @Override
     public void onEnrollmentError(CharSequence errString) {
-        mErrorText.setText(errString);
+        showError(errString);
         stopIconAnimation();
     }
 
@@ -195,7 +249,10 @@
     public void onEnrollmentProgressChange(int steps, int remaining) {
         updateProgress(true /* animate */);
         updateDescription();
-        mErrorText.setText("");
+        clearError();
+        animateFlash();
+        mErrorText.removeCallbacks(mTouchAgainRunnable);
+        mErrorText.postDelayed(mTouchAgainRunnable, HINT_TIMEOUT_DURATION);
     }
 
     private void updateProgress(boolean animate) {
@@ -221,6 +278,44 @@
         new IconTouchDialog().show(getFragmentManager(), null /* tag */);
     }
 
+    private void showError(CharSequence error) {
+        mErrorText.setText(error);
+        if (mErrorText.getVisibility() == View.INVISIBLE) {
+            mErrorText.setVisibility(View.VISIBLE);
+            mErrorText.setTranslationY(getResources().getDimensionPixelSize(
+                    R.dimen.fingerprint_error_text_appear_distance));
+            mErrorText.setAlpha(0f);
+            mErrorText.animate()
+                    .alpha(1f)
+                    .translationY(0f)
+                    .setDuration(200)
+                    .setInterpolator(mLinearOutSlowInInterpolator)
+                    .start();
+        } else {
+            mErrorText.animate().cancel();
+            mErrorText.setAlpha(1f);
+            mErrorText.setTranslationY(0f);
+        }
+    }
+
+    private void clearError() {
+        if (mErrorText.getVisibility() == View.VISIBLE) {
+            mErrorText.animate()
+                    .alpha(0f)
+                    .translationY(getResources().getDimensionPixelSize(
+                            R.dimen.fingerprint_error_text_disappear_distance))
+                    .setDuration(100)
+                    .setInterpolator(mFastOutLinearInInterpolator)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mErrorText.setVisibility(View.INVISIBLE);
+                        }
+                    })
+                    .start();
+        }
+    }
+
     private final Animator.AnimatorListener mProgressAnimationListener
             = new Animator.AnimatorListener() {
 
@@ -274,6 +369,13 @@
         }
     };
 
+    private final Runnable mTouchAgainRunnable = new Runnable() {
+        @Override
+        public void run() {
+            showError(getString(R.string.security_settings_fingerprint_enroll_lift_touch_again));
+        }
+    };
+
     private static class IconTouchDialog extends DialogFragment {
 
         @Override
diff --git a/src/com/android/settings/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/fingerprint/FingerprintEnrollFindSensor.java
index b3a5d22..552ed71 100644
--- a/src/com/android/settings/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/fingerprint/FingerprintEnrollFindSensor.java
@@ -73,6 +73,7 @@
             }
         } else if (requestCode == ENROLLING) {
             if (resultCode == RESULT_FINISHED) {
+                setResult(RESULT_FINISHED);
                 finish();
             }
         } else {
diff --git a/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
new file mode 100644
index 0000000..a488358
--- /dev/null
+++ b/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settings.fingerprint;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import com.android.settings.HelpUtils;
+import com.android.settings.R;
+
+/**
+ * Onboarding activity for fingerprint enrollment.
+ */
+public class FingerprintEnrollIntroduction extends FingerprintEnrollBase {
+
+    public static final String EXTRA_HAS_PASSWORD = "fp_existing_password";
+    private boolean mHasPassword;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.fingerprint_enroll_introduction);
+        setHeaderText(R.string.security_settings_fingerprint_enroll_introduction_title);
+        findViewById(R.id.cancel_button).setOnClickListener(this);
+        findViewById(R.id.learn_more_button).setOnClickListener(this);
+        mHasPassword = getIntent().getBooleanExtra(EXTRA_HAS_PASSWORD, false);
+    }
+
+    @Override
+    protected void onNextButtonClick() {
+        Intent intent = new Intent();
+        final String clazz;
+        if (!mHasPassword) {
+            // No fingerprints registered, launch into enrollment wizard.
+            clazz = FingerprintEnrollOnboard.class.getName();
+        } else {
+            // Lock thingy is already set up, launch directly into find sensor step from wizard.
+            clazz = FingerprintEnrollFindSensor.class.getName();
+        }
+        intent.setClassName("com.android.settings", clazz);
+        startActivityForResult(intent, 0);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (resultCode == RESULT_FINISHED) {
+            finish();
+        } else {
+            super.onActivityResult(requestCode, resultCode, data);
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v.getId() == R.id.cancel_button) {
+            finish();
+        }
+        if (v.getId() == R.id.learn_more_button) {
+            launchFingerprintHelp();
+        }
+        super.onClick(v);
+    }
+
+    private void launchFingerprintHelp() {
+        Intent helpIntent = HelpUtils.getHelpIntent(this,
+                getString(R.string.help_url_fingerprint), getClass().getName());
+        startActivity(helpIntent);
+    }
+}
diff --git a/src/com/android/settings/fingerprint/FingerprintEnrollOnboard.java b/src/com/android/settings/fingerprint/FingerprintEnrollOnboard.java
index f9ad2ba..b78636a 100644
--- a/src/com/android/settings/fingerprint/FingerprintEnrollOnboard.java
+++ b/src/com/android/settings/fingerprint/FingerprintEnrollOnboard.java
@@ -49,6 +49,7 @@
         if (requestCode == CHOOSE_LOCK_GENERIC_REQUEST && resultCode == RESULT_FINISHED) {
             byte[] token = data.getByteArrayExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
+            setResult(RESULT_FINISHED);
             launchFindSensor(token);
         } else {
             super.onActivityResult(requestCode, resultCode, data);
diff --git a/src/com/android/settings/print/PrintSettingsFragment.java b/src/com/android/settings/print/PrintSettingsFragment.java
index ebd51d5..d737282 100644
--- a/src/com/android/settings/print/PrintSettingsFragment.java
+++ b/src/com/android/settings/print/PrintSettingsFragment.java
@@ -17,6 +17,7 @@
 package com.android.settings.print;
 
 import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ActivityNotFoundException;
 import android.content.AsyncTaskLoader;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -26,6 +27,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -46,13 +48,12 @@
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.Button;
 import android.widget.Spinner;
 import android.widget.TextView;
 
@@ -75,8 +76,8 @@
  * Fragment with the top level print settings.
  */
 public class PrintSettingsFragment extends SettingsPreferenceFragment
-        implements DialogCreatable, Indexable, OnItemSelectedListener {
-
+        implements DialogCreatable, Indexable, OnItemSelectedListener, OnClickListener {
+    public static final String TAG = "PrintSettingsFragment";
     private static final int LOADER_ID_PRINT_JOBS_LOADER = 1;
 
     private static final String PRINT_JOBS_CATEGORY = "print_jobs_category";
@@ -99,6 +100,8 @@
     private static final String EXTRA_PRINT_SERVICE_COMPONENT_NAME =
             "EXTRA_PRINT_SERVICE_COMPONENT_NAME";
 
+    private static final int ORDER_LAST = 1000;
+
     private final PackageMonitor mSettingsPackageMonitor = new SettingsPackageMonitor();
 
     private final Handler mHandler = new Handler() {
@@ -122,6 +125,7 @@
     private PrintJobsController mPrintJobsController;
     private UserAdapter mProfileSpinnerAdapter;
     private Spinner mSpinner;
+    private Button mAddNewServiceButton;
 
     @Override
     protected int getMetricsCategory() {
@@ -167,18 +171,6 @@
     }
 
     @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        super.onCreateOptionsMenu(menu, inflater);
-        String searchUri = Settings.Secure.getString(getContentResolver(),
-                Settings.Secure.PRINT_SERVICE_SEARCH_URI);
-        if (!TextUtils.isEmpty(searchUri)) {
-            MenuItem menuItem = menu.add(R.string.print_menu_item_add_service);
-            menuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER);
-            menuItem.setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)));
-        }
-    }
-
-    @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         ViewGroup contentRoot = (ViewGroup) getListView().getParent();
@@ -186,6 +178,15 @@
                 R.layout.empty_print_state, contentRoot, false);
         TextView textView = (TextView) emptyView.findViewById(R.id.message);
         textView.setText(R.string.print_no_services_installed);
+
+        final Intent addNewServiceIntent = createAddNewServiceIntentOrNull();
+        if (addNewServiceIntent != null) {
+            mAddNewServiceButton = (Button) emptyView.findViewById(R.id.add_new_service);
+            mAddNewServiceButton.setOnClickListener(this);
+            // The empty is used elsewhere too so it's hidden by default.
+            mAddNewServiceButton.setVisibility(View.VISIBLE);
+        }
+
         contentRoot.addView(emptyView);
         getListView().setEmptyView(emptyView);
 
@@ -210,7 +211,9 @@
         List<ComponentName> enabledServices = PrintSettingsUtils
                 .readEnabledPrintServices(getActivity());
 
-        List<ResolveInfo> installedServices = getActivity().getPackageManager()
+        final PackageManager pm = getActivity().getPackageManager();
+
+        List<ResolveInfo> installedServices = pm
                 .queryIntentServices(
                         new Intent(android.printservice.PrintService.SERVICE_INTERFACE),
                         PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
@@ -241,6 +244,11 @@
                 preference.setSummary(getString(R.string.print_feature_state_off));
             }
 
+            final Drawable drawable = installedService.loadIcon(pm);
+            if (drawable != null) {
+                preference.setIcon(drawable);
+            }
+
             Bundle extras = preference.getExtras();
             extras.putString(EXTRA_PREFERENCE_KEY, preference.getKey());
             extras.putBoolean(EXTRA_CHECKED, serviceEnabled);
@@ -281,9 +289,37 @@
 
         if (mPrintServicesCategory.getPreferenceCount() == 0) {
             getPreferenceScreen().removePreference(mPrintServicesCategory);
+        } else {
+            final Preference addNewServicePreference = newAddServicePreferenceOrNull();
+            if (addNewServicePreference != null) {
+                mPrintServicesCategory.addPreference(addNewServicePreference);
+            }
         }
     }
 
+    private Preference newAddServicePreferenceOrNull() {
+        final Intent addNewServiceIntent = createAddNewServiceIntentOrNull();
+        if (addNewServiceIntent == null) {
+            return null;
+        }
+        Preference preference = new Preference(getContext());
+        preference.setTitle(R.string.print_menu_item_add_service);
+        preference.setIcon(R.drawable.ic_menu_add);
+        preference.setOrder(ORDER_LAST);
+        preference.setIntent(addNewServiceIntent);
+        preference.setPersistent(false);
+        return preference;
+    }
+
+    private Intent createAddNewServiceIntentOrNull() {
+        final String searchUri = Settings.Secure.getString(getContentResolver(),
+                Settings.Secure.PRINT_SERVICE_SEARCH_URI);
+        if (TextUtils.isEmpty(searchUri)) {
+            return null;
+        }
+        return new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
+    }
+
     private void startSubSettingsIfNeeded() {
         if (getArguments() == null) {
             return;
@@ -316,6 +352,20 @@
         // Nothing to do
     }
 
+    @Override
+    public void onClick(View v) {
+        if (mAddNewServiceButton == v) {
+            final Intent addNewServiceIntent = createAddNewServiceIntentOrNull();
+            if (addNewServiceIntent != null) { // check again just in case.
+                try {
+                    startActivity(addNewServiceIntent);
+                } catch (ActivityNotFoundException e) {
+                    Log.w(TAG, "Unable to start activity", e);
+                }
+            }
+        }
+    }
+
     private class SettingsPackageMonitor extends PackageMonitor {
         @Override
         public void onPackageAdded(String packageName, int uid) {
diff --git a/src/com/android/settings/widget/MatchParentShrinkingLinearLayout.java b/src/com/android/settings/widget/MatchParentShrinkingLinearLayout.java
new file mode 100644
index 0000000..79971e5
--- /dev/null
+++ b/src/com/android/settings/widget/MatchParentShrinkingLinearLayout.java
@@ -0,0 +1,1586 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settings.widget;
+
+import com.android.internal.R;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+/**
+ * A LinearLayout with a twist: if the contents don't fit, it takes space away from the
+ * MATCH_PARENT children, instead of taking it from the weighted ones.
+ *
+ * TODO: Remove once we redesign the ChooseLockPattern screen with a sane layout.
+ */
+public class MatchParentShrinkingLinearLayout extends ViewGroup {
+    /** @hide */
+    @IntDef({HORIZONTAL, VERTICAL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface OrientationMode {}
+
+    public static final int HORIZONTAL = 0;
+    public static final int VERTICAL = 1;
+
+    /** @hide */
+    @IntDef(flag = true,
+            value = {
+                SHOW_DIVIDER_NONE,
+                SHOW_DIVIDER_BEGINNING,
+                SHOW_DIVIDER_MIDDLE,
+                SHOW_DIVIDER_END
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DividerMode {}
+
+    /**
+     * Don't show any dividers.
+     */
+    public static final int SHOW_DIVIDER_NONE = 0;
+    /**
+     * Show a divider at the beginning of the group.
+     */
+    public static final int SHOW_DIVIDER_BEGINNING = 1;
+    /**
+     * Show dividers between each item in the group.
+     */
+    public static final int SHOW_DIVIDER_MIDDLE = 2;
+    /**
+     * Show a divider at the end of the group.
+     */
+    public static final int SHOW_DIVIDER_END = 4;
+
+    /**
+     * Whether the children of this layout are baseline aligned.  Only applicable
+     * if {@link #mOrientation} is horizontal.
+     */
+    @ViewDebug.ExportedProperty(category = "layout")
+    private boolean mBaselineAligned = true;
+
+    /**
+     * If this layout is part of another layout that is baseline aligned,
+     * use the child at this index as the baseline.
+     *
+     * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
+     * with whether the children of this layout are baseline aligned.
+     */
+    @ViewDebug.ExportedProperty(category = "layout")
+    private int mBaselineAlignedChildIndex = -1;
+
+    /**
+     * The additional offset to the child's baseline.
+     * We'll calculate the baseline of this layout as we measure vertically; for
+     * horizontal linear layouts, the offset of 0 is appropriate.
+     */
+    @ViewDebug.ExportedProperty(category = "measurement")
+    private int mBaselineChildTop = 0;
+
+    @ViewDebug.ExportedProperty(category = "measurement")
+    private int mOrientation;
+
+    @ViewDebug.ExportedProperty(category = "measurement", flagMapping = {
+            @ViewDebug.FlagToString(mask = -1,
+                equals = -1, name = "NONE"),
+            @ViewDebug.FlagToString(mask = Gravity.NO_GRAVITY,
+                equals = Gravity.NO_GRAVITY,name = "NONE"),
+            @ViewDebug.FlagToString(mask = Gravity.TOP,
+                equals = Gravity.TOP, name = "TOP"),
+            @ViewDebug.FlagToString(mask = Gravity.BOTTOM,
+                equals = Gravity.BOTTOM, name = "BOTTOM"),
+            @ViewDebug.FlagToString(mask = Gravity.LEFT,
+                equals = Gravity.LEFT, name = "LEFT"),
+            @ViewDebug.FlagToString(mask = Gravity.RIGHT,
+                equals = Gravity.RIGHT, name = "RIGHT"),
+            @ViewDebug.FlagToString(mask = Gravity.START,
+                equals = Gravity.START, name = "START"),
+            @ViewDebug.FlagToString(mask = Gravity.END,
+                equals = Gravity.END, name = "END"),
+            @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL,
+                equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"),
+            @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL,
+                equals = Gravity.FILL_VERTICAL, name = "FILL_VERTICAL"),
+            @ViewDebug.FlagToString(mask = Gravity.CENTER_HORIZONTAL,
+                equals = Gravity.CENTER_HORIZONTAL, name = "CENTER_HORIZONTAL"),
+            @ViewDebug.FlagToString(mask = Gravity.FILL_HORIZONTAL,
+                equals = Gravity.FILL_HORIZONTAL, name = "FILL_HORIZONTAL"),
+            @ViewDebug.FlagToString(mask = Gravity.CENTER,
+                equals = Gravity.CENTER, name = "CENTER"),
+            @ViewDebug.FlagToString(mask = Gravity.FILL,
+                equals = Gravity.FILL, name = "FILL"),
+            @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
+                equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
+        }, formatToHexString = true)
+    private int mGravity = Gravity.START | Gravity.TOP;
+
+    @ViewDebug.ExportedProperty(category = "measurement")
+    private int mTotalLength;
+
+    @ViewDebug.ExportedProperty(category = "layout")
+    private float mWeightSum;
+
+    @ViewDebug.ExportedProperty(category = "layout")
+    private boolean mUseLargestChild;
+
+    private int[] mMaxAscent;
+    private int[] mMaxDescent;
+
+    private static final int VERTICAL_GRAVITY_COUNT = 4;
+
+    private static final int INDEX_CENTER_VERTICAL = 0;
+    private static final int INDEX_TOP = 1;
+    private static final int INDEX_BOTTOM = 2;
+    private static final int INDEX_FILL = 3;
+
+    private Drawable mDivider;
+    private int mDividerWidth;
+    private int mDividerHeight;
+    private int mShowDividers;
+    private int mDividerPadding;
+
+    private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
+
+    public MatchParentShrinkingLinearLayout(Context context) {
+        this(context, null);
+    }
+
+    public MatchParentShrinkingLinearLayout(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public MatchParentShrinkingLinearLayout(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public MatchParentShrinkingLinearLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, com.android.internal.R.styleable.LinearLayout, defStyleAttr, defStyleRes);
+
+        int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1);
+        if (index >= 0) {
+            setOrientation(index);
+        }
+
+        index = a.getInt(com.android.internal.R.styleable.LinearLayout_gravity, -1);
+        if (index >= 0) {
+            setGravity(index);
+        }
+
+        boolean baselineAligned = a.getBoolean(R.styleable.LinearLayout_baselineAligned, true);
+        if (!baselineAligned) {
+            setBaselineAligned(baselineAligned);
+        }
+
+        mWeightSum = a.getFloat(R.styleable.LinearLayout_weightSum, -1.0f);
+
+        mBaselineAlignedChildIndex = a.getInt(
+                com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1);
+
+        mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false);
+
+        setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider));
+        mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE);
+        mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0);
+
+        a.recycle();
+    }
+
+    /**
+     * Set how dividers should be shown between items in this layout
+     *
+     * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
+     *                     {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
+     *                     or {@link #SHOW_DIVIDER_NONE} to show no dividers.
+     */
+    public void setShowDividers(@DividerMode int showDividers) {
+        if (showDividers != mShowDividers) {
+            requestLayout();
+        }
+        mShowDividers = showDividers;
+    }
+
+    @Override
+    public boolean shouldDelayChildPressedState() {
+        return false;
+    }
+
+    /**
+     * @return A flag set indicating how dividers should be shown around items.
+     * @see #setShowDividers(int)
+     */
+    @DividerMode
+    public int getShowDividers() {
+        return mShowDividers;
+    }
+
+    /**
+     * @return the divider Drawable that will divide each item.
+     *
+     * @see #setDividerDrawable(android.graphics.drawable.Drawable)
+     *
+     * @attr ref android.R.styleable#LinearLayout_divider
+     */
+    public Drawable getDividerDrawable() {
+        return mDivider;
+    }
+
+    /**
+     * Set a drawable to be used as a divider between items.
+     *
+     * @param divider Drawable that will divide each item.
+     *
+     * @see #setShowDividers(int)
+     *
+     * @attr ref android.R.styleable#LinearLayout_divider
+     */
+    public void setDividerDrawable(Drawable divider) {
+        if (divider == mDivider) {
+            return;
+        }
+        mDivider = divider;
+        if (divider != null) {
+            mDividerWidth = divider.getIntrinsicWidth();
+            mDividerHeight = divider.getIntrinsicHeight();
+        } else {
+            mDividerWidth = 0;
+            mDividerHeight = 0;
+        }
+        setWillNotDraw(divider == null);
+        requestLayout();
+    }
+
+    /**
+     * Set padding displayed on both ends of dividers.
+     *
+     * @param padding Padding value in pixels that will be applied to each end
+     *
+     * @see #setShowDividers(int)
+     * @see #setDividerDrawable(android.graphics.drawable.Drawable)
+     * @see #getDividerPadding()
+     */
+    public void setDividerPadding(int padding) {
+        mDividerPadding = padding;
+    }
+
+    /**
+     * Get the padding size used to inset dividers in pixels
+     *
+     * @see #setShowDividers(int)
+     * @see #setDividerDrawable(android.graphics.drawable.Drawable)
+     * @see #setDividerPadding(int)
+     */
+    public int getDividerPadding() {
+        return mDividerPadding;
+    }
+
+    /**
+     * Get the width of the current divider drawable.
+     *
+     * @hide Used internally by framework.
+     */
+    public int getDividerWidth() {
+        return mDividerWidth;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mDivider == null) {
+            return;
+        }
+
+        if (mOrientation == VERTICAL) {
+            drawDividersVertical(canvas);
+        } else {
+            drawDividersHorizontal(canvas);
+        }
+    }
+
+    void drawDividersVertical(Canvas canvas) {
+        final int count = getVirtualChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getVirtualChildAt(i);
+
+            if (child != null && child.getVisibility() != GONE) {
+                if (hasDividerBeforeChildAt(i)) {
+                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                    final int top = child.getTop() - lp.topMargin - mDividerHeight;
+                    drawHorizontalDivider(canvas, top);
+                }
+            }
+        }
+
+        if (hasDividerBeforeChildAt(count)) {
+            final View child = getVirtualChildAt(count - 1);
+            int bottom = 0;
+            if (child == null) {
+                bottom = getHeight() - getPaddingBottom() - mDividerHeight;
+            } else {
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                bottom = child.getBottom() + lp.bottomMargin;
+            }
+            drawHorizontalDivider(canvas, bottom);
+        }
+    }
+
+    void drawDividersHorizontal(Canvas canvas) {
+        final int count = getVirtualChildCount();
+        final boolean isLayoutRtl = isLayoutRtl();
+        for (int i = 0; i < count; i++) {
+            final View child = getVirtualChildAt(i);
+
+            if (child != null && child.getVisibility() != GONE) {
+                if (hasDividerBeforeChildAt(i)) {
+                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                    final int position;
+                    if (isLayoutRtl) {
+                        position = child.getRight() + lp.rightMargin;
+                    } else {
+                        position = child.getLeft() - lp.leftMargin - mDividerWidth;
+                    }
+                    drawVerticalDivider(canvas, position);
+                }
+            }
+        }
+
+        if (hasDividerBeforeChildAt(count)) {
+            final View child = getVirtualChildAt(count - 1);
+            int position;
+            if (child == null) {
+                if (isLayoutRtl) {
+                    position = getPaddingLeft();
+                } else {
+                    position = getWidth() - getPaddingRight() - mDividerWidth;
+                }
+            } else {
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                if (isLayoutRtl) {
+                    position = child.getLeft() - lp.leftMargin - mDividerWidth;
+                } else {
+                    position = child.getRight() + lp.rightMargin;
+                }
+            }
+            drawVerticalDivider(canvas, position);
+        }
+    }
+
+    void drawHorizontalDivider(Canvas canvas, int top) {
+        mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
+                getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
+        mDivider.draw(canvas);
+    }
+
+    void drawVerticalDivider(Canvas canvas, int left) {
+        mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
+                left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
+        mDivider.draw(canvas);
+    }
+
+    /**
+     * <p>Indicates whether widgets contained within this layout are aligned
+     * on their baseline or not.</p>
+     *
+     * @return true when widgets are baseline-aligned, false otherwise
+     */
+    public boolean isBaselineAligned() {
+        return mBaselineAligned;
+    }
+
+    /**
+     * <p>Defines whether widgets contained in this layout are
+     * baseline-aligned or not.</p>
+     *
+     * @param baselineAligned true to align widgets on their baseline,
+     *         false otherwise
+     *
+     * @attr ref android.R.styleable#LinearLayout_baselineAligned
+     */
+    @android.view.RemotableViewMethod
+    public void setBaselineAligned(boolean baselineAligned) {
+        mBaselineAligned = baselineAligned;
+    }
+
+    /**
+     * When true, all children with a weight will be considered having
+     * the minimum size of the largest child. If false, all children are
+     * measured normally.
+     *
+     * @return True to measure children with a weight using the minimum
+     *         size of the largest child, false otherwise.
+     *
+     * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
+     */
+    public boolean isMeasureWithLargestChildEnabled() {
+        return mUseLargestChild;
+    }
+
+    /**
+     * When set to true, all children with a weight will be considered having
+     * the minimum size of the largest child. If false, all children are
+     * measured normally.
+     *
+     * Disabled by default.
+     *
+     * @param enabled True to measure children with a weight using the
+     *        minimum size of the largest child, false otherwise.
+     *
+     * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
+     */
+    @android.view.RemotableViewMethod
+    public void setMeasureWithLargestChildEnabled(boolean enabled) {
+        mUseLargestChild = enabled;
+    }
+
+    @Override
+    public int getBaseline() {
+        if (mBaselineAlignedChildIndex < 0) {
+            return super.getBaseline();
+        }
+
+        if (getChildCount() <= mBaselineAlignedChildIndex) {
+            throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
+                    + "set to an index that is out of bounds.");
+        }
+
+        final View child = getChildAt(mBaselineAlignedChildIndex);
+        final int childBaseline = child.getBaseline();
+
+        if (childBaseline == -1) {
+            if (mBaselineAlignedChildIndex == 0) {
+                // this is just the default case, safe to return -1
+                return -1;
+            }
+            // the user picked an index that points to something that doesn't
+            // know how to calculate its baseline.
+            throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
+                    + "points to a View that doesn't know how to get its baseline.");
+        }
+
+        // TODO: This should try to take into account the virtual offsets
+        // (See getNextLocationOffset and getLocationOffset)
+        // We should add to childTop:
+        // sum([getNextLocationOffset(getChildAt(i)) / i < mBaselineAlignedChildIndex])
+        // and also add:
+        // getLocationOffset(child)
+        int childTop = mBaselineChildTop;
+
+        if (mOrientation == VERTICAL) {
+            final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
+            if (majorGravity != Gravity.TOP) {
+               switch (majorGravity) {
+                   case Gravity.BOTTOM:
+                       childTop = mBottom - mTop - mPaddingBottom - mTotalLength;
+                       break;
+
+                   case Gravity.CENTER_VERTICAL:
+                       childTop += ((mBottom - mTop - mPaddingTop - mPaddingBottom) -
+                               mTotalLength) / 2;
+                       break;
+               }
+            }
+        }
+
+        LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        return childTop + lp.topMargin + childBaseline;
+    }
+
+    /**
+     * @return The index of the child that will be used if this layout is
+     *   part of a larger layout that is baseline aligned, or -1 if none has
+     *   been set.
+     */
+    public int getBaselineAlignedChildIndex() {
+        return mBaselineAlignedChildIndex;
+    }
+
+    /**
+     * @param i The index of the child that will be used if this layout is
+     *          part of a larger layout that is baseline aligned.
+     *
+     * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
+     */
+    @android.view.RemotableViewMethod
+    public void setBaselineAlignedChildIndex(int i) {
+        if ((i < 0) || (i >= getChildCount())) {
+            throw new IllegalArgumentException("base aligned child index out "
+                    + "of range (0, " + getChildCount() + ")");
+        }
+        mBaselineAlignedChildIndex = i;
+    }
+
+    /**
+     * <p>Returns the view at the specified index. This method can be overriden
+     * to take into account virtual children. Refer to
+     * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
+     * for an example.</p>
+     *
+     * @param index the child's index
+     * @return the child at the specified index
+     */
+    View getVirtualChildAt(int index) {
+        return getChildAt(index);
+    }
+
+    /**
+     * <p>Returns the virtual number of children. This number might be different
+     * than the actual number of children if the layout can hold virtual
+     * children. Refer to
+     * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
+     * for an example.</p>
+     *
+     * @return the virtual number of children
+     */
+    int getVirtualChildCount() {
+        return getChildCount();
+    }
+
+    /**
+     * Returns the desired weights sum.
+     *
+     * @return A number greater than 0.0f if the weight sum is defined, or
+     *         a number lower than or equals to 0.0f if not weight sum is
+     *         to be used.
+     */
+    public float getWeightSum() {
+        return mWeightSum;
+    }
+
+    /**
+     * Defines the desired weights sum. If unspecified the weights sum is computed
+     * at layout time by adding the layout_weight of each child.
+     *
+     * This can be used for instance to give a single child 50% of the total
+     * available space by giving it a layout_weight of 0.5 and setting the
+     * weightSum to 1.0.
+     *
+     * @param weightSum a number greater than 0.0f, or a number lower than or equals
+     *        to 0.0f if the weight sum should be computed from the children's
+     *        layout_weight
+     */
+    @android.view.RemotableViewMethod
+    public void setWeightSum(float weightSum) {
+        mWeightSum = Math.max(0.0f, weightSum);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mOrientation == VERTICAL) {
+            measureVertical(widthMeasureSpec, heightMeasureSpec);
+        } else {
+            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    /**
+     * Determines where to position dividers between children.
+     *
+     * @param childIndex Index of child to check for preceding divider
+     * @return true if there should be a divider before the child at childIndex
+     * @hide Pending API consideration. Currently only used internally by the system.
+     */
+    protected boolean hasDividerBeforeChildAt(int childIndex) {
+        if (childIndex == 0) {
+            return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
+        } else if (childIndex == getChildCount()) {
+            return (mShowDividers & SHOW_DIVIDER_END) != 0;
+        } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
+            boolean hasVisibleViewBefore = false;
+            for (int i = childIndex - 1; i >= 0; i--) {
+                if (getChildAt(i).getVisibility() != GONE) {
+                    hasVisibleViewBefore = true;
+                    break;
+                }
+            }
+            return hasVisibleViewBefore;
+        }
+        return false;
+    }
+
+    /**
+     * Measures the children when the orientation of this LinearLayout is set
+     * to {@link #VERTICAL}.
+     *
+     * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
+     * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
+     *
+     * @see #getOrientation()
+     * @see #setOrientation(int)
+     * @see #onMeasure(int, int)
+     */
+    void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
+        mTotalLength = 0;
+        int maxWidth = 0;
+        int childState = 0;
+        int alternativeMaxWidth = 0;
+        int weightedMaxWidth = 0;
+        boolean allFillParent = true;
+        float totalWeight = 0;
+
+        final int count = getVirtualChildCount();
+
+        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+
+        boolean matchWidth = false;
+        boolean skippedMeasure = false;
+
+        final int baselineChildIndex = mBaselineAlignedChildIndex;
+        final boolean useLargestChild = mUseLargestChild;
+
+        int largestChildHeight = Integer.MIN_VALUE;
+
+        // See how tall everyone is. Also remember max width.
+        for (int i = 0; i < count; ++i) {
+            final View child = getVirtualChildAt(i);
+
+            if (child == null) {
+                mTotalLength += measureNullChild(i);
+                continue;
+            }
+
+            if (child.getVisibility() == View.GONE) {
+               i += getChildrenSkipCount(child, i);
+               continue;
+            }
+
+            if (hasDividerBeforeChildAt(i)) {
+                mTotalLength += mDividerHeight;
+            }
+
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            totalWeight += lp.weight;
+
+            if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
+                // Optimization: don't bother measuring children who are going to use
+                // leftover space. These views will get measured again down below if
+                // there is any leftover space.
+                final int totalLength = mTotalLength;
+                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
+                skippedMeasure = true;
+            } else {
+                int oldHeight = Integer.MIN_VALUE;
+
+                if (lp.height == 0 && lp.weight > 0) {
+                    // heightMode is either UNSPECIFIED or AT_MOST, and this
+                    // child wanted to stretch to fill available space.
+                    // Translate that to WRAP_CONTENT so that it does not end up
+                    // with a height of 0
+                    oldHeight = 0;
+                    lp.height = LayoutParams.WRAP_CONTENT;
+                }
+
+                // Determine how big this child would like to be. If this or
+                // previous children have given a weight, then we allow it to
+                // use all available space (and we will shrink things later
+                // if needed).
+                measureChildBeforeLayout(
+                       child, i, widthMeasureSpec, 0, heightMeasureSpec,
+                       totalWeight == 0 ? mTotalLength : 0);
+
+                if (oldHeight != Integer.MIN_VALUE) {
+                   lp.height = oldHeight;
+                }
+
+                final int childHeight = child.getMeasuredHeight();
+                final int totalLength = mTotalLength;
+                mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
+                       lp.bottomMargin + getNextLocationOffset(child));
+
+                if (useLargestChild) {
+                    largestChildHeight = Math.max(childHeight, largestChildHeight);
+                }
+            }
+
+            /**
+             * If applicable, compute the additional offset to the child's baseline
+             * we'll need later when asked {@link #getBaseline}.
+             */
+            if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
+               mBaselineChildTop = mTotalLength;
+            }
+
+            // if we are trying to use a child index for our baseline, the above
+            // book keeping only works if there are no children above it with
+            // weight.  fail fast to aid the developer.
+            if (i < baselineChildIndex && lp.weight > 0) {
+                throw new RuntimeException("A child of LinearLayout with index "
+                        + "less than mBaselineAlignedChildIndex has weight > 0, which "
+                        + "won't work.  Either remove the weight, or don't set "
+                        + "mBaselineAlignedChildIndex.");
+            }
+
+            boolean matchWidthLocally = false;
+            if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
+                // The width of the linear layout will scale, and at least one
+                // child said it wanted to match our width. Set a flag
+                // indicating that we need to remeasure at least that view when
+                // we know our width.
+                matchWidth = true;
+                matchWidthLocally = true;
+            }
+
+            final int margin = lp.leftMargin + lp.rightMargin;
+            final int measuredWidth = child.getMeasuredWidth() + margin;
+            maxWidth = Math.max(maxWidth, measuredWidth);
+            childState = combineMeasuredStates(childState, child.getMeasuredState());
+
+            allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
+            if (lp.weight > 0) {
+                /*
+                 * Widths of weighted Views are bogus if we end up
+                 * remeasuring, so keep them separate.
+                 */
+                weightedMaxWidth = Math.max(weightedMaxWidth,
+                        matchWidthLocally ? margin : measuredWidth);
+            } else {
+                alternativeMaxWidth = Math.max(alternativeMaxWidth,
+                        matchWidthLocally ? margin : measuredWidth);
+            }
+
+            i += getChildrenSkipCount(child, i);
+        }
+
+        if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
+            mTotalLength += mDividerHeight;
+        }
+
+        if (useLargestChild &&
+                (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
+            mTotalLength = 0;
+
+            for (int i = 0; i < count; ++i) {
+                final View child = getVirtualChildAt(i);
+
+                if (child == null) {
+                    mTotalLength += measureNullChild(i);
+                    continue;
+                }
+
+                if (child.getVisibility() == GONE) {
+                    i += getChildrenSkipCount(child, i);
+                    continue;
+                }
+
+                final LayoutParams lp = (LayoutParams)
+                        child.getLayoutParams();
+                // Account for negative margins
+                final int totalLength = mTotalLength;
+                mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
+                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
+            }
+        }
+
+        // Add in our padding
+        mTotalLength += mPaddingTop + mPaddingBottom;
+
+        int heightSize = mTotalLength;
+
+        // Check against our minimum height
+        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
+
+        // Reconcile our calculated size with the heightMeasureSpec
+        int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
+        heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
+
+        // Either expand children with weight to take up available space or
+        // shrink them if they extend beyond our current bounds. If we skipped
+        // measurement on any children, we need to measure them now.
+        int delta = heightSize - mTotalLength;
+        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
+            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
+
+            mTotalLength = 0;
+
+            for (int i = 0; i < count; ++i) {
+                final View child = getVirtualChildAt(i);
+
+                if (child.getVisibility() == View.GONE) {
+                    continue;
+                }
+
+                LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+                float childExtra = lp.weight;
+
+                // MatchParentShrinkingLinearLayout custom code starts here.
+                if (childExtra > 0 && delta > 0) {
+                    // Child said it could absorb extra space -- give him his share
+                    int share = (int) (childExtra * delta / weightSum);
+                    weightSum -= childExtra;
+                    delta -= share;
+
+                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
+                            mPaddingLeft + mPaddingRight +
+                                    lp.leftMargin + lp.rightMargin, lp.width);
+
+                    // TODO: Use a field like lp.isMeasured to figure out if this
+                    // child has been previously measured
+                    if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
+                        // child was measured once already above...
+                        // base new measurement on stored values
+                        int childHeight = child.getMeasuredHeight() + share;
+                        if (childHeight < 0) {
+                            childHeight = 0;
+                        }
+
+                        child.measure(childWidthMeasureSpec,
+                                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
+                    } else {
+                        // child was skipped in the loop above.
+                        // Measure for this first time here
+                        child.measure(childWidthMeasureSpec,
+                                MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
+                                        MeasureSpec.EXACTLY));
+                    }
+
+                    // Child may now not fit in vertical dimension.
+                    childState = combineMeasuredStates(childState, child.getMeasuredState()
+                            & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
+                } else if (delta < 0 && lp.height == LayoutParams.MATCH_PARENT) {
+                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
+                            mPaddingLeft + mPaddingRight +
+                                    lp.leftMargin + lp.rightMargin, lp.width);
+
+                    int childHeight = child.getMeasuredHeight() + delta;
+                    if (childHeight < 0) {
+                        childHeight = 0;
+                    }
+                    delta -= childHeight - child.getMeasuredHeight();
+
+                    child.measure(childWidthMeasureSpec,
+                            MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
+
+                    // Child may now not fit in vertical dimension.
+                    childState = combineMeasuredStates(childState, child.getMeasuredState()
+                            & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
+                }
+                // MatchParentShrinkingLinearLayout custom code ends here.
+
+                final int margin =  lp.leftMargin + lp.rightMargin;
+                final int measuredWidth = child.getMeasuredWidth() + margin;
+                maxWidth = Math.max(maxWidth, measuredWidth);
+
+                boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
+                        lp.width == LayoutParams.MATCH_PARENT;
+
+                alternativeMaxWidth = Math.max(alternativeMaxWidth,
+                        matchWidthLocally ? margin : measuredWidth);
+
+                allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
+
+                final int totalLength = mTotalLength;
+                mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
+                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
+            }
+
+            // Add in our padding
+            mTotalLength += mPaddingTop + mPaddingBottom;
+            // TODO: Should we recompute the heightSpec based on the new total length?
+        } else {
+            alternativeMaxWidth = Math.max(alternativeMaxWidth,
+                                           weightedMaxWidth);
+
+
+            // We have no limit, so make all weighted views as tall as the largest child.
+            // Children will have already been measured once.
+            if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
+                for (int i = 0; i < count; i++) {
+                    final View child = getVirtualChildAt(i);
+
+                    if (child == null || child.getVisibility() == View.GONE) {
+                        continue;
+                    }
+
+                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+                    float childExtra = lp.weight;
+                    if (childExtra > 0) {
+                        child.measure(
+                                MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
+                                        MeasureSpec.EXACTLY),
+                                MeasureSpec.makeMeasureSpec(largestChildHeight,
+                                        MeasureSpec.EXACTLY));
+                    }
+                }
+            }
+        }
+
+        if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
+            maxWidth = alternativeMaxWidth;
+        }
+
+        maxWidth += mPaddingLeft + mPaddingRight;
+
+        // Check against our minimum width
+        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+                heightSizeAndState);
+
+        if (matchWidth) {
+            forceUniformWidth(count, heightMeasureSpec);
+        }
+    }
+
+    private void forceUniformWidth(int count, int heightMeasureSpec) {
+        // Pretend that the linear layout has an exact size.
+        int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
+                MeasureSpec.EXACTLY);
+        for (int i = 0; i< count; ++i) {
+           final View child = getVirtualChildAt(i);
+           if (child.getVisibility() != GONE) {
+               LayoutParams lp =
+                       ((LayoutParams)child.getLayoutParams());
+
+               if (lp.width == LayoutParams.MATCH_PARENT) {
+                   // Temporarily force children to reuse their old measured height
+                   // FIXME: this may not be right for something like wrapping text?
+                   int oldHeight = lp.height;
+                   lp.height = child.getMeasuredHeight();
+
+                   // Remeasue with new dimensions
+                   measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
+                   lp.height = oldHeight;
+               }
+           }
+        }
+    }
+
+    /**
+     * Measures the children when the orientation of this LinearLayout is set
+     * to {@link #HORIZONTAL}.
+     *
+     * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
+     * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
+     *
+     * @see #getOrientation()
+     * @see #setOrientation(int)
+     * @see #onMeasure(int, int)
+     */
+    void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
+        // MatchParentShrinkingLinearLayout custom code starts here.
+        throw new IllegalStateException("horizontal mode not supported.");
+        // MatchParentShrinkingLinearLayout custom code ends here.
+    }
+
+    private void forceUniformHeight(int count, int widthMeasureSpec) {
+        // Pretend that the linear layout has an exact size. This is the measured height of
+        // ourselves. The measured height should be the max height of the children, changed
+        // to accommodate the heightMeasureSpec from the parent
+        int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
+                MeasureSpec.EXACTLY);
+        for (int i = 0; i < count; ++i) {
+           final View child = getVirtualChildAt(i);
+           if (child.getVisibility() != GONE) {
+               LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+               if (lp.height == LayoutParams.MATCH_PARENT) {
+                   // Temporarily force children to reuse their old measured width
+                   // FIXME: this may not be right for something like wrapping text?
+                   int oldWidth = lp.width;
+                   lp.width = child.getMeasuredWidth();
+
+                   // Remeasure with new dimensions
+                   measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
+                   lp.width = oldWidth;
+               }
+           }
+        }
+    }
+
+    /**
+     * <p>Returns the number of children to skip after measuring/laying out
+     * the specified child.</p>
+     *
+     * @param child the child after which we want to skip children
+     * @param index the index of the child after which we want to skip children
+     * @return the number of children to skip, 0 by default
+     */
+    int getChildrenSkipCount(View child, int index) {
+        return 0;
+    }
+
+    /**
+     * <p>Returns the size (width or height) that should be occupied by a null
+     * child.</p>
+     *
+     * @param childIndex the index of the null child
+     * @return the width or height of the child depending on the orientation
+     */
+    int measureNullChild(int childIndex) {
+        return 0;
+    }
+
+    /**
+     * <p>Measure the child according to the parent's measure specs. This
+     * method should be overriden by subclasses to force the sizing of
+     * children. This method is called by {@link #measureVertical(int, int)} and
+     * {@link #measureHorizontal(int, int)}.</p>
+     *
+     * @param child the child to measure
+     * @param childIndex the index of the child in this view
+     * @param widthMeasureSpec horizontal space requirements as imposed by the parent
+     * @param totalWidth extra space that has been used up by the parent horizontally
+     * @param heightMeasureSpec vertical space requirements as imposed by the parent
+     * @param totalHeight extra space that has been used up by the parent vertically
+     */
+    void measureChildBeforeLayout(View child, int childIndex,
+            int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
+            int totalHeight) {
+        measureChildWithMargins(child, widthMeasureSpec, totalWidth,
+                heightMeasureSpec, totalHeight);
+    }
+
+    /**
+     * <p>Return the location offset of the specified child. This can be used
+     * by subclasses to change the location of a given widget.</p>
+     *
+     * @param child the child for which to obtain the location offset
+     * @return the location offset in pixels
+     */
+    int getLocationOffset(View child) {
+        return 0;
+    }
+
+    /**
+     * <p>Return the size offset of the next sibling of the specified child.
+     * This can be used by subclasses to change the location of the widget
+     * following <code>child</code>.</p>
+     *
+     * @param child the child whose next sibling will be moved
+     * @return the location offset of the next child in pixels
+     */
+    int getNextLocationOffset(View child) {
+        return 0;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        if (mOrientation == VERTICAL) {
+            layoutVertical(l, t, r, b);
+        } else {
+            layoutHorizontal(l, t, r, b);
+        }
+    }
+
+    /**
+     * Position the children during a layout pass if the orientation of this
+     * LinearLayout is set to {@link #VERTICAL}.
+     *
+     * @see #getOrientation()
+     * @see #setOrientation(int)
+     * @see #onLayout(boolean, int, int, int, int)
+     * @param left
+     * @param top
+     * @param right
+     * @param bottom
+     */
+    void layoutVertical(int left, int top, int right, int bottom) {
+        final int paddingLeft = mPaddingLeft;
+
+        int childTop;
+        int childLeft;
+
+        // Where right end of child should go
+        final int width = right - left;
+        int childRight = width - mPaddingRight;
+
+        // Space available for child
+        int childSpace = width - paddingLeft - mPaddingRight;
+
+        final int count = getVirtualChildCount();
+
+        final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
+        final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
+
+        switch (majorGravity) {
+           case Gravity.BOTTOM:
+               // mTotalLength contains the padding already
+               childTop = mPaddingTop + bottom - top - mTotalLength;
+               break;
+
+               // mTotalLength contains the padding already
+           case Gravity.CENTER_VERTICAL:
+               childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
+               break;
+
+           case Gravity.TOP:
+           default:
+               childTop = mPaddingTop;
+               break;
+        }
+
+        for (int i = 0; i < count; i++) {
+            final View child = getVirtualChildAt(i);
+            if (child == null) {
+                childTop += measureNullChild(i);
+            } else if (child.getVisibility() != GONE) {
+                final int childWidth = child.getMeasuredWidth();
+                final int childHeight = child.getMeasuredHeight();
+
+                final LayoutParams lp =
+                        (LayoutParams) child.getLayoutParams();
+
+                int gravity = lp.gravity;
+                if (gravity < 0) {
+                    gravity = minorGravity;
+                }
+                final int layoutDirection = getLayoutDirection();
+                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
+                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+                    case Gravity.CENTER_HORIZONTAL:
+                        childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+                                + lp.leftMargin - lp.rightMargin;
+                        break;
+
+                    case Gravity.RIGHT:
+                        childLeft = childRight - childWidth - lp.rightMargin;
+                        break;
+
+                    case Gravity.LEFT:
+                    default:
+                        childLeft = paddingLeft + lp.leftMargin;
+                        break;
+                }
+
+                if (hasDividerBeforeChildAt(i)) {
+                    childTop += mDividerHeight;
+                }
+
+                childTop += lp.topMargin;
+                setChildFrame(child, childLeft, childTop + getLocationOffset(child),
+                        childWidth, childHeight);
+                childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
+
+                i += getChildrenSkipCount(child, i);
+            }
+        }
+    }
+
+    @Override
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+        if (layoutDirection != mLayoutDirection) {
+            mLayoutDirection = layoutDirection;
+            if (mOrientation == HORIZONTAL) {
+                requestLayout();
+            }
+        }
+    }
+
+    /**
+     * Position the children during a layout pass if the orientation of this
+     * LinearLayout is set to {@link #HORIZONTAL}.
+     *
+     * @see #getOrientation()
+     * @see #setOrientation(int)
+     * @see #onLayout(boolean, int, int, int, int)
+     * @param left
+     * @param top
+     * @param right
+     * @param bottom
+     */
+    void layoutHorizontal(int left, int top, int right, int bottom) {
+        final boolean isLayoutRtl = isLayoutRtl();
+        final int paddingTop = mPaddingTop;
+
+        int childTop;
+        int childLeft;
+
+        // Where bottom of child should go
+        final int height = bottom - top;
+        int childBottom = height - mPaddingBottom;
+
+        // Space available for child
+        int childSpace = height - paddingTop - mPaddingBottom;
+
+        final int count = getVirtualChildCount();
+
+        final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
+        final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+        final boolean baselineAligned = mBaselineAligned;
+
+        final int[] maxAscent = mMaxAscent;
+        final int[] maxDescent = mMaxDescent;
+
+        final int layoutDirection = getLayoutDirection();
+        switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
+            case Gravity.RIGHT:
+                // mTotalLength contains the padding already
+                childLeft = mPaddingLeft + right - left - mTotalLength;
+                break;
+
+            case Gravity.CENTER_HORIZONTAL:
+                // mTotalLength contains the padding already
+                childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
+                break;
+
+            case Gravity.LEFT:
+            default:
+                childLeft = mPaddingLeft;
+                break;
+        }
+
+        int start = 0;
+        int dir = 1;
+        //In case of RTL, start drawing from the last child.
+        if (isLayoutRtl) {
+            start = count - 1;
+            dir = -1;
+        }
+
+        for (int i = 0; i < count; i++) {
+            int childIndex = start + dir * i;
+            final View child = getVirtualChildAt(childIndex);
+
+            if (child == null) {
+                childLeft += measureNullChild(childIndex);
+            } else if (child.getVisibility() != GONE) {
+                final int childWidth = child.getMeasuredWidth();
+                final int childHeight = child.getMeasuredHeight();
+                int childBaseline = -1;
+
+                final LayoutParams lp =
+                        (LayoutParams) child.getLayoutParams();
+
+                if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
+                    childBaseline = child.getBaseline();
+                }
+
+                int gravity = lp.gravity;
+                if (gravity < 0) {
+                    gravity = minorGravity;
+                }
+
+                switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
+                    case Gravity.TOP:
+                        childTop = paddingTop + lp.topMargin;
+                        if (childBaseline != -1) {
+                            childTop += maxAscent[INDEX_TOP] - childBaseline;
+                        }
+                        break;
+
+                    case Gravity.CENTER_VERTICAL:
+                        // Removed support for baseline alignment when layout_gravity or
+                        // gravity == center_vertical. See bug #1038483.
+                        // Keep the code around if we need to re-enable this feature
+                        // if (childBaseline != -1) {
+                        //     // Align baselines vertically only if the child is smaller than us
+                        //     if (childSpace - childHeight > 0) {
+                        //         childTop = paddingTop + (childSpace / 2) - childBaseline;
+                        //     } else {
+                        //         childTop = paddingTop + (childSpace - childHeight) / 2;
+                        //     }
+                        // } else {
+                        childTop = paddingTop + ((childSpace - childHeight) / 2)
+                                + lp.topMargin - lp.bottomMargin;
+                        break;
+
+                    case Gravity.BOTTOM:
+                        childTop = childBottom - childHeight - lp.bottomMargin;
+                        if (childBaseline != -1) {
+                            int descent = child.getMeasuredHeight() - childBaseline;
+                            childTop -= (maxDescent[INDEX_BOTTOM] - descent);
+                        }
+                        break;
+                    default:
+                        childTop = paddingTop;
+                        break;
+                }
+
+                if (hasDividerBeforeChildAt(childIndex)) {
+                    childLeft += mDividerWidth;
+                }
+
+                childLeft += lp.leftMargin;
+                setChildFrame(child, childLeft + getLocationOffset(child), childTop,
+                        childWidth, childHeight);
+                childLeft += childWidth + lp.rightMargin +
+                        getNextLocationOffset(child);
+
+                i += getChildrenSkipCount(child, childIndex);
+            }
+        }
+    }
+
+    private void setChildFrame(View child, int left, int top, int width, int height) {
+        child.layout(left, top, left + width, top + height);
+    }
+
+    /**
+     * Should the layout be a column or a row.
+     * @param orientation Pass {@link #HORIZONTAL} or {@link #VERTICAL}. Default
+     * value is {@link #HORIZONTAL}.
+     *
+     * @attr ref android.R.styleable#LinearLayout_orientation
+     */
+    public void setOrientation(@OrientationMode int orientation) {
+        if (mOrientation != orientation) {
+            mOrientation = orientation;
+            requestLayout();
+        }
+    }
+
+    /**
+     * Returns the current orientation.
+     *
+     * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
+     */
+    @OrientationMode
+    public int getOrientation() {
+        return mOrientation;
+    }
+
+    /**
+     * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If
+     * this layout has a VERTICAL orientation, this controls where all the child
+     * views are placed if there is extra vertical space. If this layout has a
+     * HORIZONTAL orientation, this controls the alignment of the children.
+     *
+     * @param gravity See {@link android.view.Gravity}
+     *
+     * @attr ref android.R.styleable#LinearLayout_gravity
+     */
+    @android.view.RemotableViewMethod
+    public void setGravity(int gravity) {
+        if (mGravity != gravity) {
+            if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
+                gravity |= Gravity.START;
+            }
+
+            if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
+                gravity |= Gravity.TOP;
+            }
+
+            mGravity = gravity;
+            requestLayout();
+        }
+    }
+
+    @android.view.RemotableViewMethod
+    public void setHorizontalGravity(int horizontalGravity) {
+        final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
+        if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
+            mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
+            requestLayout();
+        }
+    }
+
+    @android.view.RemotableViewMethod
+    public void setVerticalGravity(int verticalGravity) {
+        final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
+        if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
+            mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
+            requestLayout();
+        }
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
+    }
+
+    /**
+     * Returns a set of layout parameters with a width of
+     * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
+     * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
+     * when the layout's orientation is {@link #VERTICAL}. When the orientation is
+     * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
+     * and the height to {@link LayoutParams#WRAP_CONTENT}.
+     */
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        if (mOrientation == HORIZONTAL) {
+            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+        } else if (mOrientation == VERTICAL) {
+            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+        }
+        return null;
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return new LayoutParams(p);
+    }
+
+
+    // Override to allow type-checking of LayoutParams.
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams;
+    }
+
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return MatchParentShrinkingLinearLayout.class.getName();
+    }
+
+    /** @hide */
+    @Override
+    protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+        super.encodeProperties(encoder);
+        encoder.addProperty("layout:baselineAligned", mBaselineAligned);
+        encoder.addProperty("layout:baselineAlignedChildIndex", mBaselineAlignedChildIndex);
+        encoder.addProperty("measurement:baselineChildTop", mBaselineChildTop);
+        encoder.addProperty("measurement:orientation", mOrientation);
+        encoder.addProperty("measurement:gravity", mGravity);
+        encoder.addProperty("measurement:totalLength", mTotalLength);
+        encoder.addProperty("layout:totalLength", mTotalLength);
+        encoder.addProperty("layout:useLargestChild", mUseLargestChild);
+    }
+
+    /**
+     * Per-child layout information associated with ViewLinearLayout.
+     *
+     * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight
+     * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity
+     */
+    public static class LayoutParams extends MarginLayoutParams {
+        /**
+         * Indicates how much of the extra space in the LinearLayout will be
+         * allocated to the view associated with these LayoutParams. Specify
+         * 0 if the view should not be stretched. Otherwise the extra pixels
+         * will be pro-rated among all views whose weight is greater than 0.
+         */
+        @ViewDebug.ExportedProperty(category = "layout")
+        public float weight;
+
+        /**
+         * Gravity for the view associated with these LayoutParams.
+         *
+         * @see android.view.Gravity
+         */
+        @ViewDebug.ExportedProperty(category = "layout", mapping = {
+            @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
+            @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
+            @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
+            @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
+            @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
+            @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
+            @ViewDebug.IntToString(from = Gravity.START,            to = "START"),
+            @ViewDebug.IntToString(from = Gravity.END,             to = "END"),
+            @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
+            @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
+            @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
+            @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
+            @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
+            @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
+        })
+        public int gravity = -1;
+
+        /**
+         * {@inheritDoc}
+         */
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            TypedArray a = c.obtainStyledAttributes(
+                    attrs, com.android.internal.R.styleable.LinearLayout_Layout);
+
+            weight = a.getFloat(
+                    com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
+            gravity = a.getInt(
+                    com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
+
+            a.recycle();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public LayoutParams(int width, int height) {
+            super(width, height);
+            weight = 0;
+        }
+
+        /**
+         * Creates a new set of layout parameters with the specified width, height
+         * and weight.
+         *
+         * @param width the width, either {@link #MATCH_PARENT},
+         *        {@link #WRAP_CONTENT} or a fixed size in pixels
+         * @param height the height, either {@link #MATCH_PARENT},
+         *        {@link #WRAP_CONTENT} or a fixed size in pixels
+         * @param weight the weight
+         */
+        public LayoutParams(int width, int height, float weight) {
+            super(width, height);
+            this.weight = weight;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public LayoutParams(ViewGroup.LayoutParams p) {
+            super(p);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public LayoutParams(MarginLayoutParams source) {
+            super(source);
+        }
+
+        /**
+         * Copy constructor. Clones the width, height, margin values, weight,
+         * and gravity of the source.
+         *
+         * @param source The layout params to copy from.
+         */
+        public LayoutParams(LayoutParams source) {
+            super(source);
+
+            this.weight = source.weight;
+            this.gravity = source.gravity;
+        }
+
+        @Override
+        public String debug(String output) {
+            return output + "MatchParentShrinkingLinearLayout.LayoutParams={width="
+                    + sizeToString(width) + ", height=" + sizeToString(height)
+                    + " weight=" + weight +  "}";
+        }
+
+        /** @hide */
+        @Override
+        protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+            super.encodeProperties(encoder);
+
+            encoder.addProperty("layout:weight", weight);
+            encoder.addProperty("layout:gravity", gravity);
+        }
+    }
+}