OSDN Git Service

am 31d519fe: (-s ours) am 66b5a58a: Merge "Fix init order so we have something to...
authorJeff Sharkey <jsharkey@android.com>
Thu, 27 Aug 2015 11:30:29 +0000 (11:30 +0000)
committerAndroid Git Automerger <android-git-automerger@android.com>
Thu, 27 Aug 2015 11:30:29 +0000 (11:30 +0000)
* commit '31d519fe37f6132f01b6193fec91d9f9b5b97908':

182 files changed:
AndroidManifest.xml
res/color/fingerprint_indicator_background_activated.xml [new file with mode: 0644]
res/drawable-hdpi/setup_illustration_lock_screen.jpg
res/drawable-mdpi/nfc_how_it_works.png
res/drawable-mdpi/setup_illustration_lock_screen.jpg
res/drawable-sw600dp-hdpi/setup_illustration_lock_screen.jpg
res/drawable-sw600dp-mdpi/setup_illustration_lock_screen.jpg
res/drawable-sw600dp-xhdpi/setup_illustration_lock_screen.jpg
res/drawable-sw600dp-xxhdpi/setup_illustration_lock_screen.jpg
res/drawable-sw600dp-xxxhdpi/setup_illustration_lock_screen.jpg
res/drawable-xhdpi/nfc_how_it_works.png
res/drawable-xhdpi/setup_illustration_lock_screen.jpg
res/drawable-xxhdpi/nfc_how_it_works.png
res/drawable-xxhdpi/setup_illustration_lock_screen.jpg
res/drawable-xxxhdpi/nfc_how_it_works.png
res/drawable-xxxhdpi/setup_illustration_lock_screen.jpg
res/drawable/fingerprint_progress_ring.xml
res/layout-land/fingerprint_enroll_enrolling.xml
res/layout-land/fingerprint_enroll_find_sensor.xml
res/layout-land/fingerprint_enroll_finish.xml
res/layout-sw600dp/nfc_payment_how_it_works.xml [new file with mode: 0644]
res/layout/app_item.xml
res/layout/bluetooth_pin_confirm.xml
res/layout/choose_lock_generic_fingerprint_header.xml [new file with mode: 0644]
res/layout/device_profiles_settings.xml
res/layout/empty_printers_list_service_enabled.xml
res/layout/fingerprint_enroll_enrolling_base.xml
res/layout/fingerprint_enroll_enrolling_content.xml
res/layout/fingerprint_enroll_find_sensor_base.xml
res/layout/fingerprint_enroll_finish_base.xml
res/layout/fingerprint_enroll_introduction.xml
res/layout/fingerprint_enroll_onboard.xml
res/layout/fingerprint_rename_dialog.xml
res/layout/ignore_optimizations_content.xml
res/layout/master_clear.xml
res/layout/master_clear_confirm.xml
res/layout/media_format_final.xml [deleted file]
res/layout/media_format_primary.xml [deleted file]
res/layout/nfc_payment_empty.xml
res/layout/nfc_payment_how_it_works.xml
res/layout/radio_with_summary.xml
res/layout/setup_encryption_interstitial.xml [new file with mode: 0644]
res/layout/setup_redaction_interstitial.xml
res/layout/sim_information.xml [deleted file]
res/layout/usb_dialog_container.xml [new file with mode: 0644]
res/layout/widget_text_views.xml
res/values-sw400dp/dimens.xml [new file with mode: 0755]
res/values/arrays.xml
res/values/colors.xml
res/values/dimens.xml
res/values/strings.xml
res/values/styles.xml
res/values/themes.xml
res/xml/advanced_apps.xml
res/xml/app_ops_permissions_details.xml [moved from res/xml/usage_access_details.xml with 75% similarity]
res/xml/app_storage_settings.xml
res/xml/development_prefs.xml
res/xml/device_info_settings.xml
res/xml/display_settings.xml
res/xml/installed_app_details.xml
res/xml/installed_app_launch_settings.xml
res/xml/notification_settings.xml
res/xml/other_sound_settings.xml
res/xml/zen_mode_priority_settings.xml
src/com/android/settings/AccessiblePreferenceCategory.java [new file with mode: 0644]
src/com/android/settings/ApnEditor.java
src/com/android/settings/ChooseLockGeneric.java
src/com/android/settings/ChooseLockPassword.java
src/com/android/settings/ChooseLockPattern.java
src/com/android/settings/ChooseLockSettingsHelper.java
src/com/android/settings/ConfirmDeviceCredentialActivity.java
src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java
src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java
src/com/android/settings/ConfirmLockPassword.java
src/com/android/settings/ConfirmLockPattern.java
src/com/android/settings/CredentialStorage.java
src/com/android/settings/DataUsageSummary.java
src/com/android/settings/DevelopmentSettings.java
src/com/android/settings/DeviceInfoSettings.java
src/com/android/settings/DisplaySettings.java
src/com/android/settings/EncryptionInterstitial.java
src/com/android/settings/InstrumentedActivity.java [new file with mode: 0644]
src/com/android/settings/InstrumentedFragment.java
src/com/android/settings/LegalSettings.java
src/com/android/settings/MasterClear.java
src/com/android/settings/MasterClearConfirm.java
src/com/android/settings/MediaFormat.java [deleted file]
src/com/android/settings/ResetNetworkConfirm.java
src/com/android/settings/SecuritySettings.java
src/com/android/settings/Settings.java
src/com/android/settings/SettingsActivity.java
src/com/android/settings/SettingsPreferenceFragment.java
src/com/android/settings/SetupChooseLockGeneric.java
src/com/android/settings/SetupEncryptionInterstitial.java
src/com/android/settings/TrustAgentSettings.java
src/com/android/settings/UserAdapter.java
src/com/android/settings/Utils.java
src/com/android/settings/WifiCallingSettings.java
src/com/android/settings/WirelessSettings.java
src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
src/com/android/settings/accounts/AccountSettings.java
src/com/android/settings/accounts/AccountSyncSettings.java
src/com/android/settings/applications/AdvancedAppSettings.java
src/com/android/settings/applications/AppDomainsPreference.java
src/com/android/settings/applications/AppLaunchSettings.java
src/com/android/settings/applications/AppStateAppOpsBridge.java [new file with mode: 0644]
src/com/android/settings/applications/AppStateOverlayBridge.java [new file with mode: 0644]
src/com/android/settings/applications/AppStatePowerBridge.java
src/com/android/settings/applications/AppStateUsageBridge.java
src/com/android/settings/applications/AppStateWriteSettingsBridge.java [new file with mode: 0644]
src/com/android/settings/applications/AppStorageSettings.java
src/com/android/settings/applications/DrawOverlayDetails.java [new file with mode: 0644]
src/com/android/settings/applications/InstalledAppDetails.java
src/com/android/settings/applications/ManageApplications.java
src/com/android/settings/applications/ManageAssist.java
src/com/android/settings/applications/ResetAppsHelper.java
src/com/android/settings/applications/RunningState.java
src/com/android/settings/applications/UsageAccessDetails.java
src/com/android/settings/applications/WriteSettingsDetails.java [new file with mode: 0644]
src/com/android/settings/bluetooth/BluetoothPairingDialog.java
src/com/android/settings/dashboard/SearchResultsSummary.java
src/com/android/settings/deviceinfo/PrivateVolumeSettings.java
src/com/android/settings/deviceinfo/PublicVolumeSettings.java
src/com/android/settings/deviceinfo/SimStatus.java
src/com/android/settings/deviceinfo/StorageItemPreference.java
src/com/android/settings/deviceinfo/StorageWizardBase.java
src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java
src/com/android/settings/deviceinfo/StorageWizardInit.java
src/com/android/settings/deviceinfo/UsbBackend.java [new file with mode: 0644]
src/com/android/settings/deviceinfo/UsbModeChooserActivity.java
src/com/android/settings/drawable/CircleFramedDrawable.java [deleted file]
src/com/android/settings/fingerprint/FingerprintEnrollBase.java
src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
src/com/android/settings/fingerprint/FingerprintEnrollFindSensor.java
src/com/android/settings/fingerprint/FingerprintEnrollFinish.java
src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
src/com/android/settings/fingerprint/FingerprintEnrollOnboard.java
src/com/android/settings/fingerprint/FingerprintEnrollSidecar.java
src/com/android/settings/fingerprint/FingerprintSettings.java
src/com/android/settings/fingerprint/SetupFingerprintEnrollEnrolling.java [new file with mode: 0644]
src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensor.java [new file with mode: 0644]
src/com/android/settings/fingerprint/SetupFingerprintEnrollFinish.java [new file with mode: 0644]
src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java [new file with mode: 0644]
src/com/android/settings/fingerprint/SetupFingerprintEnrollOnboard.java [new file with mode: 0644]
src/com/android/settings/fuelgauge/BatteryEntry.java
src/com/android/settings/fuelgauge/HighPowerDetail.java
src/com/android/settings/fuelgauge/InactiveApps.java
src/com/android/settings/fuelgauge/PowerUsageDetail.java
src/com/android/settings/fuelgauge/PowerUsageSummary.java
src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java [new file with mode: 0644]
src/com/android/settings/net/ChartData.java [deleted file]
src/com/android/settings/net/ChartDataLoader.java [deleted file]
src/com/android/settings/net/DataUsageMeteredSettings.java
src/com/android/settings/net/NetworkPolicyEditor.java [deleted file]
src/com/android/settings/net/SummaryForAllUidLoader.java [deleted file]
src/com/android/settings/net/UidDetail.java [deleted file]
src/com/android/settings/net/UidDetailProvider.java [deleted file]
src/com/android/settings/nfc/NfcForegroundPreference.java
src/com/android/settings/nfc/NfcPaymentPreference.java
src/com/android/settings/notification/AppNotificationSettings.java
src/com/android/settings/notification/NotificationSettings.java
src/com/android/settings/notification/RedactionInterstitial.java
src/com/android/settings/notification/ZenModeRuleSettingsBase.java
src/com/android/settings/users/EditUserInfoController.java
src/com/android/settings/users/EditUserPhotoController.java
src/com/android/settings/users/RestrictedProfileSettings.java
src/com/android/settings/users/UserSettings.java
src/com/android/settings/utils/VoiceSettingsActivity.java
src/com/android/settings/voice/VoiceInputListPreference.java
src/com/android/settings/vpn2/ConfigDialog.java
src/com/android/settings/vpn2/ConfigDialogFragment.java
src/com/android/settings/vpn2/VpnSettings.java
src/com/android/settings/widget/SettingsAppWidgetProvider.java
src/com/android/settings/wifi/AccessPointPreference.java
src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
src/com/android/settings/wifi/WifiApDialog.java
src/com/android/settings/wifi/WifiApEnabler.java
src/com/android/settings/wifi/WifiConfigController.java
src/com/android/settings/wifi/WifiDialog.java
src/com/android/settings/wifi/WifiSettings.java
tests/src/com/android/settings/bluetooth/Utf8ByteLengthFilterTest.java
tests/src/com/android/settings/vpn2/VpnTests.java

index ecdcfc8..f199673 100644 (file)
@@ -9,6 +9,7 @@
 
     <original-package android:name="com.android.settings" />
 
+    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
@@ -80,6 +81,7 @@
     <uses-permission android:name="android.permission.MANAGE_FINGERPRINT" />
     <uses-permission android:name="android.permission.USER_ACTIVITY" />
     <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
+    <uses-permission android:name="android.permission.PEERS_MAC_ADDRESS"/>
 
     <application android:label="@string/settings_label"
             android:icon="@mipmap/ic_launcher_settings"
             <intent-filter android:priority="1">
                 <action android:name="android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="package" />
-            </intent-filter>
-            <intent-filter android:priority="1">
-                <action android:name="android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS" />
-                <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.applications.ManageApplications" />
                 android:value="true" />
         </activity>
 
+        <activity android:name=".fuelgauge.RequestIgnoreBatteryOptimizations"
+                android:label="@string/high_power_apps"
+                android:taskAffinity=""
+                android:theme="@*android:style/Theme.Material.Light.Dialog.Alert">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="package" />
+            </intent-filter>
+        </activity>
+
         <!-- Keep compatibility with old shortcuts. -->
         <activity-alias android:name=".applications.ManageApplications"
                 android:label="@string/applications_settings"
         <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"/>
+        <activity android:name=".fingerprint.FingerprintEnrollIntroduction" android:exported="false" />
+
+        <activity android:name=".fingerprint.SetupFingerprintEnrollOnboard" android:exported="false"/>
+        <activity android:name=".fingerprint.SetupFingerprintEnrollFindSensor" android:exported="false"/>
+        <activity android:name=".fingerprint.SetupFingerprintEnrollEnrolling" android:exported="false"/>
+        <activity android:name=".fingerprint.SetupFingerprintEnrollFinish" android:exported="false"/>
+        <activity android:name=".fingerprint.SetupFingerprintEnrollIntroduction"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_FINGERPRINT"
+            android:theme="@style/SetupWizardDisableAppStartingTheme">
+            <intent-filter>
+                <action android:name="android.settings.FINGERPRINT_SETUP" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
 
         <!-- Note this must not be exported since it returns the password in the intent -->
         <activity android:name="ConfirmLockPattern$InternalActivity"
             </intent-filter>
         </activity>
 
-        <activity android:name="MediaFormat" android:label="@string/media_format_title">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-            </intent-filter>
-        </activity>
-
         <activity android:name="Settings$DevelopmentSettingsActivity"
                 android:label="@string/development_settings_title"
                 android:taskAffinity="com.android.settings"
                   android:excludeFromRecents="true"
                   android:exported="true"
                   android:permission="android.permission.MANAGE_USB"
-                  android:theme="@style/Transparent">
+                  android:theme="@android:style/Theme.Material.Light.Dialog.NoActionBar">
         </activity>
 
         <activity android:name=".bluetooth.RequestPermissionHelperActivity"
             </intent-filter>
         </provider>
 
+        <activity android:name="Settings$OverlaySettingsActivity"
+                android:label="@string/draw_overlay_title"
+                android:taskAffinity="">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.action.MANAGE_OVERLAY_PERMISSION" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.applications.ManageApplications" />
+        </activity>
+
+        <activity android:name="Settings$AppDrawOverlaySettingsActivity"
+                android:label="@string/draw_overlay_title"
+                android:taskAffinity="">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.action.MANAGE_OVERLAY_PERMISSION" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="package" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.applications.DrawOverlayDetails" />
+        </activity>
+
+        <activity android:name="Settings$WriteSettingsActivity"
+                android:label="@string/write_settings_title"
+                android:taskAffinity="">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.action.MANAGE_WRITE_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.applications.ManageApplications" />
+        </activity>
+
+        <activity android:name="Settings$AppWriteSettingsActivity"
+                android:label="@string/write_settings_title"
+                android:taskAffinity="">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.action.MANAGE_WRITE_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="package" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.applications.WriteSettingsDetails" />
+        </activity>
+
     </application>
 </manifest>
diff --git a/res/color/fingerprint_indicator_background_activated.xml b/res/color/fingerprint_indicator_background_activated.xml
new file mode 100644 (file)
index 0000000..32f5383
--- /dev/null
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="0.5" android:color="?android:attr/colorAccent" />
+</selector>
index 0bf53fc..263d33e 100644 (file)
Binary files a/res/drawable-hdpi/setup_illustration_lock_screen.jpg and b/res/drawable-hdpi/setup_illustration_lock_screen.jpg differ
index ef3cd8c..1dc272c 100644 (file)
Binary files a/res/drawable-mdpi/nfc_how_it_works.png and b/res/drawable-mdpi/nfc_how_it_works.png differ
index 9ab9117..004fa9e 100644 (file)
Binary files a/res/drawable-mdpi/setup_illustration_lock_screen.jpg and b/res/drawable-mdpi/setup_illustration_lock_screen.jpg differ
index 26cff1d..933e986 100644 (file)
Binary files a/res/drawable-sw600dp-hdpi/setup_illustration_lock_screen.jpg and b/res/drawable-sw600dp-hdpi/setup_illustration_lock_screen.jpg differ
index 386fc8d..57e0385 100644 (file)
Binary files a/res/drawable-sw600dp-mdpi/setup_illustration_lock_screen.jpg and b/res/drawable-sw600dp-mdpi/setup_illustration_lock_screen.jpg differ
index 54362e7..e329a96 100644 (file)
Binary files a/res/drawable-sw600dp-xhdpi/setup_illustration_lock_screen.jpg and b/res/drawable-sw600dp-xhdpi/setup_illustration_lock_screen.jpg differ
index 4f62b84..7934e8a 100644 (file)
Binary files a/res/drawable-sw600dp-xxhdpi/setup_illustration_lock_screen.jpg and b/res/drawable-sw600dp-xxhdpi/setup_illustration_lock_screen.jpg differ
index 2c6b57e..ab61328 100644 (file)
Binary files a/res/drawable-sw600dp-xxxhdpi/setup_illustration_lock_screen.jpg and b/res/drawable-sw600dp-xxxhdpi/setup_illustration_lock_screen.jpg differ
index 874d32f..e61d77f 100644 (file)
Binary files a/res/drawable-xhdpi/nfc_how_it_works.png and b/res/drawable-xhdpi/nfc_how_it_works.png differ
index ddad6d5..b78a59d 100644 (file)
Binary files a/res/drawable-xhdpi/setup_illustration_lock_screen.jpg and b/res/drawable-xhdpi/setup_illustration_lock_screen.jpg differ
index 748ad6a..0c816bd 100644 (file)
Binary files a/res/drawable-xxhdpi/nfc_how_it_works.png and b/res/drawable-xxhdpi/nfc_how_it_works.png differ
index 5b0a7a7..875e55e 100644 (file)
Binary files a/res/drawable-xxhdpi/setup_illustration_lock_screen.jpg and b/res/drawable-xxhdpi/setup_illustration_lock_screen.jpg differ
index d970dae..0add50f 100644 (file)
Binary files a/res/drawable-xxxhdpi/nfc_how_it_works.png and b/res/drawable-xxxhdpi/nfc_how_it_works.png differ
index 5be34fd..8ac85a4 100644 (file)
Binary files a/res/drawable-xxxhdpi/setup_illustration_lock_screen.jpg and b/res/drawable-xxxhdpi/setup_illustration_lock_screen.jpg differ
index 04578cd..44a69e9 100644 (file)
@@ -17,7 +17,7 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:id="@android:id/background">
         <shape
-            android:innerRadius="@dimen/fingerprint_ring_radius"
+            android:innerRadius="?attr/fingerprint_ring_radius"
             android:shape="ring"
             android:thickness="@dimen/fingerprint_ring_thickness"
             android:useLevel="false">
@@ -26,7 +26,7 @@
     </item>
     <item android:id="@android:id/progress">
         <shape
-            android:innerRadius="@dimen/fingerprint_ring_radius"
+            android:innerRadius="?attr/fingerprint_ring_radius"
             android:shape="ring"
             android:thickness="@dimen/fingerprint_ring_thickness">
             <gradient
index 4d02c07..c72c029 100644 (file)
@@ -20,7 +20,7 @@
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    style="@style/SetupWizardFingerprintStyle">
+    style="?attr/fingerprint_layout_theme">
 
     <LinearLayout
         style="@style/SuwContentFrame"
             android:layout_weight="1"
             android:layout_height="match_parent">
 
-            <include layout="@layout/fingerprint_enroll_enrolling_content"/>
+            <include layout="@layout/fingerprint_enroll_enrolling_content"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"/>
 
             <TextView
                 style="@style/TextAppearance.FingerprintErrorText"
index b340193..81d1dac 100644 (file)
@@ -20,7 +20,7 @@
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    style="@style/SetupWizardFingerprintStyle">
+    style="?attr/fingerprint_layout_theme">
 
     <LinearLayout
         style="@style/SuwContentFrame"
index aeadd5d..e051f15 100644 (file)
@@ -20,7 +20,7 @@
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    style="@style/SetupWizardFingerprintStyle">
+    style="?attr/fingerprint_layout_theme">
 
     <LinearLayout
         style="@style/SuwContentFrame"
             android:orientation="vertical">
 
             <TextView
+                android:id="@+id/message"
                 style="@style/TextAppearance.FingerprintMessage"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:text="@string/security_settings_fingerprint_enroll_finish_message"/>
 
+            <TextView
+                android:id="@+id/message_secondary"
+                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/setup_fingerprint_enroll_finish_message_secondary"
+                android:textColor="?android:attr/textColorSecondary"
+                android:visibility="gone" />
+
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
 
             <ImageView
                 android:id="@+id/fingerprint_in_app_indicator"
-                android:layout_width="120dp"
-                android:layout_height="120dp"
+                android:layout_width="@dimen/fingerprint_in_app_indicator_size"
+                android:layout_height="@dimen/fingerprint_in_app_indicator_size"
                 android:layout_gravity="center_horizontal"
+                android:contentDescription="@android:string/fingerprint_icon_content_description"
                 android:src="@drawable/fp_app_indicator" />
 
         </FrameLayout>
diff --git a/res/layout-sw600dp/nfc_payment_how_it_works.xml b/res/layout-sw600dp/nfc_payment_how_it_works.xml
new file mode 100644 (file)
index 0000000..3bee160
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/trusted_devices_prompt_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#FF80CBC4" >
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="336dp"
+            android:background="#FFB2DFDB" >
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/nfc_how_it_works_image_text"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:paddingLeft="16dp"
+            android:paddingRight="16dp"
+            android:gravity="center_horizontal"
+            android:paddingTop="144dp"
+            android:orientation="vertical">
+
+          <ImageView
+              android:id="@+id/nfc_how_it_works_image"
+              android:layout_width="match_parent"
+              android:layout_height="282dp"
+              android:gravity="center"
+              android:src="@drawable/nfc_how_it_works" />
+
+          <TextView
+              android:id="@+id/nfc_how_it_works_title"
+              android:layout_width="420dp"
+              android:layout_height="wrap_content"
+              android:text="@string/nfc_how_it_works_title"
+              android:textColor="#FF263238"
+              android:textSize="24sp" />
+
+          <TextView
+              android:id="@+id/nfc_how_it_works_content"
+              android:layout_width="420dp"
+              android:layout_height="wrap_content"
+              android:paddingTop="10dp"
+              android:paddingBottom="18dp"
+              android:text="@string/nfc_how_it_works_content"
+              android:textColor="#FF263238"
+              android:textSize="20sp" />
+        </LinearLayout>
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="48dp"
+            android:layout_alignParentBottom="true"
+            android:background="#FFB2DFDB" >
+          <Button
+              android:id="@+id/nfc_how_it_works_button"
+              android:background="?android:attr/selectableItemBackground"
+              android:layout_alignParentRight="true"
+              android:layout_width="wrap_content"
+              android:layout_height="48dp"
+              android:layout_marginRight="20dp"
+              android:text="@string/nfc_how_it_works_got_it"
+              android:textSize="14sp"
+              android:textColor="#263238"
+              style="?android:attr/borderlessButtonStyle" />
+        </RelativeLayout>
+    </RelativeLayout>
+</FrameLayout>
index c5a73af..ea0008e 100644 (file)
     android:paddingTop="16dp"
     android:paddingBottom="16dp"
     android:gravity="top"
-    android:columnCount="3">
+    android:columnCount="3"
+    android:duplicateParentState="true">
 
     <ImageView
         android:id="@android:id/icon"
-        android:layout_width="@dimen/app_icon_size"
-        android:layout_height="@dimen/app_icon_size"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
         android:layout_gravity="center"
         android:scaleType="fitXY"
         android:layout_marginEnd="16dip"
-        android:contentDescription="@null" />
+        android:contentDescription="@null"
+        android:duplicateParentState="true" />
 
     <TextView
         android:id="@android:id/title"
@@ -41,7 +43,8 @@
         android:ellipsize="marquee"
         android:textAppearance="@android:style/TextAppearance.Material.Subhead"
         android:textColor="?android:attr/textColorPrimary"
-        android:textAlignment="viewStart" />
+        android:textAlignment="viewStart"
+        android:duplicateParentState="true" />
 
     <TextView
         android:id="@android:id/summary"
@@ -54,7 +57,8 @@
         android:singleLine="true"
         android:ellipsize="marquee"
         android:textAppearance="@android:style/TextAppearance.Material.Subhead"
-        android:textColor="?android:attr/textColorSecondary" />
+        android:textColor="?android:attr/textColorSecondary"
+        android:duplicateParentState="true" />
 
     <FrameLayout
         android:id="@android:id/widget_frame"
@@ -63,6 +67,7 @@
         android:layout_toEndOf="@android:id/icon"
         android:layout_below="@android:id/title"
         android:layout_alignParentEnd="true"
-        android:layout_gravity="fill_horizontal|top" />
+        android:layout_gravity="fill_horizontal|top"
+        android:duplicateParentState="true" />
 
 </RelativeLayout>
index 2968b38..ebdf65d 100644 (file)
         android:orientation="vertical">
 
         <TextView
-            android:id="@+id/message_caption"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/bluetooth_dialog_padding"
-            android:layout_marginEnd="@dimen/bluetooth_dialog_padding"
-            android:layout_marginTop="@dimen/bluetooth_dialog_padding"
-            android:gravity="center_vertical"
-            android:textAppearance="@android:style/TextAppearance.Material.Body1"
-            android:textColor="@*android:color/secondary_text_material_light"  />
-
-        <TextView
-            android:id="@+id/message_subhead"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/bluetooth_dialog_padding"
-            android:layout_marginEnd="@dimen/bluetooth_dialog_padding"
-            android:layout_marginBottom="@dimen/bluetooth_dialog_padding"
-            android:gravity="center_vertical"
-            android:textAppearance="@android:style/TextAppearance.Material.Subhead" />
-
-        <TextView
             android:id="@+id/pairing_caption"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/bluetooth_dialog_padding"
             android:layout_marginStart="@dimen/bluetooth_dialog_padding"
             android:layout_marginEnd="@dimen/bluetooth_dialog_padding"
             android:gravity="center_vertical"
@@ -92,9 +72,8 @@
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/bluetooth_dialog_padding"
             android:layout_marginEnd="@dimen/bluetooth_dialog_padding"
-            android:gravity="center_vertical"
-            android:text="@string/bluetooth_pairing_shares_phonebook"
-            android:textAppearance="?android:attr/textAppearanceSmall" />
+            android:textAppearance="@android:style/TextAppearance.Material.Body1"
+            android:textColor="@*android:color/secondary_text_material_light"  />
 
     </LinearLayout>
 
diff --git a/res/layout/choose_lock_generic_fingerprint_header.xml b/res/layout/choose_lock_generic_fingerprint_header.xml
new file mode 100644 (file)
index 0000000..5ad3004
--- /dev/null
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:minHeight="56dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:text="@string/lock_settings_picker_fingerprint_message"
+    android:textAppearance="@android:style/TextAppearance.Material.Subhead" />
index 87b8500..bd78bea 100644 (file)
@@ -39,6 +39,7 @@
         android:maxLength="@integer/bluetooth_name_length"
         android:singleLine="true"
         android:paddingBottom="@dimen/bluetooth_dialog_padding"
+        android:textDirection="locale"
         style="@style/bt_item_edit_content" />
 
     <TextView
index 6928b36..e420d5f 100644 (file)
@@ -38,6 +38,9 @@
         <TextView
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_marginLeft="32dip"
+            android:layout_marginRight="32dip"
+            android:gravity="center"
             android:textAppearance="?android:attr/textAppearanceLarge"
             android:textColor="?android:attr/textColorSecondary"
             android:text="@string/print_searching_for_printers">
index 21221cf..c93de4e 100644 (file)
@@ -20,7 +20,7 @@
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    style="@style/SetupWizardFingerprintStyle">
+    style="?attr/fingerprint_layout_theme">
 
     <LinearLayout
         style="@style/SuwContentFrame"
@@ -57,8 +57,8 @@
         <include layout="@layout/fingerprint_enroll_enrolling_content"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginTop="36dp"
-            android:layout_gravity="center_horizontal"/>
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="@dimen/fingerprint_enrolling_content_margin_top"/>
 
         <TextView
             style="@style/TextAppearance.FingerprintErrorText"
@@ -67,6 +67,7 @@
             android:layout_height="wrap_content"
             android:layout_marginTop="24dp"
             android:layout_gravity="center_horizontal"
+            android:accessibilityLiveRegion="polite"
             android:visibility="invisible"/>
 
     </LinearLayout>
index b3171a3..4b36b23 100644 (file)
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_marginTop="36dp"
     android:layout_gravity="center_horizontal">
 
     <ImageView
         android:id="@+id/fingerprint_animator"
-        android:layout_width="88dp"
-        android:layout_height="88dp"
+        android:layout_width="@dimen/fingerprint_animation_size"
+        android:layout_height="@dimen/fingerprint_animation_size"
         android:layout_centerInParent="true"
         android:background="@drawable/fp_illustration_enrollment"
         android:backgroundTint="@color/fingerprint_indicator_background_resting"
@@ -33,8 +32,8 @@
 
     <ProgressBar
         android:id="@+id/fingerprint_progress_bar"
-        android:layout_width="192dp"
-        android:layout_height="192dp"
+        android:layout_width="?attr/fingerprint_progress_bar_size"
+        android:layout_height="?attr/fingerprint_progress_bar_size"
         android:layout_centerInParent="true"
         style="?android:attr/progressBarStyleHorizontal"
         android:max="10000"
index 9f1eb4b..f65e932 100644 (file)
@@ -20,7 +20,7 @@
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    style="@style/SetupWizardFingerprintStyle">
+    style="?attr/fingerprint_layout_theme">
 
     <LinearLayout
         style="@style/SuwContentFrame"
             android:layout_marginTop="@dimen/suw_description_margin_top"
             android:text="@string/security_settings_fingerprint_enroll_find_sensor_message"/>
 
+        <View
+            android:layout_height="0dp"
+            android:layout_width="match_parent"
+            android:layout_weight="1"/>
+
         <include
             layout="@layout/fingerprint_enroll_find_sensor_graphic"
-            android:layout_marginTop="32dp"
             android:layout_width="@dimen/fingerprint_find_sensor_graphic_size"
             android:layout_height="@dimen/fingerprint_find_sensor_graphic_size"
             android:layout_gravity="center_horizontal"/>
index 1c66103..03447b3 100644 (file)
@@ -20,7 +20,7 @@
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    style="@style/SetupWizardFingerprintStyle">
+    style="?attr/fingerprint_layout_theme">
 
     <LinearLayout
         style="@style/SuwContentFrame"
         android:clipChildren="false">
 
         <TextView
+            android:id="@+id/message"
             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_finish_message"/>
 
+        <TextView
+            android:id="@+id/message_secondary"
+            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/setup_fingerprint_enroll_finish_message_secondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:visibility="gone" />
+
+        <View
+            android:layout_height="0dp"
+            android:layout_width="match_parent"
+            android:layout_weight="1"/>
+
         <ImageView
             android:id="@+id/fingerprint_in_app_indicator"
-            android:layout_width="120dp"
-            android:layout_height="120dp"
-            android:layout_marginTop="56dp"
+            android:layout_width="@dimen/fingerprint_in_app_indicator_size"
+            android:layout_height="@dimen/fingerprint_in_app_indicator_size"
             android:layout_gravity="center_horizontal"
+            android:contentDescription="@android:string/fingerprint_icon_content_description"
             android:src="@drawable/fp_app_indicator" />
 
         <View
index df83bd9..b05abd8 100644 (file)
@@ -20,7 +20,7 @@
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    style="@style/SetupWizardFingerprintStyle">
+    style="?attr/fingerprint_layout_theme">
 
     <LinearLayout
         style="@style/SuwContentFrame"
@@ -58,6 +58,7 @@
             android:layout_weight="1"/>
 
         <LinearLayout
+            android:id="@+id/button_bar"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginEnd="-12dp"
index efe90ae..4189137 100644 (file)
@@ -19,7 +19,7 @@
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    style="@style/SetupWizardFingerprintStyle">
+    style="?attr/fingerprint_layout_theme">
 
     <LinearLayout
         style="@style/SuwContentFrame"
index 38b6ff2..99cfa71 100644 (file)
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="center_horizontal">
+    android:gravity="center_horizontal"
+    android:paddingTop="24dp"
+    android:paddingBottom="17dp"
+    android:paddingEnd="20dp"
+    android:paddingStart="20dp">
 
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
-        android:layout_margin="10dip"
-        android:text="@string/security_settings_fingerprint_enroll_dialog_name_label"/>
+        android:text="@string/security_settings_fingerprint_enroll_dialog_name_label"
+        android:layout_marginBottom="-8dp"
+        android:layout_marginStart="4dp"
+        android:clipChildren="false"
+        android:clipToPadding="false" />
 
     <EditText
         android:id="@+id/fingerprint_rename_field"
-        android:layout_width="160dip"
-        android:layout_height="wrap_content" />
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="textCapWords"/>
 
 </LinearLayout>
index 4641f0e..17327d9 100644 (file)
 
     <include
         layout="@layout/radio_with_summary"
-        android:id="@+id/ignore_on" />
+        android:id="@+id/ignore_off" />
 
     <include
         layout="@layout/radio_with_summary"
-        android:id="@+id/ignore_off" />
+        android:id="@+id/ignore_on" />
 
 </LinearLayout>
index 5ad921a..5d844b1 100644 (file)
@@ -27,7 +27,8 @@
             android:layout_marginEnd="12dp"
             android:layout_marginTop="12dp"
             android:layout_weight="1">
-        <LinearLayout android:layout_width="match_parent"
+        <LinearLayout android:id="@+id/master_clear_container"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical">
             <TextView
index 6e50278..4ab3a0f 100644 (file)
@@ -20,6 +20,7 @@
     >
 
     <TextView
+        android:id="@+id/master_clear_confirm"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginStart="12dp"
diff --git a/res/layout/media_format_final.xml b/res/layout/media_format_final.xml
deleted file mode 100644 (file)
index ab18f34..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright (C) 2008 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.
-*/
--->
-
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        style="@style/info_layout">        
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="20sp"
-        android:text="@string/media_format_final_desc" />
-
-    <Button android:id="@+id/execute_media_format"
-        android:layout_gravity="center_horizontal"
-        android:layout_marginTop="40dip"
-        android:layout_width="150dip"
-        android:layout_height="wrap_content"
-        android:text="@string/media_format_final_button_text"
-        android:gravity="center" />
-        
-</LinearLayout>
diff --git a/res/layout/media_format_primary.xml b/res/layout/media_format_primary.xml
deleted file mode 100644 (file)
index 3590492..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright (C) 2008 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.
-*/
--->
-
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        style="@style/info_layout">
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="18sp"
-        android:text="@string/media_format_desc" />
-
-    <Button android:id="@+id/initiate_media_format"
-        android:layout_gravity="center_horizontal"
-        android:layout_marginTop="40dip"
-        android:layout_width="150dip"
-        android:layout_height="wrap_content"
-        android:text="@string/media_format_button_text"
-        android:gravity="center" />
-        
-</LinearLayout>
index e89d865..74c15bc 100644 (file)
 
         <ImageView
             android:id="@+id/nfc_payment_tap_image"
-            android:layout_width="fill_parent"
+            android:layout_width="288dp"
             android:layout_height="wrap_content"
             android:src="@drawable/tapandpay_emptystate"/>
         <TextView
             android:id="@+id/nfc_payment_empty_text"
-            android:layout_width="wrap_content"
+            android:layout_width="264dp"
             android:layout_height="wrap_content"
             android:gravity="center"
             android:textAppearance="?android:attr/textAppearanceLarge"
             android:textColor="?android:attr/textColorSecondary"
-            android:paddingTop="32dp"
+            android:paddingTop="36dp"
             android:text="@string/nfc_payment_no_apps"/>
 </LinearLayout>
index ac48e4e..565a16b 100644 (file)
@@ -36,8 +36,8 @@
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="@string/nfc_how_it_works_title"
-              android:paddingLeft="74dp"
-              android:paddingRight="74dp"
+              android:paddingLeft="16dp"
+              android:paddingRight="16dp"
               android:textColor="#FF263238"
               android:textSize="24sp" />
 
               android:layout_height="wrap_content"
               android:paddingTop="10dp"
               android:paddingBottom="18dp"
-              android:paddingLeft="74dp"
-              android:paddingRight="74dp"
+              android:paddingLeft="16dp"
+              android:paddingRight="16dp"
               android:text="@string/nfc_how_it_works_content"
               android:textColor="#FF263238"
-              android:textSize="20sp" />
+              android:textSize="16sp" />
         </LinearLayout>
         <RelativeLayout
             android:layout_width="match_parent"
index 9c37050..8df1210 100644 (file)
@@ -36,7 +36,7 @@
         android:ellipsize="marquee" />
 
 
-    <TextView android:id="@+android:id/summary"
+    <TextView android:id="@android:id/summary"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingBottom="4dp"
diff --git a/res/layout/setup_encryption_interstitial.xml b/res/layout/setup_encryption_interstitial.xml
new file mode 100644 (file)
index 0000000..8a36e30
--- /dev/null
@@ -0,0 +1,63 @@
+<?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"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/setup_wizard_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    settings:suwBackgroundTile="@drawable/setup_illustration_tile"
+    settings:suwHeaderText="@string/encryption_interstitial_header"
+    settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
+    settings:suwIllustrationImage="@drawable/setup_illustration_lock_screen">
+
+    <LinearLayout
+        style="@style/SuwContentFrame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/encryption_message"
+            style="@style/SuwDescription"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+        <RadioGroup
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dp"
+            android:checkedButton="@+id/encrypt_require_password">
+
+            <RadioButton
+                android:id="@+id/encrypt_require_password"
+                style="@style/SuwRadioButton"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <RadioButton
+                android:id="@+id/encrypt_dont_require_password"
+                style="@style/SuwRadioButton"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+        </RadioGroup>
+
+    </LinearLayout>
+
+</com.android.setupwizardlib.SetupWizardLayout>
index 826e633..f5954a1 100644 (file)
@@ -22,7 +22,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     settings:suwBackgroundTile="@drawable/setup_illustration_tile"
-    settings:suwHeaderText="@string/notification_section_header"
+    settings:suwHeaderText="@string/lock_screen_notifications_interstitial_title"
     settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
     settings:suwIllustrationImage="@drawable/setup_illustration_lock_screen">
 
diff --git a/res/layout/sim_information.xml b/res/layout/sim_information.xml
deleted file mode 100644 (file)
index d2a4acc..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/tabhost"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <LinearLayout
-        android:id="@+id/tabs_container"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical">
-
-        <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:scrollbars="none"
-            android:fillViewport="true">
-
-            <TabWidget
-                android:id="@android:id/tabs"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="horizontal"
-                style="?android:attr/tabWidgetStyle" />
-
-        </HorizontalScrollView>
-
-        <!-- give an empty content area to make tabhost happy -->
-        <FrameLayout
-            android:id="@android:id/tabcontent"
-            android:layout_width="0dip"
-            android:layout_height="0dip" />
-
-        <ListView
-            android:id="@android:id/list"
-            android:layout_width="match_parent"
-            android:layout_height="0dip"
-            android:layout_weight="1"
-            android:clipChildren="false"
-            android:clipToPadding="false"
-            android:smoothScrollbar="false" />
-
-    </LinearLayout>
-
-</TabHost>
diff --git a/res/layout/usb_dialog_container.xml b/res/layout/usb_dialog_container.xml
new file mode 100644 (file)
index 0000000..e145911
--- /dev/null
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingTop="10dp"
+    android:paddingBottom="5dp">
+
+    <LinearLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" />
+
+</ScrollView>
index ea49fa5..d95599d 100644 (file)
@@ -17,7 +17,8 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:duplicateParentState="true">
 
     <TextView
         android:id="@+id/widget_text1"
diff --git a/res/values-sw400dp/dimens.xml b/res/values-sw400dp/dimens.xml
new file mode 100755 (executable)
index 0000000..9c82d90
--- /dev/null
@@ -0,0 +1,28 @@
+<?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
+  -->
+
+<resources>
+    <dimen name="fingerprint_ring_radius">106dp</dimen>
+    <dimen name="fingerprint_animation_size">100dp</dimen>
+    <dimen name="fingerprint_progress_bar_size">220dp</dimen>
+    <dimen name="fingerprint_enrolling_content_margin_top">56dp</dimen>
+    <dimen name="fingerprint_in_app_indicator_size">176dp</dimen>
+    <dimen name="fingerprint_find_sensor_graphic_size">240dp</dimen>
+
+    <dimen name="setup_fingerprint_ring_radius">92dp</dimen>
+    <dimen name="setup_fingerprint_progress_bar_size">192dp</dimen>
+</resources>
index 87d979e..e5f1988 100644 (file)
 
     <!-- MVNO Info used in APN editor -->
     <string-array name="mvno_type_entries">
-        <!-- Do not translate. -->
         <item>None</item>
         <!-- Do not translate. -->
         <item>SPN</item>
         <item>0</item>
     </string-array>
 
-    <!-- These values must match up with the code in UsbModeChooserActivity.java. -->
-    <string-array name="usb_available_functions">
-        <item>@string/usb_use_charging_only</item>
-        <item>@string/usb_use_file_transfers</item>
-        <item>@string/usb_use_photo_transfers</item>
-        <item>@string/usb_use_MIDI</item>
-    </string-array>
-
 </resources>
index 0395989..f3fea10 100644 (file)
@@ -67,7 +67,6 @@
     <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>
index c10e83f..53b9196 100755 (executable)
@@ -58,8 +58,6 @@
 
     <dimen name="pager_tabs_padding">0dp</dimen>
 
-    <dimen name="circle_avatar_size">40dp</dimen>
-
     <!-- Minimum width for the popup for updating a user's photo. -->
     <dimen name="update_user_photo_popup_min_width">300dip</dimen>
 
     <dimen name="fingerprint_pulse_radius">50dp</dimen>
     <item name="fingerprint_sensor_location_fraction_x" type="fraction">50%</item>
     <item name="fingerprint_sensor_location_fraction_y" type="fraction">30%</item>
-    <dimen name="fingerprint_find_sensor_graphic_size">200dp</dimen>
+    <dimen name="fingerprint_find_sensor_graphic_size">190dp</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="fingerprint_animation_size">88dp</dimen>
+    <dimen name="fingerprint_progress_bar_size">192dp</dimen>
+    <dimen name="fingerprint_enrolling_content_margin_top">36dp</dimen>
+    <dimen name="fingerprint_in_app_indicator_size">120dp</dimen>
+
+    <dimen name="setup_fingerprint_ring_radius">80dip</dimen>
+    <dimen name="setup_fingerprint_progress_bar_size">168dp</dimen>
 
     <dimen name="confirm_credentials_security_method_margin">48dp</dimen>
     <dimen name="fab_size">56dp</dimen>
index 441dbbd..1482f95 100644 (file)
         <item quantity="one"><xliff:g id="count">%1$d</xliff:g> fingerprint set up</item>
         <item quantity="other"><xliff:g id="count">%1$d</xliff:g> fingerprints set up</item>
     </plurals>
-    <!-- Introduction title shown in fingerprint enrollment dialog [CHAR LIMIT=22] -->
+    <!-- Introduction title shown in fingerprint enrollment dialog [CHAR LIMIT=29] -->
     <string name="security_settings_fingerprint_enroll_onboard_title">Fingerprint setup</string>
     <!-- Introduction message shown in fingerprint enrollment dialog when the user needs to choose an
          alternate screen unlock (pin, pattern or password) as a backup to fingerprint. -->
     <!-- Introduction message shown in fingerprint enrollment dialog, second item to describe what
          the user needs to do. -->
     <string name="security_settings_fingerprint_enroll_onboard_message_2">Add your fingerprint</string>
-    <!-- Introduction title shown in fingerprint enrollment to introduce the fingerprint feature[CHAR LIMIT=22] -->
+    <!-- Introduction title shown in fingerprint enrollment to introduce the fingerprint feature[CHAR LIMIT=29] -->
     <string name="security_settings_fingerprint_enroll_introduction_title">Unlock with fingerprint</string>
     <!-- Introduction detail message shown in fingerprint enrollment dialog [CHAR LIMIT=NONE]-->
-    <string name="security_settings_fingerprint_enroll_introduction_message">Just touch the fingerprint sensor to unlock your phone, authorize purchases, or sign in to apps. Any fingerprints added to your phone will be able to do these things, so be careful who you add.</string>
+    <string name="security_settings_fingerprint_enroll_introduction_message">Just touch the fingerprint sensor to unlock your phone, authorize purchases, or sign in to apps. Be careful whose fingerprints you add - any fingerprints added will be able to do these things.</string>
     <!-- Introduction detail message shown in fingerprint enrollment dialog to warn the user [CHAR LIMIT=NONE]-->
     <string name="security_settings_fingerprint_enroll_introduction_message_warning">Note: Your fingerprint may be less secure than a strong pattern or PIN.</string>
     <!-- Link text shown bellow the warning to learn morn about fingerprint risk [CHAR LIMIT=22]-->
     <string name="security_settings_fingerprint_enroll_introduction_cancel">Cancel</string>
     <!-- Button text to continue to the next screen from the introduction [CHAR LIMIT=22] -->
     <string name="security_settings_fingerprint_enroll_introduction_continue">Continue</string>
-    <!-- Introduction title shown in fingerprint enrollment dialog to locate the sensor [CHAR LIMIT=22] -->
+    <!-- Introduction title shown in fingerprint enrollment dialog to locate the sensor [CHAR LIMIT=29] -->
     <string name="security_settings_fingerprint_enroll_find_sensor_title">Find the sensor</string>
     <!-- Message shown in fingerprint enrollment dialog to locate the sensor -->
     <string name="security_settings_fingerprint_enroll_find_sensor_message">
     <string name="security_settings_fingerprint_enroll_dialog_ok">OK</string>
     <!-- Button text shown in fingerprint dialog that allows the user to delete the fingerprint template [CHAR LIMIT=22] -->
     <string name="security_settings_fingerprint_enroll_dialog_delete">Delete</string>
-    <!-- Title shown in fingerprint enrollment dialog to begin enrollment [CHAR LIMIT=22]-->
+    <!-- Title shown in fingerprint enrollment dialog to begin enrollment [CHAR LIMIT=29]-->
     <string name="security_settings_fingerprint_enroll_start_title">Let\u2019s start!</string>
-    <!-- Message shown in fingerprint enrollment dialog to begin enrollment -->
-    <string name="security_settings_fingerprint_enroll_start_message">Put your finger on the fingerprint sensor. Lift after you feel a vibration.</string>
-    <!-- Title shown in fingerprint enrollment dialog to repeat touching the fingerprint sensor [CHAR LIMIT=22] -->
-    <string name="security_settings_fingerprint_enroll_repeat_title">Great! Now repeat.</string>
-    <!-- Message shown in fingerprint enrollment dialog to repeat touching the fingerprint sensor -->
-    <string name="security_settings_fingerprint_enroll_repeat_message">Put the same finger on the fingerprint sensor and lift after you feel a vibration.</string>
-    <!-- Title shown in fingerprint enrollment dialog once enrollment is completed [CHAR LIMIT=22] -->
+    <!-- Message shown in fingerprint enrollment dialog to begin enrollment [CHAR LIMIT=NONE] -->
+    <string name="security_settings_fingerprint_enroll_start_message">Put your finger on the sensor and lift after you feel a vibration</string>
+    <!-- Title shown in fingerprint enrollment dialog to repeat touching the fingerprint sensor [CHAR LIMIT=29] -->
+    <string name="security_settings_fingerprint_enroll_repeat_title">Great! Now repeat</string>
+    <!-- Message shown in fingerprint enrollment dialog to repeat touching the fingerprint sensor [CHAR LIMIT=NONE] -->
+    <string name="security_settings_fingerprint_enroll_repeat_message">Move your finger slightly to add all the different parts of your fingerprint</string>
+    <!-- Title shown in fingerprint enrollment dialog once enrollment is completed [CHAR LIMIT=29] -->
     <string name="security_settings_fingerprint_enroll_finish_title">Fingerprint added!</string>
     <!-- Message shown in fingerprint enrollment dialog once enrollment is completed -->
     <string name="security_settings_fingerprint_enroll_finish_message">Whenever you see this icon, you can use your fingerprint for identification or to authorize a purchase.</string>
+    <!-- Message shown when fingerprint enrollment is completed during setup wizard [CHAR LIMIT=NONE] -->
+    <string name="setup_fingerprint_enroll_finish_message">Just touch the fingerprint sensor to wake and unlock your device.</string>
+    <!-- Message shown when fingerprint enrollment is completed, telling user about the fingerprint icon that will be shown whenever they can use their fingerprint [CHAR LIMIT=NONE] -->
+    <string name="setup_fingerprint_enroll_finish_message_secondary">When you see this icon, you can use your fingerprint.</string>
     <!-- Button text to setup screen lock in onboard dialog [CHAR LIMIT=34] -->
     <string name="security_settings_fingerprint_enroll_setup_screen_lock">Set up screen lock</string>
     <!-- Button text to exit fingerprint wizard after everything is done [CHAR LIMIT=15] -->
     <!-- Text shown when "Add fingerprint" button is disabled -->
     <string name="fingerprint_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
 
+    <!-- Title shown in a dialog which asks the user to confirm when the last fingerprint gets deleted by him. [CHAR LIMIT=35]-->
+    <string name="fingerprint_last_delete_title">Remove all fingerprints?</string>
+
+    <!-- Message shown in a dialog which asks the user to confirm when the last fingerprint gets deleted by him. [CHAR LIMIT=NONE]-->
+    <string name="fingerprint_last_delete_message">You won\'t be able to use your fingerprints to unlock your phone, authorize purchases, or sign in to apps with them.</string>
+
+    <!-- Button to confirm the last removing the last fingerprint. [CHAR LIMIT=20]-->
+    <string name="fingerprint_last_delete_confirm">Yes, remove</string>
+
     <!-- Content description for the fingerprint icon when the user is prompted to enter his credentials. Not shown on the screen. [CHAR LIMIT=NONE] -->
     <string name="confirm_fingerprint_icon_content_description">Use your fingerprint to continue.</string>
 
     <!--  Title for security picker to choose the unlock method: None/Pattern/PIN/Password [CHAR LIMIT=22] -->
     <string name="lock_settings_picker_title">Choose screen lock</string>
 
+    <!-- Message shown in screen lock picker while setting up the backup/fallback screen lock method for fingerprint. Users can choose to use this method to unlock the screen instead of fingerprint, or when fingerprint is not accepted. [CHAR LIMIT=80] [BACKUP_MESSAGE_ID=2799884038398627882] -->
+    <string name="lock_settings_picker_fingerprint_message">Choose your backup screen lock method</string>
+
     <!--  Main Security lock settings --><skip />
     <!--  Title for PreferenceScreen to launch picker for security method when there is none [CHAR LIMIT=22] -->
     <string name="unlock_set_unlock_launch_picker_title">Screen lock</string>
     <!-- Summary specifying that this is the current screen lock setting [CHAR LIMIT=45] -->
     <string name="current_screen_lock">Current screen lock</string>
 
-    <!--  Content of warning dialog about disabling device protection features when user is switching from a secure unlock method to an insecure one. [CHAR LIMIT=NONE] -->
-    <string name="unlock_disable_frp_warning_content">Device protection features will no longer work.</string>
-
     <!-- Summary for preference that has been disabled by because of the DevicePolicyAdmin, or because device encryption is enabled, or because there are credentials in the credential storage [CHAR LIMIT=50] -->
     <string name="unlock_set_unlock_disabled_summary">Disabled by administrator, encryption policy, or credential storage</string>
 
     <!-- Title for option to turn of password/pin/pattern unlock. [CHAR LIMIT=22] -->
     <string name="unlock_disable_lock_title">Turn off screen lock</string>
 
-    <!-- Summary shown under unlock_disable_lock_title when pattern is in use and can be removed [CHAR LIMIT=45] -->
-    <string name="unlock_disable_lock_pattern_summary">Remove unlock pattern</string>
-    <!-- Summary shown under unlock_disable_lock_title when PIN is in use and can be removed [CHAR LIMIT=45]-->
-    <string name="unlock_disable_lock_pin_summary">Remove unlock PIN</string>
-    <!-- Summary shown under unlock_disable_lock_title when password is in use and can be removed [CHAR LIMIT=45]-->
-    <string name="unlock_disable_lock_password_summary">Remove unlock password</string>
-    <!-- Summary shown under unlock_disable_lock_title when an unknown secure unlocking method is in use and can be removed [CHAR LIMIT=45]-->
-    <string name="unlock_disable_lock_unknown_summary">Remove screen lock</string>
+    <!-- Title of the dialog shown when the user removes the device lock [CHAR LIMIT=NONE] -->
+    <string name="unlock_disable_frp_warning_title">Remove device protection?</string>
+
+    <!-- Content of the dialog shown when the user removes the device lock pattern [CHAR LIMIT=NONE] -->
+    <string name="unlock_disable_frp_warning_content_pattern">"Device protection features will not work without your pattern."</string>
+    <!-- Content of the dialog shown when the user removes the device lock pattern and the user has fingerprints enrolled [CHAR LIMIT=NONE] -->
+    <string name="unlock_disable_frp_warning_content_pattern_fingerprint">"Device protection features will not work without your pattern.<xliff:g id="empty_line">\n\n</xliff:g>Your saved fingerprints will also be removed from this device and you won't be able to unlock your phone, authorize purchases, or sign in to apps with them."</string>
+    <!-- Content of the dialog shown when the user removes the device lock PIN [CHAR LIMIT=NONE] -->
+    <string name="unlock_disable_frp_warning_content_pin">"Device protection features will not work without your PIN."</string>
+    <!-- Content of the dialog shown when the user removes the device lock PIN and the user has fingerprints enrolled [CHAR LIMIT=NONE] -->
+    <string name="unlock_disable_frp_warning_content_pin_fingerprint">"Device protection features will not work without your PIN.<xliff:g id="empty_line">\n\n</xliff:g>Your saved fingerprints will also be removed from this device and you won't be able to unlock your phone, authorize purchases, or sign in to apps with them."</string>
+    <!-- Content of the dialog shown when the user removes the device lock password [CHAR LIMIT=NONE] -->
+    <string name="unlock_disable_frp_warning_content_password">"Device protection features will not work without your password."</string>
+    <!-- Content of the dialog shown when the user removes the device lock password and the user has fingerprints enrolled [CHAR LIMIT=NONE] -->
+    <string name="unlock_disable_frp_warning_content_password_fingerprint">"Device protection features will not work without your password.<xliff:g id="empty_line">\n\n</xliff:g>Your saved fingerprints will also be removed from this device and you won't be able to unlock your phone, authorize purchases, or sign in to apps with them."</string>
+    <!-- Content of the dialog shown when the user removes the device lock of unknown type [CHAR LIMIT=NONE] -->
+    <string name="unlock_disable_frp_warning_content_unknown">"Device protection features will not work without your screen lock."</string>
+    <!-- Content of the dialog shown when the user removes the device lock of unknown type and the user has fingerprints enrolled [CHAR LIMIT=NONE] -->
+    <string name="unlock_disable_frp_warning_content_unknown_fingerprint">"Device protection features will not work without your screen lock.<xliff:g id="empty_line">\n\n</xliff:g>Your saved fingerprints will also be removed from this device and you won't be able to unlock your phone, authorize purchases, or sign in to apps with them."</string>
+
+    <!-- Affirmative action of the dialog shown when the user removes the device lock [CHAR LIMIT=25] -->
+    <string name="unlock_disable_frp_warning_ok">Yes, remove</string>
 
     <!-- Title shown on security settings to allow the user to change their lockscreen pattern [CHAR LIMIT=22] -->
     <string name="unlock_change_lock_pattern_title">Change unlock pattern</string>
     <!-- ======================================================================================= -->
 
     <!-- Title for the dialog to enter PIN. [CHAR LIMIT=40] -->
-    <string name="bluetooth_pairing_request">Bluetooth pairing request</string>
+    <string name="bluetooth_pairing_request">Pair with <xliff:g id="device_name">%1$s</xliff:g>?</string>
 
-    <!-- Message when bluetooth dialog for pin entry is showing. [CHAR LIMIT=NONE] -->
-    <string name="bluetooth_enter_pin_msg">Device</string>
     <!-- Message when bluetooth is informing the user of the pairing key. [CHAR LIMIT=NONE] -->
-    <string name="bluetooth_pairing_key_msg">Pairing code</string>
+    <string name="bluetooth_pairing_key_msg">Bluetooth pairing code</string>
 
     <!-- Message when bluetooth dialog for passkey entry is showing. [CHAR LIMIT=NONE] -->
     <string name="bluetooth_enter_passkey_msg">Type the pairing code then press Return or Enter</string>
     <string name="bluetooth_display_passkey_pin_msg">To pair with:<xliff:g id="bold1">&lt;br>&lt;b></xliff:g><xliff:g id="device_name">%1$s</xliff:g><xliff:g id="end_bold1">&lt;/b>&lt;br>&lt;br></xliff:g>Type on it:<xliff:g id="bold2">&lt;br>&lt;b></xliff:g><xliff:g id="passkey">%2$s</xliff:g><xliff:g id="end_bold2">&lt;/b></xliff:g>, then press Return or Enter.</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>
+    <string name="bluetooth_pairing_shares_phonebook">Allow <xliff:g id="device_name">%1$s</xliff:g> to access your contacts and call history</string>
 
     <!-- Title for BT error dialogs. -->
     <string name="bluetooth_error_title"></string>
     <string name="wifi_stopping">Turning off Wi\u2011Fi\u2026</string>
     <!-- Summary text when Wi-Fi or bluetooth has error -->
     <string name="wifi_error">Error</string>
+    <!-- Summary text when wifi SoftAP started failed due to no legal usable channel allowed in this region by regulatory -->
+    <string name="wifi_sap_no_channel_error">5 GHz band not available in this country</string>
     <!-- Toast message when Wi-Fi or bluetooth is disallowed in airplane mode -->
     <string name="wifi_in_airplane_mode">In Airplane mode</string>
     <!-- Checkbox title for option to notify user when open networks are nearby -->
     <string name="category_personal">Personal</string>
     <!-- Header for items under the work user [CHAR LIMIT=30] -->
     <string name="category_work">Work</string>
+    <!-- Content description for work profile accounts group [CHAR LIMIT=NONE] -->
+    <string name="accessibility_category_work">Work profile accounts - <xliff:g id="managed_by" example="Managed by Corporate application">%s</xliff:g></string>
+    <!-- Content description for personal profile accounts group [CHAR LIMIT=NONE] -->
+    <string name="accessibility_category_personal">Personal profile accounts</string>
+    <!-- Content description for work profile details page title [CHAR LIMIT=NONE] -->
+    <string name="accessibility_work_account_title">Work account - <xliff:g id="managed_by" example="Email provider">%s</xliff:g></string>
+    <!-- Content description for personal profile details page title [CHAR LIMIT=NONE] -->
+    <string name="accessibility_personal_account_title">Personal account - <xliff:g id="managed_by" example="Email provider">%s</xliff:g></string>
 
     <!-- Main Settings screen, setting option name to go into search settings -->
     <string name="search_settings">Search</string>
 
     <!-- About phone screen, status item label  [CHAR LIMIT=40] -->
     <string name="firmware_version">Android version</string>
+    <!-- About phone screen, status item label  [CHAR LIMIT=60] -->
+    <string name="security_patch">Android security patch level</string>
     <!-- About phone screen, status item label  [CHAR LIMIT=40] -->
     <string name="model_number">Model number</string>
     <!-- About phone screen, fcc equipment id label  [CHAR LIMIT=40] -->
     <!-- Master clear progress screen text [CHAR LIMIT=75] -->
     <string name="master_clear_progress_text">Please wait...</string>
 
-    <!-- Media Format -->
-    <!-- SD card & phone storage settings screen, setting option name under Internal phone storage heading [CHAR LIMIT=25] -->
-    <string name="media_format_title" product="nosdcard">Erase USB storage</string>
-    <!-- SD card & phone storage settings screen, setting option name under Internal phone storage heading -->
-    <string name="media_format_title" product="default">Erase SD card</string>
-    <!-- SD card & phone storage settings screen, setting option summary text under Internal phone storage heading [CHAR LIMIT=30] -->
-    <string name="media_format_summary" product="nosdcard">Erases all data in USB storage</string>
-    <!-- SD card & phone storage settings screen, setting option summary text under Internal phone storage heading -->
-    <string name="media_format_summary" product="default">Erases all data on the SD card</string>
-    <!-- SD card & phone storage settings screen, message on screen after user selects Factory data reset [CHAR LIMIT=NONE] -->
-    <string name="media_format_desc" product="nosdcard">Erase all USB storage? You will lose <b>all</b> stored data!</string>
-    <!-- SD card & phone storage settings screen, message on screen after user selects Factory data reset [CHAR LIMIT=NONE] -->
-    <string name="media_format_desc" product="default">Erase the SD card? You will lose <b>all</b> data on the card!</string>
-    <!-- SD card & phone storage settings screen, button on screen after user selects Factory data reset [CHAR LIMIT=25] -->
-    <string name="media_format_button_text" product="nosdcard">Erase USB storage</string>
-    <!-- SD card & phone storage settings screen, button on screen after user selects Factory data reset -->
-    <string name="media_format_button_text" product="default">Erase SD card</string>
-    <!-- SD card & phone storage settings screen, message on screen after user selects Format media button [CHAR LIMIT=NONE] -->
-    <string name="media_format_final_desc" product="nosdcard">Erase the USB storage, deleting all files stored there? You can\u2019t reverse this action!</string>
-    <!-- SD card & phone storage settings screen, message on screen after user selects Format media button [CHAR LIMIT=NONE] -->
-    <string name="media_format_final_desc" product="default">Erase the SD card, deleting all files stored there? You can\u2019t reverse this action!</string>
-    <!-- SD card & phone storage settings screen, button on screen after user selects Format media button -->
-    <string name="media_format_final_button_text">Erase everything</string>
-
     <!-- Main settings screen, Call settings title for item to go into the call settings -->
     <string name="call_settings_title">Call settings</string>
     <!-- Main settings screen, Call settings summary for item to go into call settings -->
     <string name="call_settings_summary">Set up voicemail, call forwarding, call waiting, caller ID</string>
 
+    <!-- TODO: Remove them once the same entries in SettingsLib is translated. -->
     <!-- Tethering controls, item title to go into the tethering settings -->
     <!-- Tethering controls, item title to go into the tethering settings when only USB tethering is available [CHAR LIMIT=25]-->
     <string name="tether_settings_title_usb">USB tethering</string>
     <string name="ask_compatibility">Ask when launched</string>
     <!-- Manage applications, individual application screen, checkbox to control compatibility mode.  -->
     <string name="enable_compatibility">Scale app</string>
+    <!-- TODO: Remove it once the same entry in SettingsLib is translated. -->
     <!-- Manage apps, individual app screen, substituted for the application's label when the app's label CAN NOT be determined.-->
     <string name="unknown">Unknown</string>
     <!-- [CHAR LIMIT=25] Manage applications screen, menu item.  Sorts all of the apps in the list alphabetically. -->
     <string name="memory">RAM</string>
     <!-- Text to label a process entry with the process name. -->
     <string name="service_process_name"><xliff:g id="process">%1$s</xliff:g></string>
+    <!-- TODO: Remove it once the same entry in SettingsLib is translated. -->
     <!-- [CHAR LIMIT=NONE] Label of a running process that represents another user -->
     <string name="running_process_item_user_label">User: <xliff:g id="user_name">%1$s</xliff:g></string>
     <!-- [CHAR LIMIT=NONE] Label of a running process that represents a removed -->
     <!-- Menu label for refreshing with latest usage numbers -->
     <string name="menu_stats_refresh">Refresh</string>
 
+    <!-- TODO: Remove it once the same entry in SettingsLib is translated. -->
     <!-- Label for kernel threads in battery usage -->
     <string name="process_kernel_label">Android OS</string>
     <!-- Label for mediaserver process in battery usage -->
     <!-- Label displaying current network data usage limit threshold. [CHAR LIMIT=18] -->
     <string name="data_usage_sweep_limit"><font size="21"><xliff:g id="number" example="128">^1</xliff:g></font> <font size="9"><xliff:g id="unit" example="KB">^2</xliff:g></font>\n<font size="12">limit</font></string>
 
+    <!-- TODO: Remove it once the same entry in SettingsLib is translated. -->
     <!-- Title of data usage item that represents all uninstalled applications. [CHAR LIMIT=48] -->
     <string name="data_usage_uninstalled_apps">Removed apps</string>
+    <!-- TODO: Remove it once the same entry in SettingsLib is translated. -->
     <!-- Title of data usage item that represents all uninstalled applications or removed users. [CHAR LIMIT=48] -->
     <string name="data_usage_uninstalled_apps_users">Removed apps and users</string>
     <!-- Combination of total network bytes sent and received by an application. [CHAR LIMIT=NONE] -->
     <string name="user_delete_user_description">Delete user</string>
     <!-- Delete button text [CHAR LIMIT=25] -->
     <string name="user_delete_button">Delete</string>
+    <!-- TODO: Remove it once the same entry in SettingsLib is translated. -->
     <!-- Title for Guest user [CHAR LIMIT=35] -->
     <string name="user_guest">Guest</string>
     <!-- Label for item to exit guest mode [CHAR LIMIT=35] -->
     <!-- Caption for button linking to a page explaining how Tap and Pay works-->
     <string name="nfc_payment_how_it_works">How it works</string>
     <!-- String shown when there are no NFC payment applications installed -->
-    <string name="nfc_payment_no_apps">Use Tap &amp; pay to make in-store purchases</string>
+    <string name="nfc_payment_no_apps">Pay with your phone in stores</string>
     <!-- Header text that can be clicked on to change the default payment app -->
     <string name="nfc_payment_default">Payment default</string>
     <!-- Summary text that is shown when no default app is set -->
     <string name="nfc_payment_default_not_set">Not set</string>
     <!-- String indicating the label of the default payment app and a description of its state; eg Google Wallet - MasterCard 1234 -->
     <string name="nfc_payment_app_and_desc"><xliff:g id="app">%1$s</xliff:g> - <xliff:g id="description">%2$s</xliff:g></string>
-    <!-- Header for action to choose when the open app supports TapPay -->
-    <string name="nfc_payment_open_app">If open app supports Tap &amp; pay</string>
+    <!-- Header for what to do when the open app supports TapPay: use the default set app, or the open app -->
+    <string name="nfc_payment_use_default">Use default</string>
+    <!-- Always use the default app (independent of what app is open) -->
+    <string name="nfc_payment_favor_default">Always</string>
     <!-- If open app supports TapPay, use that app instead of the default -->
-    <string name="nfc_payment_favor_open">Use that app instead of <xliff:g id="app">%1$s</xliff:g></string>
-    <!-- If open app supports TapPay, use that app instead of the default (name of default app unknown) -->
-    <string name="nfc_payment_favor_open_default_unknown">Use that app instead</string>
-    <!-- If open app supports TapPay, still use the default app -->
-    <string name="nfc_payment_favor_default">Still use <xliff:g id="app">%1$s</xliff:g></string>
-    <!-- If open app supports TapPay, still use the default app (name of default app unknown) -->
-    <string name="nfc_payment_favor_default_default_unknown">Still use default</string>
+    <string name="nfc_payment_favor_open">Except when another payment app is open</string>
     <!-- Header for a dialog asking the user which payment app to use -->
     <string name="nfc_payment_pay_with">At a Tap &amp; pay terminal, pay with:</string>
     <!-- Header for text explaning how to pay at a payment terminal in a store -->
     <string name="nfc_how_it_works_title">Paying at the terminal</string>
     <!-- Content for text explaning how to pay at a payment terminal in a store -->
-    <string name="nfc_how_it_works_content">Once you\u2019ve set up a Tap &amp; pay app and your phone is powered on, tap your phone on any terminal with the Tap &amp; pay logo on it to make a purchase.</string>
+    <string name="nfc_how_it_works_content">Set up a payment app. Then just hold the back of your phone up to any terminal with the contactless symbol.</string>
     <!-- Button the user can click to indicate he understood and dismiss the screen -->
     <string name="nfc_how_it_works_got_it">Got it</string>
     <!-- NFC More... title.  [CHAR LIMIT=40] -->
     <!-- Security > Choose PIN/PW/Pattern > Notification redaction interstitial: Message asking the user how they want their notifications to appear when the device is locked [CHAR LIMIT=NONE] -->
     <string name="lock_screen_notifications_interstitial_message">When your device is locked, how do you want notifications to show?</string>
 
+    <!-- Security > Choose PIN/PW/Pattern > Notification redaction interstitial: Title for the screen asking the user how they want their notifications to appear when the device is locked [CHAR LIMIT=30] -->
+    <string name="lock_screen_notifications_interstitial_title">Notifications</string>
+
     <!-- Sound & notification > Notification section: Title for the option managing notifications per application. [CHAR LIMIT=30] -->
     <string name="app_notifications_title">App notifications</string>
 
     <!-- [CHAR LIMIT=60] Unlock setting for screen pinning -->
     <string name="screen_pinning_unlock_none">Lock device when unpinning</string>
 
+    <!-- TODO: Remove it once the same entry in SettingsLib is translated. -->
     <!-- Title for a work profile. [CHAR LIMIT=25] -->
     <string name="managed_user_title">Work profile</string>
     <!-- Opening string on the dialog that prompts the user to confirm that they really want to delete their existing work profile. The administration app icon and name appear after the final colon. [CHAR LIMIT=NONE] -->
     <!-- Title for IMEI preference [CHAR LIMIT=30] -->
     <string name="imei_information_title">IMEI information</string>
 
-    <!-- Encryption interstitial title [CHAR LIMIT=30] -->
-    <string name="encryption_interstitial_header">Encryption</string>
+    <!-- Encryption interstitial title. This screen asks the user whether the device will ask for a PIN / pattern / password before the device starts up. [CHAR LIMIT=30] -->
+    <string name="encryption_interstitial_header">Secure start-up</string>
 
     <!-- Encryption interstitial button to continue with the shown setting.  Appears on screen that asks the user to opt in/out of encrypting device with a pin/pattern/password. [CHAR LIMIT=NONE] -->
     <string name="encryption_continue_button">Continue</string>
    \n\nThis helps protect data on lost or stolen devices.
    </string>
 
+    <!-- Message shown on encryption interstitial to ask the user whether or not they want to use a PIN to encrypt the device while setting up fingerprint unlock. [CHAR LIMIT=NONE] -->
+    <string name="encryption_interstitial_message_pin_for_fingerprint">In addition to using your fingerprint to unlock your device, you can further protect this device by requiring your PIN before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices.</string>
+    <!-- Message shown on encryption interstitial to ask the user whether or not they want to use a pattern to encrypt the device while setting up fingerprint unlock. [CHAR LIMIT=NONE] -->
+    <string name="encryption_interstitial_message_pattern_for_fingerprint">In addition to using your fingerprint to unlock your device, you can further protect this device by requiring your pattern before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices.</string>
+    <!-- Message shown on encryption interstitial to ask the user whether or not they want to use a password to encrypt the device while setting up fingerprint unlock. [CHAR LIMIT=NONE] -->
+    <string name="encryption_interstitial_message_password_for_fingerprint">In addition to using your fingerprint to unlock your device, you can further protect this device by requiring your password before it starts up. Until the device starts up, it can\u2019t receive calls, messages, or notifications, including alarms.\n\nThis helps protect data on lost or stolen devices.</string>
+
     <!-- Radio button text that require a PIN to start device [CHAR LIMIT=NONE] -->
     <string name="encrypt_require_pin">Require PIN to start device</string>
     <!-- Radio button text that require a pattern to start device [CHAR LIMIT=NONE] -->
    <!-- Title of storage preference to control where app is stored -->
    <string name="storage_used">Storage used</string>
 
+   <!-- Title of button to change storage [CHAR LIMIT=25] -->
+   <string name="change">Change</string>
+
+   <!-- Title of dialog to change storage [CHAR LIMIT=25] -->
+   <string name="change_storage">Change storage</string>
+
    <!-- Label for notification settings for an specific app [CHAR LIMIT=40] -->
    <string name="notifications_label">Notifications</string>
    <!-- App notification summary with notifications enabled [CHAR LIMIT=40] -->
         <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps can open their supported links</item>
     </plurals>
 
+    <!-- Explanation that the app that will ALWAYS be launched to open web links to domains that it understands -->
+    <string name="app_link_open_always">Open in this app</string>
+
+    <!-- Explanation that the user will be asked whether to launch the app to open web links to domains that it understands -->
+    <string name="app_link_open_ask">Ask every time</string>
+
+    <!-- Explanation that the app that will NEVER be launched to open web links to domains that it understands -->
+    <string name="app_link_open_never">Don&#8217;t open in this app</string>
+
     <!-- Fingerprint hint message when finger was not recognized.-->
     <string name="fingerprint_not_recognized">Not recognized</string>
 
     <!-- Summary for No Default Assist settings [CHAR LIMIT=45] -->
     <string name="default_assist_none">None</string>
 
-    <!-- Title for Choose Assist dialog [CHAR LIMIT=30] -->
-    <string name="choose_assist_title">Choose Assist</string>
+    <!-- Title for Choose Assist dialog [CHAR LIMIT=35] -->
+    <string name="choose_assist_title">Choose Assist app</string>
 
     <!-- [CHAR_LIMIT=45] Title of the security warning dialog for setting an assitant -->
     <string name="assistant_security_warning_title">
 
     <!-- [CHAR_LIMIT=NONE] Warning message about security implications of setting an assistant,
          displayed as a dialog message when the user selects an assistant. -->
-    <string name="assistant_security_warning"><xliff:g id="assistant_app_name">%s</xliff:g> will
-        be able to read information about apps in use on your system, including information
-        visible on your screen or accessible within the apps.</string>
+    <string name="assistant_security_warning">The assistant will be able to read information about
+        apps in use on your system, including information visible on your screen or accessible
+        within the apps.</string>
 
     <!-- Label for the button to acknowledge assistant security implications. [CHAR LIMIT=30] -->
     <string name="assistant_security_warning_agree">Agree</string>
     <!-- Label for process [CHAR LIMIT=25] -->
     <string name="process_format"><xliff:g id="app_name" example="Settings">%1$s</xliff:g> (<xliff:g id="count" example="3">%2$d</xliff:g>)</string>
 
-    <!-- Label for list to control apps that ignore battery saving restrictions [CHAR LIMIT=25]-->
-    <string name="high_power_apps">Ignore optimizations</string>
+    <!-- Label for list to control apps that ignore battery saving restrictions [CHAR LIMIT=27]-->
+    <string name="high_power_apps">Battery optimization</string>
 
     <!-- Filter for apps allowed to use a lot of power [CHAR LIMIT=25] -->
-    <string name="high_power_filter_on">Allowed</string>
+    <string name="high_power_filter_on">Not optimized</string>
 
     <!-- Summary of app allowed to use a lot of power [CHAR LIMIT=60] -->
-    <string name="high_power_on">Ignoring battery optimizations</string>
+    <string name="high_power_on">Ignoring battery optimization</string>
 
     <!-- Summary of app not allowed to use a lot of power [CHAR LIMIT=60] -->
-    <string name="high_power_off">Not allowed</string>
+    <string name="high_power_off">Optimizing battery use</string>
+
+    <!-- Summary of app which doesn't have a battery optimization setting [CHAR LIMIT=60] -->
+    <string name="high_power_system">Battery optimization not available</string>
 
     <!-- Description of high power switch [CHAR LIMIT=NONE] -->
-    <string name="high_power_desc">Don\u2019t apply battery optimizations. May drain your battery more quickly.</string>
+    <string name="high_power_desc">Don\u2019t apply battery optimization. May drain your battery more quickly.</string>
 
     <!-- Description of number of apps with high power turned on [CHAR LIMIT=NONE] -->
     <plurals name="high_power_count">
-        <item quantity="one">1 app allowed to ignore battery optimizations</item>
-        <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps allowed to ignore battery optimizations</item>
+        <item quantity="one">1 app allowed to ignore battery optimization</item>
+        <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps allowed to ignore battery optimization</item>
     </plurals>
 
+    <!-- Title of prompt dialog app can invoke to turn off optimization [CHAR LIMIT=NONE] -->
+    <string name="high_power_prompt_title">Ignore battery optimizations?</string>
+
+    <!-- Body text of prompt dialog app can invoke to turn off optimization [CHAR LIMIT=NONE] -->
+    <string name="high_power_prompt_body">Let app
+        <xliff:g id="app_name" example="Settings">%1$s</xliff:g> stay connected in the
+        background?  This may use more battery.</string>
+
     <!-- Summary of power usage for an app [CHAR LIMIT=NONE] -->
     <string name="battery_summary"><xliff:g id="percentage" example="2">%1$d</xliff:g>%% use since last full charge</string>
 
     <!-- Description of how many more permissions to view on next page [CHAR LIMIT=30] -->
     <string name="additional_permissions_more"><xliff:g id="count" example="2">%1$d</xliff:g> more</string>
 
-    <!-- One of the choices in a dialog (with title defined in usb_use) that lets the user
+    <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for charging only. -->
-    <string name="usb_use_charging_only">Charging only</string>
-    <!-- One of the choices in a dialog (with title defined in usb_use) that lets the user
+    <string name="usb_use_charging_only">Charging</string>
+    <!-- Decription of one of the choices in a dialog (with title defined in usb_use) that lets the
+         user select what the USB connection for this device should be used for. This choice
+         is for charging only. -->
+    <string name="usb_use_charging_only_desc">Just charge this device</string>
+    <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
+         select what the USB connection for this device should be used for. This choice
+         is for powering the other device only. -->
+    <string name="usb_use_power_only">Power supply</string>
+    <!-- Decription of one of the choices in a dialog (with title defined in usb_use) that lets the
+         user select what the USB connection for this device should be used for. This choice
+         is for powering the other device only. -->
+    <string name="usb_use_power_only_desc">Charge the other connected device</string>
+    <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for transferring files via MTP. -->
-    <string name="usb_use_file_transfers">Transfer files (MTP)</string>
-    <!-- One of the choices in a dialog (with title defined in usb_use) that lets the user
+    <string name="usb_use_file_transfers">File transfers</string>
+    <!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
+         select what the USB connection for this device should be used for. This choice
+         is for transferring files via MTP. -->
+    <string name="usb_use_file_transfers_desc">Transfer files to Windows or Mac (MTP)</string>
+    <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
+         select what the USB connection for this device should be used for. This choice
+         is for transferring photos via PTP. -->
+    <string name="usb_use_photo_transfers">Photo transfer (PTP)</string>
+    <!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for transferring photos via PTP. -->
-    <string name="usb_use_photo_transfers">Transfer photos (PTP)</string>
-    <!-- One of the choices in a dialog (with title defined in usb_use) that lets the user
+    <string name="usb_use_photo_transfers_desc">Transfer photos or files if MTP is not supported (PTP)</string>
+    <!-- Title of one of the choices in a dialog (with title defined in usb_use) that lets the user
          select what the USB connection for this device should be used for. This choice
          is for entering MIDI mode. -->
     <string name="usb_use_MIDI">MIDI</string>
+    <!-- Description of one of the choices in a dialog (with title defined in usb_use) that lets the user
+         select what the USB connection for this device should be used for. This choice
+         is for entering MIDI mode. -->
+    <string name="usb_use_MIDI_desc">Use device for MIDI input</string>
     <!-- The title used in a dialog which lets the user select what the USB connection
          for this device should be used for. Choices are usb_use_charging_only,
          usb_use_file_transfer, use_use_photo_transfer, and usb_use_MIDI -->
     <string name="inactive_app_active_summary">Active. Touch to toggle.</string>
 
     <!-- Title for the "context" preference to determine whether assist can access the data currently displayed on-screen [CHAR LIMIT=40] -->
-    <string name="assist_access_context_title">Use current context</string>
+    <string name="assist_access_context_title">Use text from screen</string>
 
     <!-- Summary for the "context" preference to determine whether assist can access the data currently displayed on-screen [CHAR LIMIT=NONE] -->
-    <string name="assist_access_context_summary">Allow the assist app to access content and metadata from the app you\u2019re using</string>
+    <string name="assist_access_context_summary">Allow the assist app to access the screen contents as text</string>
 
     <!-- Title for the "screenshot" preference to determine whether assist can access the screenshot of your screen [CHAR LIMIT=40] -->
-    <string name="assist_access_screenshot_title">Use screenshots</string>
+    <string name="assist_access_screenshot_title">Use screenshot</string>
 
     <!-- Summary for the "screenshot" preference to determine whether assist can access the screenshot of your screen [CHAR LIMIT=NONE] -->
-    <string name="assist_access_screenshot_summary">Allow the assist app to analyze images of the app you\u2019re using</string>
+    <string name="assist_access_screenshot_summary">Allow the assist app to access an image of the screen</string>
 
 
     <!-- Footer text in the manage assist screen. [CHAR LIMIT=NONE] -->
-    <string name="assist_footer">Assist apps help you identify and act on useful information without having to ask. Some apps support both launcher and voice input services to give you integrated assistance.</string>
+    <string name="assist_footer">Assist apps can help you based on information from the screen you\u2019re viewing. Some apps support both launcher and voice input services to give you integrated assistance.</string>
 
     <!-- Label for average memory use section [CHAR LIMIT=30] -->
     <string name="average_memory_use">Average memory use</string>
     <string name="zen_access_warning_dialog_summary">The app will be able to turn on/off Do Not Disturb and make changes to related settings.</string>
 
     <!-- Ignore battery optimizations on label [CHAR LIMIT=30] -->
-    <string name="ignore_optimizations_on">Allow</string>
+    <string name="ignore_optimizations_on">Don\u2019t optimize</string>
 
     <!-- Ignore battery optimizations off label [CHAR LIMIT=30] -->
-    <string name="ignore_optimizations_off">Don\u2019t allow</string>
+    <string name="ignore_optimizations_off">Optimize</string>
 
     <!-- Ignore battery optimizations on description [CHAR LIMIT=NONE] -->
     <string name="ignore_optimizations_on_desc">May drain your battery more quickly</string>
 
     <!-- Number of characters used for lock screen text [CHAR LIMIT=NONE] -->
     <string name="accessibility_lock_screen_progress"><xliff:g id="count" example="1">%1$d</xliff:g> of <xliff:g id="count" example="1">%2$d</xliff:g> characters used</string>
+
+    <!-- System Alert Window settings -->
+    <!-- Title of Draw Overlay preference item [CHAR LIMIT=55] -->
+    <string name="draw_overlay_title">Apps that can draw over other apps</string>
+    <!-- Title of draw overlay screen [CHAR LIMIT=30] -->
+    <string name="draw_overlay">Draw over other apps</string>
+    <!-- Settings title in main settings screen for SYSTEM_ALERT_WINDOW [CHAR LIMIT=45] -->
+    <string name="system_alert_window_settings">Draw over other apps</string>
+    <!-- Title for the apps with SYSTEM_ALERT_WINDOW permission/privilege [CHAR LIMIT=20] -->
+    <string name="system_alert_window_apps_title">Apps</string>
+    <!-- Title for the apps that are allowed to draw on top of other apps [CHAR LIMIT=60] -->
+    <string name="system_alert_window_access_title">Draw over other apps</string>
+    <!-- Label for setting which controls whether app can draw overlays [CHAR LIMIT=45] -->
+    <string name="permit_draw_overlay">Permit drawing over other apps</string>
+    <!-- Link to the apps page for SYSTEM_ALERT_WINDOW settings [CHAR LIMIT=45] -->
+    <string name="app_overlay_permission_preference">App draw on top permission</string>
+    <!-- Description of allowing overlay setting [CHAR LIMIT=NONE] -->
+    <string name="allow_overlay_description">This permission allows an app to display on top of other apps you\u2019re using and may interfere with your use of the interface in other applications, or change what you think you are seeing in other applications.</string>
+
+    <!-- Keyword for SYSTEM_ALERT_WINDOW -->
+    <string name="keywords_system_alert_window">system alert window dialog draw on top other apps</string>
+    <!-- Main settings screen item's title to go into the overlay settings screen [CHAR LIMIT=30] -->
+    <string name="overlay_settings">Draw over other apps</string>
+
+    <!-- Summary of number of apps currently can draw overlays [CHAR LIMIT=60] -->
+    <string name="system_alert_window_summary"><xliff:g id="count" example="10">%d</xliff:g> of <xliff:g id="count" example="10">%d</xliff:g> apps allowed to draw on top of other apps</string>
+
+    <!-- Label for showing apps that can draw overlays [CHAR LIMIT=45] -->
+    <string name="filter_overlay_apps">Apps with permission</string>
+    <!-- Summary of app allowed to draw overlay [CHAR LIMIT=60] -->
+    <string name="system_alert_window_on">Yes</string>
+    <!-- Summary of app not allowed to draw overlay [CHAR LIMIT=60] -->
+    <string name="system_alert_window_off">No</string>
+
+
+    <!-- Write Settings settings -->
+    <!-- Settings title in main settings screen for WRITE_SETTINGS [CHAR LIMIT=30] -->
+    <string name="write_settings">Modify system settings</string>
+    <!-- Keyword for WRITE_SETTINGS -->
+    <string name="keywords_write_settings">write modify system settings</string>
+    <!-- Summary of number of apps currently can draw overlays [CHAR LIMIT=60] -->
+    <string name="write_settings_summary"><xliff:g id="count" example="10">%d</xliff:g> of <xliff:g id="count" example="10">%d</xliff:g> apps allowed to modify system settings</string>
+
+    <!-- Label for showing apps that can write system settings [CHAR LIMIT=45] -->
+    <string name="filter_write_settings_apps">Can modify system settings</string>
+    <!-- Title for the apps that are allowed to write system settings [CHAR LIMIT=60] -->
+    <string name="write_settings_title">Can modify system settings</string>
+    <!-- Main settings screen item's title to go into the write system settings screen [CHAR LIMIT=30] -->
+    <string name="write_system_settings">Modify system settings</string>
+    <!-- Link to the apps page for WRITE_SETTINGS settings [CHAR LIMIT=52] -->
+    <string name="write_settings_preference">App modify system settings permission</string>
+    <!-- Label for setting which controls whether app can write system settings [CHAR LIMIT=45] -->
+    <string name="permit_write_settings">Allow modify system settings</string>
+    <!-- Description of the write system settings [CHAR LIMIT=NONE] -->
+    <string name="write_settings_description">This permission allows an app to modify system settings.</string>
+    <!-- Summary of app allowed to write system settings [CHAR LIMIT=45] -->
+    <string name="write_settings_on">Yes</string>
+    <!-- Summary of app not allowed to write system settings [CHAR LIMIT=45] -->
+    <string name="write_settings_off">No</string>
+
+    <!-- Title of setting that controls gesture to open camera [CHAR LIMIT=40] -->
+    <string name="camera_gesture_title">Double twist for camera</string>
+
+    <!-- Description of setting that controls gesture to open camera [CHAR LIMIT=NONE] -->
+    <string name="camera_gesture_desc">Open the camera app by twisting your wrist twice</string>
+
 </resources>
index 0f820f0..387acf5 100644 (file)
         <item name="android:textColor">@color/warning</item>
     </style>
 
-    <style name="SetupWizardFingerprintStyle">
+    <style name="FingerprintLayoutTheme">
         <item name="suwBackground">@drawable/fp_enrollment_header</item>
         <item name="suwIllustrationAspectRatio">@dimen/fingerprint_illustration_aspect_ratio</item>
         <item name="suwDecorPaddingTop">@dimen/fingerprint_decor_padding_top</item>
     </style>
 
+    <style name="SetupWizardFingerprintLayoutTheme">
+        <item name="suwBackgroundTile">@drawable/setup_illustration_tile</item>
+        <item name="suwIllustration">@drawable/setup_illustration_lock_screen</item>
+        <item name="suwIllustrationHorizontalTile">@drawable/setup_illustration_horizontal_tile</item>
+    </style>
+
     <style name="TextAppearance.ConfirmDeviceCredentialsErrorText"
         parent="android:TextAppearance.Material.Body1">
         <item name="android:textColor">@color/warning</item>
index e6621a8..b1bf390 100644 (file)
@@ -15,6 +15,9 @@
 -->
 
 <resources>
+    <attr name="fingerprint_layout_theme" format="reference" />
+    <attr name="fingerprint_progress_bar_size" format="reference|dimension" />
+    <attr name="fingerprint_ring_radius" format="reference|dimension" />
     <attr name="ic_menu_add" format="reference" />
     <attr name="ic_menu_moreoverflow" format="reference" />
     <attr name="ic_wps" format="reference" />
@@ -36,6 +39,9 @@
         <item name="android:listPreferredItemPaddingStart">@dimen/suw_layout_margin_sides</item>
         <item name="android:windowBackground">?android:attr/colorBackground</item>
         <item name="@*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
+        <item name="fingerprint_layout_theme">@style/SetupWizardFingerprintLayoutTheme</item>
+        <item name="fingerprint_progress_bar_size">@dimen/setup_fingerprint_progress_bar_size</item>
+        <item name="fingerprint_ring_radius">@dimen/setup_fingerprint_ring_radius</item>
         <item name="ic_menu_add">@drawable/ic_menu_add_dark</item>
         <item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_material</item>
         <item name="ic_wps">@drawable/ic_wps_dark</item>
@@ -58,6 +64,9 @@
         <item name="android:listPreferredItemPaddingStart">@dimen/suw_layout_margin_sides</item>
         <item name="android:windowBackground">?android:attr/colorBackground</item>
         <item name="@*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
+        <item name="fingerprint_layout_theme">@style/SetupWizardFingerprintLayoutTheme</item>
+        <item name="fingerprint_progress_bar_size">@dimen/setup_fingerprint_progress_bar_size</item>
+        <item name="fingerprint_ring_radius">@dimen/setup_fingerprint_ring_radius</item>
         <item name="ic_menu_add">@drawable/ic_menu_add_light</item>
         <item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_material</item>
         <item name="ic_wps">@drawable/ic_wps_light</item>
@@ -90,6 +99,9 @@
 
         <item name="apnPreferenceStyle">@style/ApnPreference</item>
 
+        <item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
+        <item name="fingerprint_progress_bar_size">@dimen/fingerprint_progress_bar_size</item>
+        <item name="fingerprint_ring_radius">@dimen/fingerprint_ring_radius</item>
         <item name="ic_menu_add">@drawable/ic_menu_add_dark</item>
         <item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_holo_dark</item>
         <item name="ic_wps">@drawable/ic_wps_light</item>
index ccdcbf0..70b6856 100644 (file)
         settings:keywords="@string/keywords_default_apps" />
 
     <PreferenceScreen
+        android:key="system_alert_window"
+        android:title="@string/system_alert_window_settings"
+        android:fragment="com.android.settings.applications.ManageApplications"
+        settings:keywords="@string/keywords_system_alert_window">
+        <extra
+            android:name="classname"
+            android:value="com.android.settings.Settings$OverlaySettingsActivity" />
+    </PreferenceScreen>
+
+    <PreferenceScreen
+        android:key="write_settings_apps"
+        android:title="@string/write_settings"
+        android:fragment="com.android.settings.applications.ManageApplications"
+        settings:keywords="@string/keywords_write_settings">
+        <extra
+            android:name="classname"
+            android:value="com.android.settings.Settings$WriteSettingsActivity" />
+    </PreferenceScreen>
+
+    <PreferenceScreen
         android:key="high_power_apps"
         android:title="@string/high_power_apps"
         android:fragment="com.android.settings.applications.ManageApplications"
similarity index 75%
rename from res/xml/usage_access_details.xml
rename to res/xml/app_ops_permissions_details.xml
index d8b3bb1..c36f44e 100644 (file)
 -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
-    android:title="@string/usage_access">
+    xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
 
     <SwitchPreference
-        android:key="usage_switch"
-        android:title="@string/permit_usage_access" />
+        android:key="app_ops_settings_switch" />
 
     <Preference
-        android:key="app_usage_preference"
-        android:title="@string/app_usage_preference" />
+        android:key="app_ops_settings_preference" />
 
     <Preference
-        android:summary="@string/usage_access_description"
+        android:key="app_ops_settings_description"
         android:selectable="false" />
 
 </PreferenceScreen>
index 7c5c25c..1620164 100644 (file)
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                   android:title="@string/application_info_label">
 
-    <com.android.settings.DropDownPreference
-        android:key="app_location_setting"
+    <com.android.settings.applications.SpacePreference
+        android:key="storage_space"
+        android:layout_height="8dp" />
+
+    <Preference
+        android:key="storage_used"
         android:title="@string/storage_used"
         android:summary="@string/storage_type_internal"
-        android:selectable="true" />
+        android:selectable="false"
+        android:layout="@layout/horizontal_preference" />
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="change_storage_button"
+        android:layout="@layout/single_button_panel" />
 
     <PreferenceCategory
         android:key="storage_category"
index 14c829a..01f3ba5 100644 (file)
         android:summary="@string/runningservices_settings_summary"
         android:fragment="com.android.settings.applications.RunningServices" />
 
-    <SwitchPreference
-        android:key="tuner_ui"
-        android:persistent="false"
-        android:title="@string/system_ui_settings" />
-
     <PreferenceCategory android:key="debug_debugging_category"
             android:title="@string/debug_debugging_category">
 
index c3a3d52..358f3e1 100644 (file)
                 android:title="@string/firmware_version"
                 android:summary="@string/device_info_default"/>
 
+        <!-- Security patch level -->
+        <Preference android:key="security_patch"
+                style="?android:preferenceInformationStyle"
+                android:title="@string/security_patch"
+                android:summary="@string/device_info_default"/>
+
         <!-- Device FCC equipment id -->
         <Preference android:key="fcc_equipment_id"
                 style="?android:preferenceInformationStyle"
index e04d818..b5157e4 100644 (file)
                 android:entries="@array/screen_timeout_entries"
                 android:entryValues="@array/screen_timeout_values" />
 
+        <SwitchPreference
+                android:key="camera_gesture"
+                android:title="@string/camera_gesture_title"
+                android:summary="@string/camera_gesture_desc"
+                android:persistent="false" />
+
         <PreferenceScreen
                 android:key="screensaver"
                 android:title="@string/screensaver_settings_title"
index 470e292..7ebdaf7 100644 (file)
@@ -28,7 +28,7 @@
 
     <Preference
         android:key="data_settings"
-        android:title="@string/data_size_label"
+        android:title="@string/data_usage_summary_title"
         android:selectable="true" />
 
     <Preference
index 7d6642c..e48476f 100644 (file)
     <PreferenceCategory android:key="app_launch_domain_links"
                         android:title="@string/app_launch_domain_links_title">
 
-        <SwitchPreference
-                android:key="app_launch_open_domain_urls"
-                android:title="@string/app_launch_open_domain_urls_title"
-                android:summary="@string/app_launch_open_domain_urls_summary"
-                />
-
+        <com.android.settings.DropDownPreference
+                android:key="app_link_state"
+                android:persistent="false"
+                android:title="@string/app_launch_open_domain_urls_title" />
 
         <com.android.settings.applications.AppDomainsPreference
                 android:key="app_launch_supported_domain_urls"
                 android:title="@string/app_launch_supported_domain_urls_title"
                 android:persistent="false"
-                android:dependency="app_launch_open_domain_urls"
+                android:dependency="app_link_state"
                 />
 
     </PreferenceCategory>
index 72a189d..43fd116 100644 (file)
         <SwitchPreference
                 android:key="notification_pulse"
                 android:title="@string/notification_pulse_title"
-                android:switchTextOff=""
-                android:switchTextOn=""
                 android:persistent="false" />
 
          <!-- When device is locked -->
index 57a302b..cc88c3f 100644 (file)
     <SwitchPreference
             android:key="dial_pad_tones"
             android:title="@string/dial_pad_tones_title"
-            android:switchTextOff=""
-            android:switchTextOn=""
             android:persistent="false" />
 
     <!-- Screen locking sounds -->
     <SwitchPreference
             android:key="screen_locking_sounds"
             android:title="@string/screen_locking_sounds_title"
-            android:switchTextOff=""
-            android:switchTextOn=""
             android:persistent="false" />
 
     <!-- Charging sounds -->
     <SwitchPreference
             android:key="charging_sounds"
             android:title="@string/charging_sounds_title"
-            android:switchTextOff=""
-            android:switchTextOn=""
             android:persistent="false" />
 
     <!-- Docking sounds -->
     <SwitchPreference
             android:key="docking_sounds"
             android:title="@string/docking_sounds_title"
-            android:switchTextOff=""
-            android:switchTextOn=""
             android:persistent="false" />
 
     <!-- Touch sounds -->
     <SwitchPreference
             android:key="touch_sounds"
             android:title="@string/touch_sounds_title"
-            android:switchTextOff=""
-            android:switchTextOn=""
             android:persistent="false" />
 
     <!-- Vibrate on touch -->
     <SwitchPreference
             android:key="vibrate_on_touch"
             android:title="@string/vibrate_on_touch_title"
-            android:switchTextOff=""
-            android:switchTextOn=""
             android:persistent="false" />
 
     <!-- Dock speaker plays -->
index bfe0871..6e324ce 100644 (file)
         android:title="@string/zen_mode_alarms"
         android:enabled="false"
         android:defaultValue="true"
-        android:persistent="false"
-        android:switchTextOff=""
-        android:switchTextOn="" />
+        android:persistent="false"/>
 
     <!-- Reminders -->
     <SwitchPreference
         android:key="reminders"
         android:title="@string/zen_mode_reminders"
-        android:persistent="false"
-        android:switchTextOff=""
-        android:switchTextOn="" />
+        android:persistent="false"/>
 
     <!-- Events -->
     <SwitchPreference
         android:key="events"
         android:title="@string/zen_mode_events"
-        android:persistent="false"
-        android:switchTextOff=""
-        android:switchTextOn="" />
+        android:persistent="false"/>
 
     <!-- Messages -->
     <com.android.settings.DropDownPreference
@@ -61,8 +55,6 @@
     <SwitchPreference
         android:key="repeat_callers"
         android:title="@string/zen_mode_repeat_callers"
-        android:persistent="false"
-        android:switchTextOff=""
-        android:switchTextOn="" />
+        android:persistent="false"/>
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/AccessiblePreferenceCategory.java b/src/com/android/settings/AccessiblePreferenceCategory.java
new file mode 100644 (file)
index 0000000..7784c16
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.preference.PreferenceCategory;
+import android.view.View;
+
+/**
+ * Preference category that accepts a content description for accessibility.
+ */
+public class AccessiblePreferenceCategory extends PreferenceCategory {
+    private String mContentDescription;
+
+    public AccessiblePreferenceCategory(Context context) {
+        super(context);
+    }
+
+    public void setContentDescription(String contentDescription) {
+        mContentDescription = contentDescription;
+    }
+
+    @Override
+    protected void onBindView(View view) {
+        super.onBindView(view);
+
+        view.setContentDescription(mContentDescription);
+    }
+}
index 7142fb6..8299c67 100644 (file)
@@ -758,7 +758,7 @@ public class ApnEditor extends InstrumentedPreferenceActivity
         if (pref != null) {
             if (pref.equals(mPassword)){
                 pref.setSummary(starify(sharedPreferences.getString(key, "")));
-            } else if (pref.equals(mCarrierEnabled)) {
+            } else if (pref.equals(mCarrierEnabled) || pref.equals(mBearerMulti)) {
                 // do nothing
             } else {
                 pref.setSummary(checkNull(sharedPreferences.getString(key, "")));
index 317d372..94a9de5 100644 (file)
@@ -38,7 +38,10 @@ import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintManager.RemovalCallback;
 import android.util.EventLog;
 import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.ListView;
 import android.widget.Toast;
 
 import com.android.internal.logging.MetricsLogger;
@@ -97,6 +100,7 @@ public class ChooseLockGeneric extends SettingsActivity {
         private int mEncryptionRequestQuality;
         private boolean mEncryptionRequestDisabled;
         private boolean mRequirePassword;
+        private boolean mForFingerprint = false;
         private String mUserPassword;
         private LockPatternUtils mLockPatternUtils;
         private FingerprintManager mFingerprintManager;
@@ -105,11 +109,18 @@ public class ChooseLockGeneric extends SettingsActivity {
             @Override
             public void onRemovalSucceeded(Fingerprint fingerprint) {
                 Log.v(TAG, "Fingerprint removed: " + fingerprint.getFingerId());
+                if (mFingerprintManager.getEnrolledFingerprints().size() == 0) {
+                    finish();
+                }
             }
 
             @Override
             public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) {
-                Toast.makeText(getActivity(), errString, Toast.LENGTH_SHORT);
+                Activity activity = getActivity();
+                if (activity != null) {
+                    Toast.makeText(getActivity(), errString, Toast.LENGTH_SHORT);
+                }
+                finish();
             }
         };
 
@@ -140,6 +151,8 @@ public class ChooseLockGeneric extends SettingsActivity {
                     ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
             mChallenge = getActivity().getIntent().getLongExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
+            mForFingerprint = getActivity().getIntent().getBooleanExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
 
             if (savedInstanceState != null) {
                 mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
@@ -165,6 +178,18 @@ public class ChooseLockGeneric extends SettingsActivity {
         }
 
         @Override
+        public void onViewCreated(View view, Bundle savedInstanceState) {
+            super.onViewCreated(view, savedInstanceState);
+            if (mForFingerprint) {
+                final LayoutInflater inflater = LayoutInflater.from(getContext());
+                final ListView listView = getListView();
+                final View fingerprintHeader = inflater.inflate(
+                        R.layout.choose_lock_generic_fingerprint_header, listView, false);
+                listView.addHeaderView(fingerprintHeader, null, false);
+            }
+        }
+
+        @Override
         public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
                 Preference preference) {
             final String key = preference.getKey();
@@ -201,6 +226,8 @@ public class ChooseLockGeneric extends SettingsActivity {
                 final boolean accEn = AccessibilityManager.getInstance(context).isEnabled();
                 final boolean required = mLockPatternUtils.isCredentialRequiredToDecrypt(!accEn);
                 Intent intent = getEncryptionInterstitialIntent(context, quality, required);
+                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT,
+                        mForFingerprint);
                 startActivityForResult(intent, ENABLE_ENCRYPTION_REQUEST);
             } else {
                 mRequirePassword = false; // device encryption not enabled or not device owner.
@@ -470,18 +497,19 @@ public class ChooseLockGeneric extends SettingsActivity {
                 mChooseLockSettingsHelper.utils().clearLock(UserHandle.myUserId());
                 mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled,
                         UserHandle.myUserId());
-                removeAllFingerprintTemplates();
+                removeAllFingerprintTemplatesAndFinish();
                 getActivity().setResult(Activity.RESULT_OK);
-                finish();
             } else {
-                removeAllFingerprintTemplates();
-                finish();
+                removeAllFingerprintTemplatesAndFinish();
             }
         }
 
-        private void removeAllFingerprintTemplates() {
-            if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
+        private void removeAllFingerprintTemplatesAndFinish() {
+            if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()
+                    && mFingerprintManager.getEnrolledFingerprints().size() > 0) {
                 mFingerprintManager.remove(new Fingerprint(null, 0, 0, 0), mRemovalCallback);
+            } else {
+                finish();
             }
         }
 
@@ -495,19 +523,28 @@ public class ChooseLockGeneric extends SettingsActivity {
             return R.string.help_url_choose_lockscreen;
         }
 
-        private int getResIdForFactoryResetProtectionWarningTitle() {
+        private int getResIdForFactoryResetProtectionWarningMessage() {
+            boolean hasFingerprints = mFingerprintManager.hasEnrolledFingerprints();
             switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(UserHandle.myUserId())) {
                 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
-                    return R.string.unlock_disable_lock_pattern_summary;
+                    return hasFingerprints
+                            ? R.string.unlock_disable_frp_warning_content_pattern_fingerprint
+                            : R.string.unlock_disable_frp_warning_content_pattern;
                 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
                 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
-                    return R.string.unlock_disable_lock_pin_summary;
+                    return hasFingerprints
+                            ? R.string.unlock_disable_frp_warning_content_pin_fingerprint
+                            : R.string.unlock_disable_frp_warning_content_pin;
                 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
                 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
                 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
-                    return R.string.unlock_disable_lock_password_summary;
+                    return hasFingerprints
+                            ? R.string.unlock_disable_frp_warning_content_password_fingerprint
+                            : R.string.unlock_disable_frp_warning_content_password;
                 default:
-                    return R.string.unlock_disable_lock_unknown_summary;
+                    return hasFingerprints
+                            ? R.string.unlock_disable_frp_warning_content_unknown_fingerprint
+                            : R.string.unlock_disable_frp_warning_content_unknown;
             }
         }
 
@@ -542,23 +579,23 @@ public class ChooseLockGeneric extends SettingsActivity {
         }
 
         private void showFactoryResetProtectionWarningDialog(String unlockMethodToSet) {
-            int title = getResIdForFactoryResetProtectionWarningTitle();
+            int message = getResIdForFactoryResetProtectionWarningMessage();
             FactoryResetProtectionWarningDialog dialog =
-                    FactoryResetProtectionWarningDialog.newInstance(title, unlockMethodToSet);
+                    FactoryResetProtectionWarningDialog.newInstance(message, unlockMethodToSet);
             dialog.show(getChildFragmentManager(), TAG_FRP_WARNING_DIALOG);
         }
 
         public static class FactoryResetProtectionWarningDialog extends DialogFragment {
 
-            private static final String ARG_TITLE_RES = "titleRes";
+            private static final String ARG_MESSAGE_RES = "messageRes";
             private static final String ARG_UNLOCK_METHOD_TO_SET = "unlockMethodToSet";
 
-            public static FactoryResetProtectionWarningDialog newInstance(int title,
+            public static FactoryResetProtectionWarningDialog newInstance(int messageRes,
                     String unlockMethodToSet) {
                 FactoryResetProtectionWarningDialog frag =
                         new FactoryResetProtectionWarningDialog();
                 Bundle args = new Bundle();
-                args.putInt(ARG_TITLE_RES, title);
+                args.putInt(ARG_MESSAGE_RES, messageRes);
                 args.putString(ARG_UNLOCK_METHOD_TO_SET, unlockMethodToSet);
                 frag.setArguments(args);
                 return frag;
@@ -577,9 +614,9 @@ public class ChooseLockGeneric extends SettingsActivity {
                 final Bundle args = getArguments();
 
                 return new AlertDialog.Builder(getActivity())
-                        .setTitle(args.getInt(ARG_TITLE_RES))
-                        .setMessage(R.string.unlock_disable_frp_warning_content)
-                        .setPositiveButton(R.string.okay,
+                        .setTitle(R.string.unlock_disable_frp_warning_title)
+                        .setMessage(args.getInt(ARG_MESSAGE_RES))
+                        .setPositiveButton(R.string.unlock_disable_frp_warning_ok,
                                 new DialogInterface.OnClickListener() {
                                     @Override
                                     public void onClick(DialogInterface dialog, int whichButton) {
index 64aaaca..bc370c9 100644 (file)
@@ -128,6 +128,7 @@ public class ChooseLockPassword extends SettingsActivity {
         private static final String KEY_CURRENT_PASSWORD = "current_password";
 
         private String mCurrentPassword;
+        private String mChosenPassword;
         private boolean mHasChallenge;
         private long mChallenge;
         private TextView mPasswordEntry;
@@ -145,6 +146,9 @@ public class ChooseLockPassword extends SettingsActivity {
         private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
         private ChooseLockSettingsHelper mChooseLockSettingsHelper;
         private Stage mUiStage = Stage.Introduction;
+
+        // True once we have confirmed new PIN/password to prevent virtual keyboard
+        // re-entries of the same PIN
         private boolean mDone = false;
         private TextView mHeaderText;
         private String mFirstPin;
@@ -478,37 +482,56 @@ public class ChooseLockPassword extends SettingsActivity {
             return null;
         }
 
+        private class SaveChosenPasswordAndFinish extends AsyncTask<Void, Void, Void> {
+            boolean mWasSecureBefore;
+
+            @Override
+            public void onPreExecute() {
+                mWasSecureBefore = mLockPatternUtils.isSecure(UserHandle.myUserId());
+                final boolean required = getActivity().getIntent().getBooleanExtra(
+                        EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
+                mLockPatternUtils.setCredentialRequiredToDecrypt(required);
+            }
+
+            @Override
+            public Void doInBackground(Void... v) {
+                mLockPatternUtils.saveLockPassword(mChosenPassword, mCurrentPassword, mRequestedQuality,
+                                                   UserHandle.myUserId());
+                return null;
+            }
+
+            @Override
+            public void onPostExecute(Void v) {
+                if (mHasChallenge) {
+                    startVerifyPassword(mChosenPassword, mWasSecureBefore);
+                    return;
+                } else {
+                    getActivity().setResult(RESULT_FINISHED);
+                }
+                finishConfirmStage(mWasSecureBefore);
+            }
+        }
+
+
         public void handleNext() {
             if (mDone) return;
-
-            final String pin = mPasswordEntry.getText().toString();
-            if (TextUtils.isEmpty(pin)) {
+            mChosenPassword = mPasswordEntry.getText().toString();
+            if (TextUtils.isEmpty(mChosenPassword)) {
                 return;
             }
             String errorMsg = null;
             if (mUiStage == Stage.Introduction) {
-                errorMsg = validatePassword(pin);
+                errorMsg = validatePassword(mChosenPassword);
                 if (errorMsg == null) {
-                    mFirstPin = pin;
+                    mFirstPin = mChosenPassword;
                     mPasswordEntry.setText("");
                     updateStage(Stage.NeedToConfirm);
                 }
             } else if (mUiStage == Stage.NeedToConfirm) {
-                if (mFirstPin.equals(pin)) {
-                    boolean wasSecureBefore = mLockPatternUtils.isSecure(UserHandle.myUserId());
-                    final boolean required = getActivity().getIntent().getBooleanExtra(
-                            EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
-                    mLockPatternUtils.setCredentialRequiredToDecrypt(required);
-                    mLockPatternUtils.saveLockPassword(pin, mCurrentPassword, mRequestedQuality,
-                            UserHandle.myUserId());
-
-                    if (mHasChallenge) {
-                        startVerifyPassword(pin, wasSecureBefore);
-                        return;
-                    } else {
-                        getActivity().setResult(RESULT_FINISHED);
-                    }
-                    finishConfirmStage(wasSecureBefore);
+                if (mFirstPin.equals(mChosenPassword)) {
+                    setNextEnabled(false);
+                    mDone = true;
+                    new SaveChosenPasswordAndFinish().execute();
                 } else {
                     CharSequence tmp = mPasswordEntry.getText();
                     if (tmp != null) {
@@ -557,7 +580,6 @@ public class ChooseLockPassword extends SettingsActivity {
 
         private void finishConfirmStage(boolean wasSecureBefore) {
             getActivity().finish();
-            mDone = true;
             if (!wasSecureBefore) {
                 Intent intent = getRedactionInterstitialIntent(getActivity());
                 if (intent != null) {
index 087a23e..026fd46 100644 (file)
@@ -346,7 +346,6 @@ public class ChooseLockPattern extends SettingsActivity {
         }
 
         private Stage mUiStage = Stage.Introduction;
-        private boolean mDone = false;
 
         private Runnable mClearPatternRunnable = new Runnable() {
             public void run() {
@@ -435,7 +434,6 @@ public class ChooseLockPattern extends SettingsActivity {
                 }
                 updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
             }
-            mDone = false;
         }
 
         @Override
@@ -483,7 +481,7 @@ public class ChooseLockPattern extends SettingsActivity {
                     throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed
                             + " when button is " + RightButtonMode.Confirm);
                 }
-                saveChosenPatternAndFinish();
+                new SaveChosenPatternAndFinish().execute();
             } else if (mUiStage.rightMode == RightButtonMode.Ok) {
                 if (mUiStage != Stage.HelpScreen) {
                     throw new IllegalStateException("Help screen is only mode with ok button, "
@@ -623,34 +621,49 @@ public class ChooseLockPattern extends SettingsActivity {
             mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
         }
 
-        private void saveChosenPatternAndFinish() {
-            if (mDone) return;
-            LockPatternUtils utils = mChooseLockSettingsHelper.utils();
-            final boolean lockVirgin = !utils.isPatternEverChosen(UserHandle.myUserId());
+        private class SaveChosenPatternAndFinish extends AsyncTask<Void,Void,Void> {
+            boolean mLockVirgin;
+            LockPatternUtils mUtils;
+            boolean mWasSecureBefore;
 
-            boolean wasSecureBefore = utils.isSecure(UserHandle.myUserId());
+            @Override
+            protected void onPreExecute(){
+                setRightButtonEnabled(false);
+                mUtils = mChooseLockSettingsHelper.utils();
+                mLockVirgin = !mUtils.isPatternEverChosen(UserHandle.myUserId());
 
-            final boolean required = getActivity().getIntent().getBooleanExtra(
+                mWasSecureBefore = mUtils.isSecure(UserHandle.myUserId());
+
+                final boolean required = getActivity().getIntent().getBooleanExtra(
                     EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
 
-            utils.setCredentialRequiredToDecrypt(required);
-            utils.saveLockPattern(mChosenPattern, mCurrentPattern, UserHandle.myUserId());
+                mUtils.setCredentialRequiredToDecrypt(required);
+            }
 
-            if (lockVirgin) {
-                utils.setVisiblePatternEnabled(true, UserHandle.myUserId());
+            @Override
+            protected Void doInBackground(Void... params){
+                mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, UserHandle.myUserId());
+                return null;
             }
 
-            if (mHasChallenge) {
-                startVerifyPattern(utils, wasSecureBefore);
-            } else {
-                if (!wasSecureBefore) {
-                    Intent intent = getRedactionInterstitialIntent(getActivity());
-                    if (intent != null) {
-                        startActivity(intent);
+            @Override
+            protected void onPostExecute(Void param) {
+                if (mLockVirgin) {
+                    mUtils.setVisiblePatternEnabled(true, UserHandle.myUserId());
+                }
+
+                if (mHasChallenge) {
+                    startVerifyPattern(mUtils, mWasSecureBefore);
+                } else {
+                    if (!mWasSecureBefore) {
+                        Intent intent = getRedactionInterstitialIntent(getActivity());
+                        if (intent != null) {
+                            startActivity(intent);
+                        }
                     }
+                    getActivity().setResult(RESULT_FINISHED);
+                    doFinish();
                 }
-                getActivity().setResult(RESULT_FINISHED);
-                doFinish();
             }
         }
 
@@ -693,7 +706,6 @@ public class ChooseLockPattern extends SettingsActivity {
 
         private void doFinish() {
             getActivity().finish();
-            mDone = true;
         }
     }
 }
index 8b8d976..327e622 100644 (file)
@@ -32,6 +32,7 @@ public final class ChooseLockSettingsHelper {
     public static final String EXTRA_KEY_HAS_CHALLENGE = "has_challenge";
     public static final String EXTRA_KEY_CHALLENGE = "challenge";
     public static final String EXTRA_KEY_CHALLENGE_TOKEN = "hw_auth_token";
+    public static final String EXTRA_KEY_FOR_FINGERPRINT = "for_fingerprint";
 
 
     private LockPatternUtils mLockPatternUtils;
@@ -117,7 +118,10 @@ public final class ChooseLockSettingsHelper {
             boolean returnCredentials, boolean external, boolean hasChallenge,
             long challenge) {
         boolean launched = false;
-        switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(UserHandle.myUserId())) {
+
+        int effectiveUserId = Utils.getEffectiveUserId(mActivity);
+
+        switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(effectiveUserId)) {
             case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
                 launched = launchConfirmationActivity(request, title, header, description,
                         returnCredentials || hasChallenge
index 7653603..da39a0f 100644 (file)
@@ -58,12 +58,15 @@ public class ConfirmDeviceCredentialActivity extends Activity {
         String title = intent.getStringExtra(KeyguardManager.EXTRA_TITLE);
         String details = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
 
-        ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this);
-        if (!helper.launchConfirmationActivity(0 /* request code */, null /* title */, title,
-                details, false /* returnCredentials */, true /* isExternal */)) {
-            Log.d(TAG, "No pattern, password or PIN set.");
-            setResult(Activity.RESULT_OK);
-            finish();
+        // Ignore rotates and ensure we only launch this once
+        if (savedInstanceState == null) {
+            ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this);
+            if (!helper.launchConfirmationActivity(0 /* request code */, null /* title */, title,
+                    details, false /* returnCredentials */, true /* isExternal */)) {
+                Log.d(TAG, "No pattern, password or PIN set.");
+                setResult(Activity.RESULT_OK);
+                finish();
+            }
         }
     }
 
index f56c315..176efbc 100644 (file)
@@ -27,6 +27,7 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
     private boolean mRestoring;
     private boolean mDark;
     private boolean mEnterAnimationPending;
+    private boolean mFirstTimeVisible = true;
 
     @Override
     protected void onCreate(Bundle savedState) {
@@ -62,7 +63,8 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
     @Override
     public void onResume() {
         super.onResume();
-        if (!isChangingConfigurations() && !mRestoring && mDark) {
+        if (!isChangingConfigurations() && !mRestoring && mDark && mFirstTimeVisible) {
+            mFirstTimeVisible = false;
             prepareEnterAnimation();
             mEnterAnimationPending = true;
         }
@@ -81,6 +83,7 @@ public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivi
         super.onEnterAnimationComplete();
         if (mEnterAnimationPending) {
             startEnterAnimation();
+            mEnterAnimationPending = false;
         }
     }
 
index a8b5f2d..32ad317 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.settings;
 
 import android.annotation.Nullable;
+import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
 import android.widget.Button;
@@ -82,6 +83,20 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
         }
     }
 
+    protected void setAccessibilityTitle(CharSequence suplementalText) {
+        Intent intent = getActivity().getIntent();
+        if (intent != null) {
+            CharSequence titleText = intent.getCharSequenceExtra(
+                    ConfirmDeviceCredentialBaseFragment.TITLE_TEXT);
+            if (titleText == null || suplementalText == null) {
+                return;
+            }
+            String accessibilityTitle =
+                    new StringBuilder(titleText).append(",").append(suplementalText).toString();
+            getActivity().setTitle(Utils.createAccessibleSequence(titleText, accessibilityTitle));
+        }
+    }
+
     @Override
     public void onPause() {
         super.onPause();
index 7761807..1c42045 100644 (file)
@@ -28,8 +28,8 @@ import com.android.settingslib.animation.DisappearAnimationUtils;
 
 import android.app.Fragment;
 import android.app.admin.DevicePolicyManager;
-import android.content.Context;
 import android.content.Intent;
+import android.content.Context;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.CountDownTimer;
@@ -98,6 +98,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
         private AppearAnimationUtils mAppearAnimationUtils;
         private DisappearAnimationUtils mDisappearAnimationUtils;
         private boolean mBlockImm;
+        private int mEffectiveUserId;
 
         // required constructor for fragments
         public ConfirmLockPasswordFragment() {
@@ -108,6 +109,8 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             mLockPatternUtils = new LockPatternUtils(getActivity());
+            mEffectiveUserId = Utils.getEffectiveUserId(getActivity());
+
             if (savedInstanceState != null) {
                 mNumWrongConfirmAttempts = savedInstanceState.getInt(
                         KEY_NUM_WRONG_CONFIRM_ATTEMPTS, 0);
@@ -118,7 +121,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
             final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
-                    UserHandle.myUserId());
+                    mEffectiveUserId);
             View view = inflater.inflate(R.layout.confirm_lock_password, null);
 
             mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
@@ -161,6 +164,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
                     110, 1f /* translationScale */,
                     0.5f /* delayScale */, AnimationUtils.loadInterpolator(
                             getContext(), android.R.interpolator.fast_out_linear_in));
+            setAccessibilityTitle(mHeaderTextView.getText());
             return view;
         }
 
@@ -237,7 +241,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
         @Override
         public void onResume() {
             super.onResume();
-            long deadline = mLockPatternUtils.getLockoutAttemptDeadline(UserHandle.myUserId());
+            long deadline = mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId);
             if (deadline != 0) {
                 handleAttemptLockout(deadline);
             } else {
@@ -313,7 +317,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
                 return;
             }
 
-            onPasswordChecked(false, intent, 0);
+            onPasswordChecked(false, intent, 0, mEffectiveUserId);
         }
 
         private boolean isInternalActivity() {
@@ -323,11 +327,12 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
         private void startVerifyPassword(final String pin, final Intent intent) {
             long challenge = getActivity().getIntent().getLongExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
+            final int localEffectiveUserId = mEffectiveUserId;
             mPendingLockCheck = LockPatternChecker.verifyPassword(
                     mLockPatternUtils,
                     pin,
                     challenge,
-                    UserHandle.myUserId(),
+                    localEffectiveUserId,
                     new LockPatternChecker.OnVerifyCallback() {
                         @Override
                         public void onVerified(byte[] token, int timeoutMs) {
@@ -339,16 +344,17 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
                                         ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
                                         token);
                             }
-                            onPasswordChecked(matched, intent, timeoutMs);
+                            onPasswordChecked(matched, intent, timeoutMs, localEffectiveUserId);
                         }
                     });
         }
 
         private void startCheckPassword(final String pin, final Intent intent) {
+            final int localEffectiveUserId = mEffectiveUserId;
             mPendingLockCheck = LockPatternChecker.checkPassword(
                     mLockPatternUtils,
                     pin,
-                    UserHandle.myUserId(),
+                    localEffectiveUserId,
                     new LockPatternChecker.OnCheckCallback() {
                         @Override
                         public void onChecked(boolean matched, int timeoutMs) {
@@ -360,7 +366,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
                                 intent.putExtra(
                                         ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
                             }
-                            onPasswordChecked(matched, intent, timeoutMs);
+                            onPasswordChecked(matched, intent, timeoutMs, localEffectiveUserId);
                         }
                     });
         }
@@ -383,14 +389,15 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
             }
         }
 
-        private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs) {
+        private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs,
+                int effectiveUserId) {
             mPasswordEntryInputDisabler.setInputEnabled(true);
             if (matched) {
                 startDisappearAnimation(intent);
             } else {
                 if (timeoutMs > 0) {
                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
-                            UserHandle.myUserId(), timeoutMs);
+                            effectiveUserId, timeoutMs);
                     handleAttemptLockout(deadline);
                 } else {
                     showError(getErrorMessage());
index 1737635..51cde60 100644 (file)
@@ -26,24 +26,14 @@ import com.android.settingslib.animation.AppearAnimationCreator;
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.settingslib.animation.DisappearAnimationUtils;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
 import android.app.Activity;
-import android.app.Fragment;
 import android.content.Intent;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
 import android.os.CountDownTimer;
 import android.os.SystemClock;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.view.MenuItem;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
@@ -52,7 +42,6 @@ import android.view.View;
 import android.view.ViewGroup;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -113,6 +102,8 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
         private AppearAnimationUtils mAppearAnimationUtils;
         private DisappearAnimationUtils mDisappearAnimationUtils;
 
+        private int mEffectiveUserId;
+
         // required constructor for fragments
         public ConfirmLockPatternFragment() {
 
@@ -122,6 +113,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             mLockPatternUtils = new LockPatternUtils(getActivity());
+            mEffectiveUserId = Utils.getEffectiveUserId(getActivity());
         }
 
         @Override
@@ -149,7 +141,10 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
                         ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
             }
 
-            mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+            mLockPatternView.setTactileFeedbackEnabled(
+                    mLockPatternUtils.isTactileFeedbackEnabled());
+            mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+                    mEffectiveUserId));
             mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
             updateStage(Stage.NeedToUnlock);
 
@@ -159,7 +154,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
                 // on first launch, if no lock pattern is set, then finish with
                 // success (don't want user to get stuck confirming something that
                 // doesn't exist).
-                if (!mLockPatternUtils.isLockPatternEnabled(UserHandle.myUserId())) {
+                if (!mLockPatternUtils.isLockPatternEnabled(mEffectiveUserId)) {
                     getActivity().setResult(Activity.RESULT_OK);
                     getActivity().finish();
                 }
@@ -178,6 +173,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
                             return (float)(numRows - row) / numRows;
                         }
                     });
+            setAccessibilityTitle(mHeaderTextView.getText());
             return view;
         }
 
@@ -210,7 +206,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
             super.onResume();
 
             // if the user is currently locked out, enforce it.
-            long deadline = mLockPatternUtils.getLockoutAttemptDeadline(UserHandle.myUserId());
+            long deadline = mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId);
             if (deadline != 0) {
                 handleAttemptLockout(deadline);
             } else if (!mLockPatternView.isEnabled()) {
@@ -392,7 +388,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
                     return;
                 }
 
-                onPatternChecked(pattern, false, intent, 0);
+                onPatternChecked(pattern, false, intent, 0, mEffectiveUserId);
             }
 
             private boolean isInternalActivity() {
@@ -401,13 +397,14 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
 
             private void startVerifyPattern(final List<LockPatternView.Cell> pattern,
                     final Intent intent) {
+                final int localEffectiveUserId = mEffectiveUserId;
                 long challenge = getActivity().getIntent().getLongExtra(
                         ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
                 mPendingLockCheck = LockPatternChecker.verifyPattern(
                         mLockPatternUtils,
                         pattern,
                         challenge,
-                        UserHandle.myUserId(),
+                        localEffectiveUserId,
                         new LockPatternChecker.OnVerifyCallback() {
                             @Override
                             public void onVerified(byte[] token, int timeoutMs) {
@@ -419,7 +416,8 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
                                             ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
                                             token);
                                 }
-                                onPatternChecked(pattern, matched, intent, timeoutMs);
+                                onPatternChecked(pattern,
+                                        matched, intent, timeoutMs, localEffectiveUserId);
                             }
                         });
             }
@@ -427,14 +425,15 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
             private void startCheckPattern(final List<LockPatternView.Cell> pattern,
                     final Intent intent) {
                 if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
-                    onPatternChecked(pattern, false, intent, 0);
+                    onPatternChecked(pattern, false, intent, 0, mEffectiveUserId);
                     return;
                 }
 
+                final int localEffectiveUserId = mEffectiveUserId;
                 mPendingLockCheck = LockPatternChecker.checkPattern(
                         mLockPatternUtils,
                         pattern,
-                        UserHandle.myUserId(),
+                        localEffectiveUserId,
                         new LockPatternChecker.OnCheckCallback() {
                             @Override
                             public void onChecked(boolean matched, int timeoutMs) {
@@ -445,20 +444,21 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
                                     intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
                                                     LockPatternUtils.patternToString(pattern));
                                 }
-                                onPatternChecked(pattern, matched, intent, timeoutMs);
+                                onPatternChecked(pattern, matched, intent, timeoutMs,
+                                        localEffectiveUserId);
                             }
                         });
             }
 
             private void onPatternChecked(List<LockPatternView.Cell> pattern,
-                    boolean matched, Intent intent, int timeoutMs) {
+                    boolean matched, Intent intent, int timeoutMs, int effectiveUserId) {
                 mLockPatternView.setEnabled(true);
                 if (matched) {
                     startDisappearAnimation(intent);
                 } else {
                     if (timeoutMs > 0) {
                         long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
-                                UserHandle.myUserId(), timeoutMs);
+                                effectiveUserId, timeoutMs);
                         handleAttemptLockout(deadline);
                     } else {
                         updateStage(Stage.NeedToUnlockWrong);
@@ -498,40 +498,12 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
                 final Runnable finishListener) {
             if (obj instanceof LockPatternView.CellState) {
                 final LockPatternView.CellState animatedCell = (LockPatternView.CellState) obj;
-                if (appearing) {
-                    animatedCell.scale = 0.0f;
-                    animatedCell.alpha = 1.0f;
-                }
-                animatedCell.translateY = appearing ? translationY : 0;
-                ValueAnimator animator = ValueAnimator.ofFloat(animatedCell.translateY,
-                        appearing ? 0 : translationY);
-                animator.setInterpolator(interpolator);
-                animator.setDuration(duration);
-                animator.setStartDelay(delay);
-                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        float animatedFraction = animation.getAnimatedFraction();
-                        if (appearing) {
-                            animatedCell.scale = animatedFraction;
-                        } else {
-                            animatedCell.alpha = 1 - animatedFraction;
-                        }
-                        animatedCell.translateY = (float) animation.getAnimatedValue();
-                        mLockPatternView.invalidate();
-                    }
-                });
-                if (finishListener != null) {
-                    animator.addListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            finishListener.run();
-                        }
-                    });
-                }
-
-                animator.start();
-                mLockPatternView.invalidate();
+                mLockPatternView.startCellStateAnimation(animatedCell,
+                        1f, appearing ? 1f : 0f, /* alpha */
+                        appearing ? translationY : 0f, /* startTranslation */
+                        appearing ? 0f : translationY, /* endTranslation */
+                        appearing ? 0f : 1f, 1f /* scale */,
+                        delay, duration, interpolator, finishListener);
             } else {
                 mAppearAnimationUtils.createAnimation((View) obj, delay, duration, translationY,
                         appearing, interpolator, finishListener);
index a12369b..18b4ac8 100644 (file)
@@ -22,6 +22,7 @@ import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.os.AsyncTask;
@@ -417,8 +418,10 @@ public final class CredentialStorage extends Activity {
      */
     private boolean checkCallerIsCertInstallerOrSelfInProfile() {
         if (TextUtils.equals("com.android.certinstaller", getCallingPackage())) {
-            // CertInstaller is allowed to install credentials
-            return true;
+            // CertInstaller is allowed to install credentials if it has the same signature as
+            // Settings package.
+            return getPackageManager().checkSignatures(
+                    getCallingPackage(), getPackageName()) == PackageManager.SIGNATURE_MATCH;
         }
 
         final int launchedFromUserId;
index 7a5a60a..387331b 100644 (file)
@@ -81,8 +81,6 @@ import android.net.TrafficStats;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.INetworkManagementService;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -98,7 +96,6 @@ import android.text.format.Formatter;
 import android.text.format.Time;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -131,19 +128,20 @@ import android.widget.Toast;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.settings.drawable.InsetBoundsDrawable;
-import com.android.settings.net.ChartData;
-import com.android.settings.net.ChartDataLoader;
 import com.android.settings.net.DataUsageMeteredSettings;
-import com.android.settings.net.NetworkPolicyEditor;
-import com.android.settings.net.SummaryForAllUidLoader;
-import com.android.settings.net.UidDetail;
-import com.android.settings.net.UidDetailProvider;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
 import com.android.settings.widget.ChartDataUsageView;
 import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener;
 import com.android.settings.widget.ChartNetworkSeriesView;
+import com.android.settingslib.AppItem;
+import com.android.settingslib.NetworkPolicyEditor;
+import com.android.settingslib.net.ChartData;
+import com.android.settingslib.net.ChartDataLoader;
+import com.android.settingslib.net.SummaryForAllUidLoader;
+import com.android.settingslib.net.UidDetail;
+import com.android.settingslib.net.UidDetailProvider;
 import com.google.android.collect.Lists;
 
 import libcore.util.Objects;
@@ -485,7 +483,7 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
             // a header at the top.
             FrameLayout pinnedHeader = (FrameLayout) rootView.findViewById(R.id.pinned_header);
             AppHeader.createAppHeader(getActivity(), detail.icon, detail.label, null, pinnedHeader);
-            AppDetailsFragment.show(DataUsageSummary.this, app, detail.label, false);
+            AppDetailsFragment.show(DataUsageSummary.this, app, detail.label, true);
         } catch (NameNotFoundException e) {
             Log.w(TAG, "Could not find " + mShowAppImmediatePkg, e);
             Toast.makeText(getActivity(), getString(R.string.unknown_app), Toast.LENGTH_LONG)
@@ -936,9 +934,11 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
         if (isAppDetailMode()) {
             mAppDetail.setVisibility(View.VISIBLE);
             mCycleAdapter.setChangeVisible(false);
+            mChart.setVisibility(View.GONE);
         } else {
             mAppDetail.setVisibility(View.GONE);
             mCycleAdapter.setChangeVisible(true);
+            mChart.setVisibility(View.VISIBLE);
 
             // hide detail stats when not in detail mode
             mChart.bindDetailNetworkStats(null);
@@ -1664,70 +1664,6 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
         }
     }
 
-    public static class AppItem implements Comparable<AppItem>, Parcelable {
-        public static final int CATEGORY_USER = 0;
-        public static final int CATEGORY_APP_TITLE = 1;
-        public static final int CATEGORY_APP = 2;
-
-        public final int key;
-        public boolean restricted;
-        public int category;
-
-        public SparseBooleanArray uids = new SparseBooleanArray();
-        public long total;
-
-        public AppItem() {
-            this.key = 0;
-        }
-
-        public AppItem(int key) {
-            this.key = key;
-        }
-
-        public AppItem(Parcel parcel) {
-            key = parcel.readInt();
-            uids = parcel.readSparseBooleanArray();
-            total = parcel.readLong();
-        }
-
-        public void addUid(int uid) {
-            uids.put(uid, true);
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(key);
-            dest.writeSparseBooleanArray(uids);
-            dest.writeLong(total);
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public int compareTo(AppItem another) {
-            int comparison = Integer.compare(category, another.category);
-            if (comparison == 0) {
-                comparison = Long.compare(another.total, total);
-            }
-            return comparison;
-        }
-
-        public static final Creator<AppItem> CREATOR = new Creator<AppItem>() {
-            @Override
-            public AppItem createFromParcel(Parcel in) {
-                return new AppItem(in);
-            }
-
-            @Override
-            public AppItem[] newArray(int size) {
-                return new AppItem[size];
-            }
-        };
-    }
-
     /**
      * Adapter of applications, sorted by total usage descending.
      */
@@ -1998,6 +1934,16 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
             target.mCurrentApp = null;
             target.updateBody();
         }
+
+        @Override
+        public boolean onOptionsItemSelected(MenuItem item) {
+            switch (item.getItemId()) {
+            case android.R.id.home:
+                getFragmentManager().popBackStack();
+                return true;
+            }
+            return super.onOptionsItemSelected(item);
+        }
     }
 
     /**
index f20728a..4a4c51d 100644 (file)
@@ -101,9 +101,6 @@ public class DevelopmentSettings extends SettingsPreferenceFragment
      */
     public static final String PREF_SHOW = "show";
 
-    private static final ComponentName SYSUI_TUNER = new ComponentName("com.android.systemui",
-            "com.android.systemui.tuner.TunerActivity");
-
     private static final String ENABLE_ADB = "enable_adb";
     private static final String CLEAR_ADB_KEYS = "clear_adb_keys";
     private static final String ENABLE_TERMINAL = "enable_terminal";
@@ -270,8 +267,6 @@ public class DevelopmentSettings extends SettingsPreferenceFragment
     private Dialog mAdbKeysDialog;
     private boolean mUnavailable;
 
-    private SwitchPreference mTunerUiPref;
-
     @Override
     protected int getMetricsCategory() {
         return MetricsLogger.DEVELOPMENT;
@@ -407,8 +402,6 @@ public class DevelopmentSettings extends SettingsPreferenceFragment
         mAllPrefs.add(mShowAllANRs);
         mResetSwitchPrefs.add(mShowAllANRs);
 
-        mTunerUiPref = findAndInitSwitchPref(TUNER_UI_KEY);
-
         Preference hdcpChecking = findPreference(HDCP_CHECKING_KEY);
         if (hdcpChecking != null) {
             mAllPrefs.add(hdcpChecking);
@@ -608,7 +601,6 @@ public class DevelopmentSettings extends SettingsPreferenceFragment
         updateMobileDataAlwaysOnOptions();
         updateSimulateColorSpace();
         updateUSBAudioOptions();
-        updateTweakUi();
     }
 
     private void resetDangerousOptions() {
@@ -1153,21 +1145,6 @@ public class DevelopmentSettings extends SettingsPreferenceFragment
         }
     }
 
-    private void updateTweakUi() {
-        updateSwitchPreference(mTunerUiPref, getActivity().getPackageManager()
-                .getComponentEnabledSetting(SYSUI_TUNER)
-                == PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
-        mTunerUiPref.setOnPreferenceChangeListener(this);
-    }
-
-    private void writeTweakUi(Object newValue) {
-        Boolean enabled = (Boolean) newValue;
-        getActivity().getPackageManager().setComponentEnabledSetting(SYSUI_TUNER,
-                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
-                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                        PackageManager.DONT_KILL_APP);
-    }
-
     private void updateUSBAudioOptions() {
         updateSwitchPreference(mUSBAudio, Settings.Secure.getInt(getContentResolver(),
                 Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED, 0) != 0);
@@ -1766,9 +1743,6 @@ public class DevelopmentSettings extends SettingsPreferenceFragment
         } else if (preference == mSimulateColorSpace) {
             writeSimulateColorSpace(newValue);
             return true;
-        } else if (preference == mTunerUiPref) {
-            writeTweakUi(newValue);
-            return true;
         }
         return false;
     }
index 76b4635..1f0cad1 100644 (file)
@@ -37,8 +37,10 @@ import android.provider.SearchIndexableResource;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.text.TextUtils;
+import android.text.format.DateFormat;
 import android.util.Log;
 import android.widget.Toast;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Index;
@@ -47,9 +49,13 @@ import com.android.settings.search.Indexable;
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
+import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -69,6 +75,7 @@ public class DeviceInfoSettings extends SettingsPreferenceFragment implements In
     private static final String KEY_SELINUX_STATUS = "selinux_status";
     private static final String KEY_BASEBAND_VERSION = "baseband_version";
     private static final String KEY_FIRMWARE_VERSION = "firmware_version";
+    private static final String KEY_SECURITY_PATCH = "security_patch";
     private static final String KEY_UPDATE_SETTING = "additional_system_update_settings";
     private static final String KEY_EQUIPMENT_ID = "fcc_equipment_id";
     private static final String PROPERTY_EQUIPMENT_ID = "ro.ril.fccid";
@@ -99,6 +106,21 @@ public class DeviceInfoSettings extends SettingsPreferenceFragment implements In
 
         setStringSummary(KEY_FIRMWARE_VERSION, Build.VERSION.RELEASE);
         findPreference(KEY_FIRMWARE_VERSION).setEnabled(true);
+        String patch = Build.VERSION.SECURITY_PATCH;
+        if (!"".equals(patch)) {
+            try {
+                SimpleDateFormat template = new SimpleDateFormat("yyyy-MM-dd");
+                Date patchDate = template.parse(patch);
+                String format = DateFormat.getBestDateTimePattern(Locale.getDefault(), "dMMMMyyyy");
+                patch = DateFormat.format(format, patchDate).toString();
+            } catch (ParseException e) {
+                // broken parse; fall through and use the raw string
+            }
+            setStringSummary(KEY_SECURITY_PATCH, patch);
+        } else {
+            getPreferenceScreen().removePreference(findPreference(KEY_SECURITY_PATCH));
+
+        }
         setValueSummary(KEY_BASEBAND_VERSION, "gsm.version.baseband");
         setStringSummary(KEY_DEVICE_MODEL, Build.MODEL + getMsvSuffix());
         setValueSummary(KEY_EQUIPMENT_ID, PROPERTY_EQUIPMENT_ID);
index eef3c6b..d2daac8 100644 (file)
@@ -22,6 +22,7 @@ import com.android.settings.DropDownPreference.Callback;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 
+import static android.provider.Settings.Secure.CAMERA_GESTURE_DISABLED;
 import static android.provider.Settings.Secure.DOUBLE_TAP_TO_WAKE;
 import static android.provider.Settings.Secure.DOZE_ENABLED;
 import static android.provider.Settings.Secure.WAKE_GESTURE_ENABLED;
@@ -74,6 +75,7 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
     private static final String KEY_AUTO_BRIGHTNESS = "auto_brightness";
     private static final String KEY_AUTO_ROTATE = "auto_rotate";
     private static final String KEY_NIGHT_MODE = "night_mode";
+    private static final String KEY_CAMERA_GESTURE = "camera_gesture";
 
     private static final int DLG_GLOBAL_CHANGE_WARNING = 1;
 
@@ -88,6 +90,7 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
     private SwitchPreference mDozePreference;
     private SwitchPreference mTapToWakePreference;
     private SwitchPreference mAutoBrightnessPreference;
+    private SwitchPreference mCameraGesturePreference;
 
     @Override
     protected int getMetricsCategory() {
@@ -149,6 +152,13 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
             removePreference(KEY_TAP_TO_WAKE);
         }
 
+        if (isCameraGestureAvailable(getResources())) {
+            mCameraGesturePreference = (SwitchPreference) findPreference(KEY_CAMERA_GESTURE);
+            mCameraGesturePreference.setOnPreferenceChangeListener(this);
+        } else {
+            removePreference(KEY_CAMERA_GESTURE);
+        }
+
         if (RotationPolicy.isRotationLockToggleVisible(activity)) {
             DropDownPreference rotatePreference =
                     (DropDownPreference) findPreference(KEY_AUTO_ROTATE);
@@ -224,6 +234,13 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
         return res.getBoolean(com.android.internal.R.bool.config_automatic_brightness_available);
     }
 
+    private static boolean isCameraGestureAvailable(Resources res) {
+        boolean configSet = res.getInteger(
+                com.android.internal.R.integer.config_cameraLaunchGestureSensorType) != -1;
+        return configSet &&
+                !SystemProperties.getBoolean("gesture.disable_camera_launch", false);
+    }
+
     private void updateTimeoutPreferenceDescription(long currentTimeout) {
         ListPreference preference = mScreenTimeoutPreference;
         String summary;
@@ -370,6 +387,12 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
             int value = Settings.Secure.getInt(getContentResolver(), DOUBLE_TAP_TO_WAKE, 0);
             mTapToWakePreference.setChecked(value != 0);
         }
+
+        // Update camera gesture if it is available.
+        if (mCameraGesturePreference != null) {
+            int value = Settings.Secure.getInt(getContentResolver(), CAMERA_GESTURE_DISABLED, 0);
+            mCameraGesturePreference.setChecked(value == 0);
+        }
     }
 
     private void updateScreenSaverSummary() {
@@ -425,6 +448,11 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
             boolean value = (Boolean) objValue;
             Settings.Secure.putInt(getContentResolver(), DOUBLE_TAP_TO_WAKE, value ? 1 : 0);
         }
+        if (preference == mCameraGesturePreference) {
+            boolean value = (Boolean) objValue;
+            Settings.Secure.putInt(getContentResolver(), CAMERA_GESTURE_DISABLED,
+                    value ? 0 : 1 /* Backwards because setting is for disabling */);
+        }
         if (preference == mNightModePreference) {
             try {
                 final int value = Integer.parseInt((String) objValue);
@@ -493,6 +521,9 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
                     if (!isTapToWakeAvailable(context.getResources())) {
                         result.add(KEY_TAP_TO_WAKE);
                     }
+                    if (!isCameraGestureAvailable(context.getResources())) {
+                        result.add(KEY_CAMERA_GESTURE);
+                    }
                     return result;
                 }
             };
index 794005b..1bef99a 100644 (file)
@@ -87,32 +87,44 @@ public class EncryptionInterstitial extends SettingsActivity {
         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
-            final int layoutId = R.layout.encryption_interstitial;
-            View view = inflater.inflate(layoutId, container, false);
+            return inflater.inflate(R.layout.encryption_interstitial, container, false);
+        }
+
+        @Override
+        public void onViewCreated(View view, Bundle savedInstanceState) {
+            super.onViewCreated(view, savedInstanceState);
             mRequirePasswordToDecryptButton =
                     (RadioButton) view.findViewById(R.id.encrypt_require_password);
             mDontRequirePasswordToDecryptButton =
                     (RadioButton) view.findViewById(R.id.encrypt_dont_require_password);
             mEncryptionMessage =
                     (TextView) view.findViewById(R.id.encryption_message);
+            boolean forFingerprint = getActivity().getIntent().getBooleanExtra(
+                    ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
             int quality = getActivity().getIntent().getIntExtra(EXTRA_PASSWORD_QUALITY, 0);
             final int msgId;
             final int enableId;
             final int disableId;
             switch (quality) {
                 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
-                    msgId = R.string.encryption_interstitial_message_pattern;
+                    msgId = forFingerprint ?
+                            R.string.encryption_interstitial_message_pattern_for_fingerprint :
+                            R.string.encryption_interstitial_message_pattern;
                     enableId = R.string.encrypt_require_pattern;
                     disableId = R.string.encrypt_dont_require_pattern;
                     break;
                 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
                 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
-                    msgId = R.string.encryption_interstitial_message_pin;
+                    msgId = forFingerprint ?
+                            R.string.encryption_interstitial_message_pin_for_fingerprint :
+                            R.string.encryption_interstitial_message_pin;
                     enableId = R.string.encrypt_require_pin;
                     disableId = R.string.encrypt_dont_require_pin;
                     break;
                 default:
-                    msgId = R.string.encryption_interstitial_message_password;
+                    msgId = forFingerprint ?
+                            R.string.encryption_interstitial_message_password_for_fingerprint :
+                            R.string.encryption_interstitial_message_password;
                     enableId = R.string.encrypt_require_password;
                     disableId = R.string.encrypt_dont_require_password;
                     break;
@@ -127,7 +139,6 @@ public class EncryptionInterstitial extends SettingsActivity {
 
             setRequirePasswordState(getActivity().getIntent().getBooleanExtra(
                     EXTRA_REQUIRE_PASSWORD, true));
-            return view;
         }
 
         @Override
diff --git a/src/com/android/settings/InstrumentedActivity.java b/src/com/android/settings/InstrumentedActivity.java
new file mode 100644 (file)
index 0000000..4a0e03a
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import com.android.internal.logging.MetricsLogger;
+
+import android.app.Activity;
+
+/**
+ * Instrumented activity that logs visibility state.
+ */
+public abstract class InstrumentedActivity extends Activity {
+    /**
+     * Declare the view of this category.
+     *
+     * Categories are defined in {@link com.android.internal.logging.MetricsLogger}
+     * or if there is no relevant existing category you may define one in
+     * {@link com.android.settings.InstrumentedFragment}.
+     */
+    protected abstract int getMetricsCategory();
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        MetricsLogger.visible(this, getMetricsCategory());
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        MetricsLogger.hidden(this, getMetricsCategory());
+    }
+}
index 453dbd0..da2a340 100644 (file)
@@ -26,9 +26,6 @@ public abstract class InstrumentedFragment extends PreferenceFragment {
     // Declare new temporary categories here, starting after this value.
     public static final int UNDECLARED = 100000;
 
-    public static final int ABOUT_LEGAL_SETTINGS = UNDECLARED + 1;
-    public static final int ACTION_SEARCH_RESULTS = UNDECLARED + 2;
-
     /**
      * Declare the view of this category.
      *
index 5a9bf0b..cd91d20 100644 (file)
@@ -26,6 +26,7 @@ import android.os.Bundle;
 import android.preference.PreferenceGroup;
 import android.provider.SearchIndexableResource;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 
@@ -59,7 +60,7 @@ public class LegalSettings extends SettingsPreferenceFragment implements Indexab
 
     @Override
     protected int getMetricsCategory() {
-        return InstrumentedFragment.ABOUT_LEGAL_SETTINGS;
+        return MetricsLogger.ABOUT_LEGAL_SETTINGS;
     }
 
     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
index 6ab36c1..b6cbebe 100644 (file)
@@ -168,6 +168,24 @@ public class MasterClear extends InstrumentedFragment {
 
         final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
         loadAccountList(um);
+        StringBuffer contentDescription = new StringBuffer();
+        View masterClearContainer = mContentView.findViewById(R.id.master_clear_container);
+        getContentDescription(masterClearContainer, contentDescription);
+        masterClearContainer.setContentDescription(contentDescription);
+    }
+
+    private void getContentDescription(View v, StringBuffer description) {
+       if (v instanceof ViewGroup) {
+           ViewGroup vGroup = (ViewGroup) v;
+           for (int i = 0; i < vGroup.getChildCount(); i++) {
+               View nextChild = vGroup.getChildAt(i);
+               getContentDescription(nextChild, description);
+           }
+       } else if (v instanceof TextView) {
+           TextView vText = (TextView) v;
+           description.append(vText.getText());
+           description.append(","); // Allow Talkback to pause between sections.
+       }
     }
 
     private boolean isExtStorageEncrypted() {
index 37d9dd6..7319a4e 100644 (file)
@@ -21,6 +21,7 @@ import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.os.AsyncTask;
 import android.service.persistentdata.PersistentDataBlockManager;
+
 import com.android.internal.logging.MetricsLogger;
 
 import android.content.Intent;
@@ -30,6 +31,7 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
+import android.widget.TextView;
 
 /**
  * Confirm and execute a reset of the device to a clean "just out of the box"
@@ -134,9 +136,21 @@ public class MasterClearConfirm extends InstrumentedFragment {
         }
         mContentView = inflater.inflate(R.layout.master_clear_confirm, null);
         establishFinalConfirmationState();
+        setAccessibilityTitle();
         return mContentView;
     }
 
+    private void setAccessibilityTitle() {
+        CharSequence currentTitle = getActivity().getTitle();
+        TextView confirmationMessage =
+                (TextView) mContentView.findViewById(R.id.master_clear_confirm);
+        if (confirmationMessage != null) {
+            String accessibileText = new StringBuilder(currentTitle).append(",").append(
+                    confirmationMessage.getText()).toString();
+            getActivity().setTitle(Utils.createAccessibleSequence(currentTitle, accessibileText));
+        }
+    }
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/src/com/android/settings/MediaFormat.java b/src/com/android/settings/MediaFormat.java
deleted file mode 100644 (file)
index 517ec2d..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.storage.StorageVolume;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Button;
-
-import com.android.internal.os.storage.ExternalStorageFormatter;
-
-/**
- * Confirm and execute a format of the sdcard.
- * Multiple confirmations are required: first, a general "are you sure
- * you want to do this?" prompt, followed by a keyguard pattern trace if the user
- * has defined one, followed by a final strongly-worded "THIS WILL ERASE EVERYTHING
- * ON THE SD CARD" prompt.  If at any time the phone is allowed to go to sleep, is
- * locked, et cetera, then the confirmation sequence is abandoned.
- */
-public class MediaFormat extends Activity {
-
-    private static final int KEYGUARD_REQUEST = 55;
-
-    private LayoutInflater mInflater;
-
-    private View mInitialView;
-    private Button mInitiateButton;
-
-    private View mFinalView;
-    private Button mFinalButton;
-
-    /**
-     * The user has gone through the multiple confirmation, so now we go ahead
-     * and invoke the Mount Service to format the SD card.
-     */
-    private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {
-            public void onClick(View v) {
-
-                if (Utils.isMonkeyRunning()) {
-                    return;
-                }
-                Intent intent = new Intent(ExternalStorageFormatter.FORMAT_ONLY);
-                intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
-                // Transfer the storage volume to the new intent
-                final StorageVolume storageVolume = getIntent().getParcelableExtra(
-                        StorageVolume.EXTRA_STORAGE_VOLUME);
-                intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, storageVolume);
-                startService(intent);
-                finish();
-            }
-        };
-
-    /**
-     *  Keyguard validation is run using the standard {@link ConfirmLockPattern}
-     * component as a subactivity
-     */
-    private boolean runKeyguardConfirmation(int request) {
-        return new ChooseLockSettingsHelper(this).launchConfirmationActivity(request,
-                getText(R.string.media_format_title));
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
-
-        if (requestCode != KEYGUARD_REQUEST) {
-            return;
-        }
-
-        // If the user entered a valid keyguard trace, present the final
-        // confirmation prompt; otherwise, go back to the initial state.
-        if (resultCode == Activity.RESULT_OK) {
-            establishFinalConfirmationState();
-        } else if (resultCode == Activity.RESULT_CANCELED) {
-            finish();
-        } else {
-            establishInitialState();
-        }
-    }
-
-    /**
-     * If the user clicks to begin the reset sequence, we next require a
-     * keyguard confirmation if the user has currently enabled one.  If there
-     * is no keyguard available, we simply go to the final confirmation prompt.
-     */
-    private Button.OnClickListener mInitiateListener = new Button.OnClickListener() {
-            public void onClick(View v) {
-                if (!runKeyguardConfirmation(KEYGUARD_REQUEST)) {
-                    establishFinalConfirmationState();
-                }
-            }
-        };
-
-    /**
-     * Configure the UI for the final confirmation interaction
-     */
-    private void establishFinalConfirmationState() {
-        if (mFinalView == null) {
-            mFinalView = mInflater.inflate(R.layout.media_format_final, null);
-            mFinalButton =
-                    (Button) mFinalView.findViewById(R.id.execute_media_format);
-            mFinalButton.setOnClickListener(mFinalClickListener);
-        }
-
-        setContentView(mFinalView);
-    }
-
-    /**
-     * In its initial state, the activity presents a button for the user to
-     * click in order to initiate a confirmation sequence.  This method is
-     * called from various other points in the code to reset the activity to
-     * this base state.
-     *
-     * <p>Reinflating views from resources is expensive and prevents us from
-     * caching widget pointers, so we use a single-inflate pattern:  we lazy-
-     * inflate each view, caching all of the widget pointers we'll need at the
-     * time, then simply reuse the inflated views directly whenever we need
-     * to change contents.
-     */
-    private void establishInitialState() {
-        if (mInitialView == null) {
-            mInitialView = mInflater.inflate(R.layout.media_format_primary, null);
-            mInitiateButton =
-                    (Button) mInitialView.findViewById(R.id.initiate_media_format);
-            mInitiateButton.setOnClickListener(mInitiateListener);
-        }
-
-        setContentView(mInitialView);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedState) {
-        super.onCreate(savedState);
-
-        mInitialView = null;
-        mFinalView = null;
-        mInflater = LayoutInflater.from(this);
-
-        establishInitialState();
-    }
-
-    /** Abandon all progress through the confirmation sequence by returning
-     * to the initial view any time the activity is interrupted (e.g. by
-     * idle timeout).
-     */
-    @Override
-    public void onPause() {
-        super.onPause();
-
-        if (!isFinishing()) {
-            establishInitialState();
-        }
-    }
-}
index 7762b89..db4b9a5 100644 (file)
@@ -18,6 +18,7 @@ package com.android.settings;
 
 import android.app.Fragment;
 import android.content.Context;
+import android.bluetooth.BluetoothManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkPolicyManager;
 import android.net.wifi.WifiManager;
@@ -93,6 +94,12 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
                 policyManager.factoryReset(subscriberId);
             }
 
+            BluetoothManager btManager = (BluetoothManager)
+                    context.getSystemService(Context.BLUETOOTH_SERVICE);
+            if (btManager != null) {
+                btManager.getAdapter().factoryReset();
+            }
+
             Toast.makeText(context, R.string.reset_network_complete_toast, Toast.LENGTH_SHORT)
                     .show();
         }
index b41d70f..6e679b6 100644 (file)
@@ -355,9 +355,6 @@ public class SecuritySettings extends SettingsPreferenceFragment
         final List<Fingerprint> items = fpm.getEnrolledFingerprints();
         final int fingerprintCount = items != null ? items.size() : 0;
         final String clazz;
-        boolean hasPassword = mChooseLockSettingsHelper.utils().getActivePasswordQuality(
-                MY_USER_ID)
-                != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
         if (fingerprintCount > 0) {
             fingerprintPreference.setSummary(getResources().getQuantityString(
                     R.plurals.security_settings_fingerprint_preference_summary,
@@ -365,7 +362,6 @@ public class SecuritySettings extends SettingsPreferenceFragment
             clazz = FingerprintSettings.class.getName();
         } else {
             clazz = FingerprintEnrollIntroduction.class.getName();
-            intent.putExtra(FingerprintEnrollIntroduction.EXTRA_HAS_PASSWORD, hasPassword);
         }
         intent.setClassName("com.android.settings", clazz);
         fingerprintPreference.setIntent(intent);
@@ -795,6 +791,16 @@ public class SecuritySettings extends SettingsPreferenceFragment
                 result.add(data);
             }
 
+            // Fingerprint
+            FingerprintManager fpm =
+                    (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
+            if (fpm.isHardwareDetected()) {
+                data = new SearchIndexableRaw(context);
+                data.title = res.getString(R.string.security_settings_fingerprint_preference_title);
+                data.screenTitle = screenTitle;
+                result.add(data);
+            }
+
             // Credential storage
             final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
index ea4f77a..7b94d79 100644 (file)
@@ -114,5 +114,8 @@ public class Settings extends SettingsActivity {
     public static class ApnSettingsActivity extends SettingsActivity { /* empty */ }
     public static class WifiCallingSettingsActivity extends SettingsActivity { /* empty */ }
     public static class MemorySettingsActivity extends SettingsActivity { /* empty */ }
+    public static class OverlaySettingsActivity extends SettingsActivity { /* empty */ }
+    public static class WriteSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class AppDrawOverlaySettingsActivity extends SettingsActivity { /* empty */ }
+    public static class AppWriteSettingsActivity extends SettingsActivity { /* empty */ }
 }
-
index 442ce60..8edec09 100644 (file)
@@ -70,12 +70,14 @@ import com.android.settings.accessibility.AccessibilitySettings;
 import com.android.settings.accessibility.CaptionPropertiesFragment;
 import com.android.settings.accounts.AccountSettings;
 import com.android.settings.accounts.AccountSyncSettings;
+import com.android.settings.applications.DrawOverlayDetails;
 import com.android.settings.applications.InstalledAppDetails;
 import com.android.settings.applications.ManageApplications;
 import com.android.settings.applications.ManageAssist;
 import com.android.settings.applications.ProcessStatsSummary;
 import com.android.settings.applications.ProcessStatsUi;
 import com.android.settings.applications.UsageAccessDetails;
+import com.android.settings.applications.WriteSettingsDetails;
 import com.android.settings.bluetooth.BluetoothSettings;
 import com.android.settings.dashboard.DashboardCategory;
 import com.android.settings.dashboard.DashboardSummary;
@@ -350,6 +352,8 @@ public class SettingsActivity extends Activity
             ProcessStatsUi.class.getName(),
             PowerUsageDetail.class.getName(),
             ProcessStatsSummary.class.getName(),
+            DrawOverlayDetails.class.getName(),
+            WriteSettingsDetails.class.getName(),
     };
 
 
index baf04d4..b957c38 100644 (file)
@@ -505,7 +505,10 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
     }
 
     public void finish() {
-        getActivity().onBackPressed();
+        Activity activity = getActivity();
+        if (activity != null) {
+            activity.onBackPressed();
+        }
     }
 
     public boolean startFragment(Fragment caller, String fragmentClass, int titleRes,
index 8b09a10..5a6ac7f 100644 (file)
@@ -28,11 +28,9 @@ import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.preference.PreferenceFragment;
-import android.hardware.fingerprint.FingerprintManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ListView;
 
 /**
  * Setup Wizard's version of ChooseLockGeneric screen. It inherits the logic and basic structure
@@ -70,14 +68,6 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
             final SetupWizardListLayout layout = (SetupWizardListLayout) inflater.inflate(
                     R.layout.setup_choose_lock_generic, container, false);
             layout.setHeaderText(getActivity().getTitle());
-            ListView list = layout.getListView();
-            final FingerprintManager fpm = (FingerprintManager)
-                    getActivity().getSystemService(Context.FINGERPRINT_SERVICE);
-            if (fpm != null && fpm.isHardwareDetected()) {
-                final View footer = inflater.inflate(
-                        R.layout.setup_screen_lock_fingerprint_details, list, false);
-                list.addFooterView(footer, null, false);
-            }
 
             final NavigationBar navigationBar = layout.getNavigationBar();
             navigationBar.getNextButton().setEnabled(false);
index 61ff81c..27d7e0c 100644 (file)
@@ -20,7 +20,6 @@ import com.android.setupwizardlib.SetupWizardLayout;
 import com.android.setupwizardlib.view.NavigationBar;
 
 import android.app.Activity;
-import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -73,36 +72,24 @@ public class SetupEncryptionInterstitial extends EncryptionInterstitial {
         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
-            final SetupWizardLayout layout = new SetupWizardLayout(inflater.getContext());
-            layout.setIllustration(R.drawable.setup_illustration_lock_screen,
-                    R.drawable.setup_illustration_horizontal_tile);
-            layout.setBackgroundTile(R.drawable.setup_illustration_tile);
-            final int headerTextResource = getHeaderTextResource();
-            layout.setHeaderText(headerTextResource);
+            return inflater.inflate(R.layout.setup_encryption_interstitial, container, false);
+        }
+
+        @Override
+        public void onViewCreated(View view, Bundle savedInstanceState) {
+            super.onViewCreated(view, savedInstanceState);
+
+            final SetupWizardLayout layout =
+                    (SetupWizardLayout) view.findViewById(R.id.setup_wizard_layout);
 
-            View content = super.onCreateView(inflater, layout, savedInstanceState);
-            layout.addView(content);
-            layout.getNavigationBar().setNavigationBarListener(this);
+            final NavigationBar navigationBar = layout.getNavigationBar();
+            navigationBar.setNavigationBarListener(this);
 
             Activity activity = getActivity();
             if (activity != null) {
-                activity.setTitle(headerTextResource);
+                activity.setTitle(R.string.encryption_interstitial_header);
                 SetupWizardUtils.setImmersiveMode(activity);
             }
-            return layout;
-        }
-
-        private int getHeaderTextResource() {
-            final int quality = getActivity().getIntent().getIntExtra(EXTRA_PASSWORD_QUALITY, 0);
-            switch (quality) {
-                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
-                    return R.string.unlock_set_unlock_pattern_title;
-                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
-                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
-                    return R.string.unlock_set_unlock_pin_title;
-                default:
-                    return R.string.unlock_set_unlock_password_title;
-            }
         }
 
         @Override
index 498ec5b..02a4ef2 100644 (file)
@@ -113,6 +113,7 @@ public class TrustAgentSettings extends SettingsPreferenceFragment implements
 
             if (disabledByDevicePolicy
                     && mDpm.getTrustAgentConfiguration(null, agent.component) == null) {
+                preference.setChecked(false);
                 preference.setEnabled(false);
                 preference.setSummary(R.string.trust_agent_disabled_device_admin);
             }
index dcdc7eb..2ac908e 100644 (file)
@@ -33,7 +33,7 @@ import android.widget.SpinnerAdapter;
 import android.widget.TextView;
 
 import com.android.internal.util.UserIcons;
-import com.android.settings.drawable.CircleFramedDrawable;
+import com.android.settingslib.drawable.CircleFramedDrawable;
 
 import java.util.ArrayList;
 
@@ -173,4 +173,4 @@ public class UserAdapter implements SpinnerAdapter, ListAdapter {
     public boolean isEnabled(int position) {
         return true;
     }
-}
\ No newline at end of file
+}
index 21a5019..7a6f165 100644 (file)
@@ -33,6 +33,7 @@ import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -69,7 +70,11 @@ import android.provider.ContactsContract.Profile;
 import android.provider.ContactsContract.RawContacts;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.telephony.TelephonyManager;
+import android.text.Spannable;
+import android.text.SpannableString;
 import android.text.TextUtils;
+import android.text.style.TtsSpan;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
@@ -84,8 +89,8 @@ import android.widget.TabWidget;
 import com.android.internal.util.UserIcons;
 import com.android.settings.UserAdapter.UserDetails;
 import com.android.settings.dashboard.DashboardTile;
-import com.android.settings.drawable.CircleFramedDrawable;
 import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.drawable.CircleFramedDrawable;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -455,36 +460,6 @@ public final class Utils {
         view.setPaddingRelative(paddingStart, 0, paddingEnd, paddingBottom);
     }
 
-    /**
-     * Return string resource that best describes combination of tethering
-     * options available on this device.
-     */
-    public static int getTetheringLabel(ConnectivityManager cm) {
-        String[] usbRegexs = cm.getTetherableUsbRegexs();
-        String[] wifiRegexs = cm.getTetherableWifiRegexs();
-        String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
-
-        boolean usbAvailable = usbRegexs.length != 0;
-        boolean wifiAvailable = wifiRegexs.length != 0;
-        boolean bluetoothAvailable = bluetoothRegexs.length != 0;
-
-        if (wifiAvailable && usbAvailable && bluetoothAvailable) {
-            return R.string.tether_settings_title_all;
-        } else if (wifiAvailable && usbAvailable) {
-            return R.string.tether_settings_title_all;
-        } else if (wifiAvailable && bluetoothAvailable) {
-            return R.string.tether_settings_title_all;
-        } else if (wifiAvailable) {
-            return R.string.tether_settings_title_wifi;
-        } else if (usbAvailable && bluetoothAvailable) {
-            return R.string.tether_settings_title_usb_bluetooth;
-        } else if (usbAvailable) {
-            return R.string.tether_settings_title_usb;
-        } else {
-            return R.string.tether_settings_title_bluetooth;
-        }
-    }
-
     /* Used by UserSettings as well. Call this on a non-ui thread. */
     public static boolean copyMeProfilePhoto(Context context, UserInfo user) {
         Uri contactUri = Profile.CONTENT_URI;
@@ -900,43 +875,6 @@ public final class Utils {
     }
 
     /**
-     * Returns a circular icon for a user.
-     */
-    public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
-        if (user.isManagedProfile()) {
-            // We use predefined values for managed profiles
-            Bitmap b = BitmapFactory.decodeResource(context.getResources(),
-                    com.android.internal.R.drawable.ic_corp_icon);
-            return CircleFramedDrawable.getInstance(context, b);
-        }
-        if (user.iconPath != null) {
-            Bitmap icon = um.getUserIcon(user.id);
-            if (icon != null) {
-                return CircleFramedDrawable.getInstance(context, icon);
-            }
-        }
-        return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
-                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
-    }
-
-    /**
-     * Returns a label for the user, in the form of "User: user name" or "Work profile".
-     */
-    public static String getUserLabel(Context context, UserInfo info) {
-        if (info.isManagedProfile()) {
-            // We use predefined values for managed profiles
-            return context.getString(R.string.managed_user_title);
-        }
-        String name = info != null ? info.name : null;
-        if (name == null && info != null) {
-            name = Integer.toString(info.id);
-        } else if (info == null) {
-            name = context.getString(R.string.unknown);
-        }
-        return context.getResources().getString(R.string.running_process_item_user_label, name);
-    }
-
-    /**
      * Return whether or not the user should have a SIM Cards option in Settings.
      * TODO: Change back to returning true if count is greater than one after testing.
      * TODO: See bug 16533525.
@@ -1109,6 +1047,30 @@ public final class Utils {
         return prefActList.size() > 0;
     }
 
+    public static ArraySet<String> getHandledDomains(PackageManager pm, String packageName) {
+        List<IntentFilterVerificationInfo> iviList = pm.getIntentFilterVerifications(packageName);
+        List<IntentFilter> filters = pm.getAllIntentFilters(packageName);
+
+        ArraySet<String> result = new ArraySet<>();
+        if (iviList.size() > 0) {
+            for (IntentFilterVerificationInfo ivi : iviList) {
+                for (String host : ivi.getDomains()) {
+                    result.add(host);
+                }
+            }
+        }
+        if (filters != null && filters.size() > 0) {
+            for (IntentFilter filter : filters) {
+                if (filter.hasCategory(Intent.CATEGORY_BROWSABLE)
+                        && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+                                filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
+                    result.addAll(filter.getHostsList());
+                }
+            }
+        }
+        return result;
+    }
+
     public static CharSequence getLaunchByDeafaultSummary(ApplicationsState.AppEntry appEntry,
             IUsbManager usbManager, PackageManager pm, Context context) {
         String packageName = appEntry.info.packageName;
@@ -1190,4 +1152,29 @@ public final class Utils {
             return false;
         }
     }
-}
\ No newline at end of file
+
+    /**
+     * Returns an accessible SpannableString.
+     * @param displayText the text to display
+     * @param accessibileText the text text-to-speech engines should read
+     */
+    public static SpannableString createAccessibleSequence(CharSequence displayText,
+            String accessibileText) {
+        SpannableString str = new SpannableString(displayText);
+        str.setSpan(new TtsSpan.TextBuilder(accessibileText).build(), 0,
+                displayText.length(),
+                Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+        return str;
+    }
+
+    public static int getEffectiveUserId(Context context) {
+        UserManager um = UserManager.get(context);
+        if (um != null) {
+            return um.getCredentialOwnerProfile(UserHandle.myUserId());
+        } else {
+            Log.e(TAG, "Unable to acquire UserManager");
+            return UserHandle.myUserId();
+        }
+    }
+}
+
index 62c7b3e..787ccb4 100644 (file)
@@ -57,6 +57,8 @@ public class WifiCallingSettings extends SettingsPreferenceFragment
     private ListPreference mButtonWfcMode;
     private TextView mEmptyView;
 
+    private boolean mValidListener = false;
+
     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
         /*
          * Enable/disable controls when in/out of a call and depending on
@@ -172,6 +174,8 @@ public class WifiCallingSettings extends SettingsPreferenceFragment
             tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
 
             mSwitchBar.addOnSwitchChangeListener(this);
+
+            mValidListener = true;
         }
 
         // NOTE: Buttons will be enabled/disabled in mPhoneStateListener
@@ -196,7 +200,9 @@ public class WifiCallingSettings extends SettingsPreferenceFragment
 
         final Context context = getActivity();
 
-        if (ImsManager.isWfcEnabledByPlatform(getActivity())) {
+        if (mValidListener) {
+            mValidListener = false;
+
             TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
             tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
 
index 32d50bf..9253eac 100644 (file)
@@ -237,11 +237,7 @@ public class WirelessSettings extends SettingsPreferenceFragment implements Inde
         mAirplaneModeEnabler = new AirplaneModeEnabler(activity, mAirplaneModePreference);
         mNfcEnabler = new NfcEnabler(activity, nfc, androidBeam);
 
-        if (ImsManager.isWfcEnabledByPlatform(activity)) {
-            mButtonWfc = (PreferenceScreen) findPreference(KEY_WFC_SETTINGS);
-        } else {
-            removePreference(KEY_WFC_SETTINGS);
-        }
+        mButtonWfc = (PreferenceScreen) findPreference(KEY_WFC_SETTINGS);
 
         // Remove NSD checkbox by default
         getPreferenceScreen().removePreference(nsd);
@@ -333,7 +329,7 @@ public class WirelessSettings extends SettingsPreferenceFragment implements Inde
             getPreferenceScreen().removePreference(findPreference(KEY_TETHER_SETTINGS));
         } else {
             Preference p = findPreference(KEY_TETHER_SETTINGS);
-            p.setTitle(Utils.getTetheringLabel(cm));
+            p.setTitle(com.android.settingslib.Utils.getTetheringLabel(cm));
 
             // Grey out if provisioning is not available.
             p.setEnabled(!TetherSettings
@@ -373,10 +369,15 @@ public class WirelessSettings extends SettingsPreferenceFragment implements Inde
             mNsdEnabler.resume();
         }
 
+        // update WFC setting
         final Context context = getActivity();
         if (ImsManager.isWfcEnabledByPlatform(context)) {
+            getPreferenceScreen().addPreference(mButtonWfc);
+
             mButtonWfc.setSummary(WifiCallingSettings.getWfcModeSummary(
                     context, ImsManager.getWfcMode(context)));
+        } else {
+            removePreference(KEY_WFC_SETTINGS);
         }
     }
 
index b23035b..d04c03f 100644 (file)
@@ -78,6 +78,7 @@ public abstract class ToggleFeaturePreferenceFragment
                 }
             }
         };
+        mSummaryPreference.setSelectable(false);
         mSummaryPreference.setPersistent(false);
         mSummaryPreference.setLayoutResource(R.layout.text_description_preference);
         preferenceScreen.addPreference(mSummaryPreference);
index c010a7b..94958a0 100644 (file)
@@ -49,6 +49,7 @@ import android.preference.PreferenceCategory;
 import android.preference.PreferenceScreen;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.settings.AccessiblePreferenceCategory;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
@@ -270,14 +271,19 @@ public class AccountSettings extends SettingsPreferenceFragment
         final ProfileData profileData = new ProfileData();
         profileData.userInfo = userInfo;
         if (addCategory) {
-            profileData.preferenceGroup = new PreferenceCategory(context);
+            profileData.preferenceGroup = new AccessiblePreferenceCategory(context);
             if (userInfo.isManagedProfile()) {
                 profileData.preferenceGroup.setLayoutResource(R.layout.work_profile_category);
                 profileData.preferenceGroup.setTitle(R.string.category_work);
-                profileData.preferenceGroup.setSummary(getWorkGroupSummary(context, userInfo));
+                String workGroupSummary = getWorkGroupSummary(context, userInfo);
+                profileData.preferenceGroup.setSummary(workGroupSummary);
+                ((AccessiblePreferenceCategory) profileData.preferenceGroup).setContentDescription(
+                        getString(R.string.accessibility_category_work, workGroupSummary));
                 profileData.removeWorkProfilePreference = newRemoveWorkProfilePreference(context);
             } else {
                 profileData.preferenceGroup.setTitle(R.string.category_personal);
+                ((AccessiblePreferenceCategory) profileData.preferenceGroup).setContentDescription(
+                        getString(R.string.accessibility_category_personal));
             }
             parent.addPreference(profileData.preferenceGroup);
         } else {
index d1df82a..59ca995 100644 (file)
@@ -35,6 +35,7 @@ import android.content.SyncAdapterType;
 import android.content.SyncInfo;
 import android.content.SyncStatusInfo;
 import android.content.pm.ProviderInfo;
+import android.content.pm.UserInfo;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -151,6 +152,7 @@ public class AccountSyncSettings extends AccountPreferenceBase {
         super.onCreate(icicle);
         setPreferenceScreen(null);
         addPreferencesFromResource(R.xml.account_sync_settings);
+        setAccessibilityTitle();
 
         setHasOptionsMenu(true);
     }
@@ -200,6 +202,18 @@ public class AccountSyncSettings extends AccountPreferenceBase {
         mProviderId.setText(mAccount.type);
     }
 
+    private void setAccessibilityTitle() {
+        final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
+        UserInfo user = um.getUserInfo(mUserHandle.getIdentifier());
+        boolean isWorkProfile = user != null ? user.isManagedProfile() : false;
+        CharSequence currentTitle = getActivity().getTitle();
+        String accessibilityTitle =
+                getString(isWorkProfile
+                        ? R.string.accessibility_work_account_title
+                        : R.string.accessibility_personal_account_title, currentTitle);
+        getActivity().setTitle(Utils.createAccessibleSequence(currentTitle, accessibilityTitle));
+    }
+
     @Override
     public void onResume() {
         removePreference("dummy");
index 7df269e..43fae71 100644 (file)
  */
 package com.android.settings.applications;
 
+import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.os.Bundle;
+import android.os.AsyncTask;
 import android.preference.Preference;
+import android.provider.Settings;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.R;
@@ -40,11 +43,15 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
     private static final String KEY_APP_PERM = "manage_perms";
     private static final String KEY_APP_DOMAIN_URLS = "domain_urls";
     private static final String KEY_HIGH_POWER_APPS = "high_power_apps";
+    private static final String KEY_SYSTEM_ALERT_WINDOW = "system_alert_window";
+    private static final String KEY_WRITE_SETTINGS_APPS = "write_settings_apps";
 
     private Session mSession;
     private Preference mAppPermsPreference;
     private Preference mAppDomainURLsPreference;
     private Preference mHighPowerPreference;
+    private Preference mSystemAlertWindowPreference;
+    private Preference mWriteSettingsPreference;
 
     private BroadcastReceiver mPermissionReceiver;
 
@@ -63,40 +70,8 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
         mAppPermsPreference = findPreference(KEY_APP_PERM);
         mAppDomainURLsPreference = findPreference(KEY_APP_DOMAIN_URLS);
         mHighPowerPreference = findPreference(KEY_HIGH_POWER_APPS);
-        updateUI();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        if (mPermissionReceiver != null) {
-            getContext().unregisterReceiver(mPermissionReceiver);
-            mPermissionReceiver = null;
-        }
-    }
-
-    private void updateUI() {
-        ArrayList<AppEntry> allApps = mSession.getAllApps();
-
-        int countAppWithDomainURLs = 0;
-        for (AppEntry entry : allApps) {
-            boolean hasDomainURLs =
-                    (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
-            if (hasDomainURLs) countAppWithDomainURLs++;
-        }
-        String summary = getResources().getQuantityString(
-                R.plurals.domain_urls_apps_summary, countAppWithDomainURLs, countAppWithDomainURLs);
-        mAppDomainURLsPreference.setSummary(summary);
-
-        int highPowerCount = PowerWhitelistBackend.getInstance().getWhitelistSize();
-        mHighPowerPreference.setSummary(getResources().getQuantityString(R.plurals.high_power_count,
-                highPowerCount, highPowerCount));
-
-        if (mPermissionReceiver != null) {
-            getContext().unregisterReceiver(mPermissionReceiver);
-        }
-        mPermissionReceiver = PermissionsSummaryHelper.getAppWithPermissionsCounts(getContext(),
-                mPermissionCallback);
+        mSystemAlertWindowPreference = findPreference(KEY_SYSTEM_ALERT_WINDOW);
+        mWriteSettingsPreference = findPreference(KEY_WRITE_SETTINGS_APPS);
     }
 
     @Override
@@ -111,7 +86,7 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
 
     @Override
     public void onPackageListChanged() {
-        updateUI();
+        // No-op.
     }
 
     @Override
@@ -159,4 +134,50 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements
             }
         }
     };
+
+    private class CountAppsWithOverlayPermission extends
+            AsyncTask<AppStateOverlayBridge, Void, Integer> {
+        int numOfPackagesRequestedPermission = 0;
+
+        @Override
+        protected Integer doInBackground(AppStateOverlayBridge... params) {
+            AppStateOverlayBridge overlayBridge = params[0];
+            numOfPackagesRequestedPermission = overlayBridge
+                    .getNumberOfPackagesWithPermission();
+            return overlayBridge.getNumberOfPackagesCanDrawOverlay();
+        }
+
+        @Override
+        protected void onPostExecute(Integer result) {
+            // checks if fragment is still there before updating the preference object
+            if (isAdded()) {
+                mSystemAlertWindowPreference.setSummary(getContext().getString(
+                        R.string.system_alert_window_summary, result,
+                        numOfPackagesRequestedPermission));
+            }
+        }
+    }
+
+    private class CountAppsWithWriteSettingsPermission extends
+        AsyncTask<AppStateWriteSettingsBridge, Void, Integer> {
+        int numOfPackagesRequestedPermission = 0;
+
+        @Override
+        protected Integer doInBackground(AppStateWriteSettingsBridge... params) {
+            AppStateWriteSettingsBridge writeSettingsBridge = params[0];
+            numOfPackagesRequestedPermission = writeSettingsBridge
+                .getNumberOfPackagesWithPermission();
+            return writeSettingsBridge.getNumberOfPackagesCanWriteSettings();
+        }
+
+        @Override
+        protected void onPostExecute(Integer result) {
+            // checks if fragment is still there before updating the preference object
+            if (isAdded()) {
+                mWriteSettingsPreference.setSummary(getContext().getString(
+                        R.string.write_settings_summary, result,
+                        numOfPackagesRequestedPermission));
+            }
+        }
+    }
 }
index 7e29a20..5d6bdb0 100644 (file)
@@ -20,11 +20,12 @@ import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.TextView;
-import com.android.settings.accessibility.ListDialogPreference;
 
+import com.android.settings.accessibility.ListDialogPreference;
 import com.android.settings.R;
 
 public class AppDomainsPreference extends ListDialogPreference {
+    private int mNumEntries;
 
     public AppDomainsPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -34,6 +35,27 @@ public class AppDomainsPreference extends ListDialogPreference {
     }
 
     @Override
+    public void setTitles(CharSequence[] titles) {
+        mNumEntries = (titles != null) ? titles.length : 0;
+        super.setTitles(titles);
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        final Context context = getContext();
+        if (mNumEntries == 0) {
+            return context.getString(R.string.domain_urls_summary_none);
+        }
+
+        // The superclass summary is the text of the first entry in the list
+        final CharSequence summary = super.getSummary();
+        final int whichVersion = (mNumEntries == 1)
+                ? R.string.domain_urls_summary_one
+                : R.string.domain_urls_summary_some;
+        return context.getString(whichVersion, summary);
+    }
+
+    @Override
     protected void onBindListItem(View view, int index) {
         final CharSequence title = getTitleAt(index);
         if (title != null) {
index 05a1fca..c885b02 100644 (file)
 package com.android.settings.applications;
 
 import android.app.AlertDialog;
-import android.content.ComponentName;
-import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.preference.Preference;
 import android.preference.SwitchPreference;
 import android.util.ArraySet;
+import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.settings.DropDownPreference;
+import com.android.settings.DropDownPreference.Callback;
 import com.android.settings.R;
+import com.android.settings.Utils;
 
-import java.util.List;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
 
-import static android.content.pm.PackageManager.GET_ACTIVITIES;
-import static android.content.pm.PackageManager.GET_META_DATA;
-import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
+import java.util.List;
 
 public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListener,
         Preference.OnPreferenceChangeListener {
+    private static final String TAG = "AppLaunchSettings";
 
-    private static final String KEY_OPEN_DOMAIN_URLS = "app_launch_open_domain_urls";
+    private static final String KEY_APP_LINK_STATE = "app_link_state";
     private static final String KEY_SUPPORTED_DOMAIN_URLS = "app_launch_supported_domain_urls";
     private static final String KEY_CLEAR_DEFAULTS = "app_launch_clear_defaults";
 
     private PackageManager mPm;
 
-    private SwitchPreference mOpenDomainUrls;
+    private boolean mHasDomainUrls;
+    private DropDownPreference mAppLinkState;
     private AppDomainsPreference mAppDomainUrls;
     private ClearDefaultsPreference mClearDefaultsPreference;
 
@@ -61,61 +66,88 @@ public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListe
         addPreferencesFromResource(R.xml.installed_app_launch_settings);
 
         mPm = getActivity().getPackageManager();
-        final int myUserId = UserHandle.myUserId();
-
-        mOpenDomainUrls = (SwitchPreference) findPreference(KEY_OPEN_DOMAIN_URLS);
-        mOpenDomainUrls.setOnPreferenceChangeListener(this);
 
-        boolean hasDomainUrls =
+        mHasDomainUrls =
                 (mAppEntry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
         List<IntentFilterVerificationInfo> iviList = mPm.getIntentFilterVerifications(mPackageName);
 
-        boolean enabled = hasDomainUrls && (iviList.size() != 0);
-
-        mOpenDomainUrls.setEnabled(enabled);
-        if (enabled) {
-            final int status = mPm.getIntentVerificationStatus(mPackageName, myUserId);
-            boolean checked =
-                    (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
-            mOpenDomainUrls.setChecked(checked);
-        }
-
         List<IntentFilter> filters = mPm.getAllIntentFilters(mPackageName);
 
         mAppDomainUrls = (AppDomainsPreference) findPreference(KEY_SUPPORTED_DOMAIN_URLS);
-        CharSequence[] entries = getEntries(iviList, filters);
+        CharSequence[] entries = getEntries(mPackageName, iviList, filters);
         mAppDomainUrls.setTitles(entries);
         mAppDomainUrls.setValues(new int[entries.length]);
 
         mClearDefaultsPreference = (ClearDefaultsPreference) findPreference(KEY_CLEAR_DEFAULTS);
+
+        buildStateDropDown();
     }
 
-    private CharSequence[] getEntries(List<IntentFilterVerificationInfo> iviList,
-            List<IntentFilter> filters) {
-        ArraySet<String> result = new ArraySet<>();
-        if (iviList.size() > 0) {
-            for (IntentFilterVerificationInfo ivi : iviList) {
-                for (String host : ivi.getDomains()) {
-                    result.add(host);
+    private void buildStateDropDown() {
+        mAppLinkState = (DropDownPreference) findPreference(KEY_APP_LINK_STATE);
+
+        // Designed order of states in the dropdown:
+        //
+        // * always
+        // * ask
+        // * never
+        mAppLinkState.addItem(R.string.app_link_open_always,
+                INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
+        mAppLinkState.addItem(R.string.app_link_open_ask,
+                INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK);
+        mAppLinkState.addItem(R.string.app_link_open_never,
+                INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER);
+
+        mAppLinkState.setEnabled(mHasDomainUrls);
+        if (mHasDomainUrls) {
+            // Present 'undefined' as 'ask' because the OS treats them identically for
+            // purposes of the UI (and does the right thing around pending domain
+            // verifications that might arrive after the user chooses 'ask' in this UI).
+            final int state = mPm.getIntentVerificationStatus(mPackageName, UserHandle.myUserId());
+            mAppLinkState.setSelectedValue(
+                    (state == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)
+                        ? INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK
+                        : state);
+
+            // Set the callback only after setting the initial selected item
+            mAppLinkState.setCallback(new Callback() {
+                @Override
+                public boolean onItemSelected(int pos, Object value) {
+                    return updateAppLinkState((Integer) value);
                 }
-            }
+            });
         }
-        if (filters != null && filters.size() > 0) {
-            for (IntentFilter filter : filters) {
-                if (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
-                        filter.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
-                    result.addAll(filter.getHostsList());
-                }
-            }
+    }
+
+    private boolean updateAppLinkState(final int newState) {
+        final int userId = UserHandle.myUserId();
+        final int priorState = mPm.getIntentVerificationStatus(mPackageName, userId);
+
+        if (priorState == newState) {
+            return false;
+        }
+
+        boolean success = mPm.updateIntentVerificationStatus(mPackageName, newState, userId);
+        if (success) {
+            // Read back the state to see if the change worked
+            final int updatedState = mPm.getIntentVerificationStatus(mPackageName, userId);
+            success = (newState == updatedState);
+        } else {
+            Log.e(TAG, "Couldn't update intent verification status!");
         }
-        return result.toArray(new CharSequence[0]);
+        return success;
+    }
+
+    private CharSequence[] getEntries(String packageName, List<IntentFilterVerificationInfo> iviList,
+            List<IntentFilter> filters) {
+        ArraySet<String> result = Utils.getHandledDomains(mPm, packageName);
+        return result.toArray(new CharSequence[result.size()]);
     }
 
     @Override
     protected boolean refreshUi() {
         mClearDefaultsPreference.setPackageName(mPackageName);
         mClearDefaultsPreference.setAppEntry(mAppEntry);
-
         return true;
     }
 
@@ -125,7 +157,6 @@ public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListe
         return null;
     }
 
-
     @Override
     public void onClick(View v) {
         // Nothing to do
@@ -133,16 +164,8 @@ public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListe
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
-        boolean ret = false;
-        final String key = preference.getKey();
-        if (KEY_OPEN_DOMAIN_URLS.equals(key)) {
-            SwitchPreference pref = (SwitchPreference) preference;
-            int status = !pref.isChecked() ?
-                    PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS :
-                    PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-            ret = mPm.updateIntentVerificationStatus(mPackageName, status, UserHandle.myUserId());
-        }
-        return ret;
+        // actual updates are handled by the app link dropdown callback
+        return true;
     }
 
     @Override
diff --git a/src/com/android/settings/applications/AppStateAppOpsBridge.java b/src/com/android/settings/applications/AppStateAppOpsBridge.java
new file mode 100644 (file)
index 0000000..ebff54e
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * 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.applications;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.PackageOps;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.AppFilter;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/*
+ * Connects app ops info to the ApplicationsState. Makes use of AppOpsManager to
+ * determine further permission level.
+ */
+public abstract class AppStateAppOpsBridge extends AppStateBaseBridge {
+
+    private static final String TAG = "AppStateAppOpsBridge";
+
+    private final IPackageManager mIPackageManager;
+    private final UserManager mUserManager;
+    private final List<UserHandle> mProfiles;
+    private final AppOpsManager mAppOpsManager;
+    private final Context mContext;
+    private final int[] mAppOpsOpCodes;
+    private final String[] mPermissions;
+
+    public AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback,
+            int appOpsOpCode, String[] permissions) {
+        super(appState, callback);
+        mContext = context;
+        mIPackageManager = AppGlobals.getPackageManager();
+        mUserManager = UserManager.get(context);
+        mProfiles = mUserManager.getUserProfiles();
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mAppOpsOpCodes = new int[] {appOpsOpCode};
+        mPermissions = permissions;
+    }
+
+    private boolean isThisUserAProfileOfCurrentUser(final int userId) {
+        final int profilesMax = mProfiles.size();
+        for (int i = 0; i < profilesMax; i++) {
+            if (mProfiles.get(i).getIdentifier() == userId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected abstract void updateExtraInfo(AppEntry app, String pkg, int uid);
+
+    private boolean doesAnyPermissionMatch(String permissionToMatch, String[] permissions) {
+        for (String permission : permissions) {
+            if (permissionToMatch.equals(permission)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public PermissionState getPermissionInfo(String pkg, int uid) {
+        PermissionState permissionState = new PermissionState(pkg, new UserHandle(UserHandle
+                .getUserId(uid)));
+        try {
+            permissionState.packageInfo = mIPackageManager.getPackageInfo(pkg,
+                    PackageManager.GET_PERMISSIONS, permissionState.userHandle.getIdentifier());
+            // Check static permission state (whatever that is declared in package manifest)
+            String[] requestedPermissions = permissionState.packageInfo.requestedPermissions;
+            int[] permissionFlags = permissionState.packageInfo.requestedPermissionsFlags;
+            if (requestedPermissions != null) {
+                for (int i = 0; i < requestedPermissions.length; i++) {
+                    if (doesAnyPermissionMatch(requestedPermissions[i], mPermissions)) {
+                        permissionState.permissionDeclared = true;
+                        if ((permissionFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
+                            permissionState.staticPermissionGranted = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            // Check app op state.
+            List<PackageOps> ops = mAppOpsManager.getOpsForPackage(uid, pkg, mAppOpsOpCodes);
+            if (ops != null && ops.size() > 0 && ops.get(0).getOps().size() > 0) {
+                permissionState.appOpMode = ops.get(0).getOps().get(0).getMode();
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "PackageManager is dead. Can't get package info " + pkg, e);
+        }
+        return permissionState;
+    }
+
+    @Override
+    protected void loadAllExtraInfo() {
+        SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
+
+        // Load state info.
+        loadPermissionsStates(entries);
+        loadAppOpsStates(entries);
+
+        // Map states to application info.
+        List<AppEntry> apps = mAppSession.getAllApps();
+        final int N = apps.size();
+        for (int i = 0; i < N; i++) {
+            AppEntry app = apps.get(i);
+            int userId = UserHandle.getUserId(app.info.uid);
+            ArrayMap<String, PermissionState> userMap = entries.get(userId);
+            app.extraInfo = userMap != null ? userMap.get(app.info.packageName) : null;
+        }
+    }
+
+    /*
+     * Gets a sparse array that describes every user on the device and all the associated packages
+     * of each user, together with the packages available for that user.
+     */
+    private SparseArray<ArrayMap<String, PermissionState>> getEntries() {
+        try {
+            Set<String> packagesSet = new HashSet<>();
+            for (String permission : mPermissions) {
+                String[] pkgs = mIPackageManager.getAppOpPermissionPackages(permission);
+                if (pkgs != null) {
+                    packagesSet.addAll(Arrays.asList(pkgs));
+                }
+            }
+
+            if (packagesSet.isEmpty()) {
+                // No packages are requesting permission as specified by mPermissions.
+                return null;
+            }
+
+            // Create a sparse array that maps profileIds to an ArrayMap that maps package names to
+            // an associated PermissionState object
+            SparseArray<ArrayMap<String, PermissionState>> entries = new SparseArray<>();
+            for (final UserHandle profile : mProfiles) {
+                final ArrayMap<String, PermissionState> entriesForProfile = new ArrayMap<>();
+                final int profileId = profile.getIdentifier();
+                entries.put(profileId, entriesForProfile);
+                for (final String packageName : packagesSet) {
+                    final boolean isAvailable = mIPackageManager.isPackageAvailable(packageName,
+                            profileId);
+                    if (!shouldIgnorePackage(packageName) && isAvailable) {
+                        final PermissionState newEntry = new PermissionState(packageName, profile);
+                        entriesForProfile.put(packageName, newEntry);
+                    }
+                }
+            }
+
+            return entries;
+        } catch (RemoteException e) {
+            Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting "
+                    + mPermissions[0], e);
+            return null;
+        }
+    }
+
+    /*
+     * This method will set the packageInfo and staticPermissionGranted field of the associated
+     * PermissionState, which describes a particular package.
+     */
+    private void loadPermissionsStates(SparseArray<ArrayMap<String, PermissionState>> entries) {
+         // Load the packages that have been granted the permission specified in mPermission.
+        try {
+            for (final UserHandle profile : mProfiles) {
+                final int profileId = profile.getIdentifier();
+                final ArrayMap<String, PermissionState> entriesForProfile = entries.get(profileId);
+                if (entriesForProfile == null) {
+                    continue;
+                }
+                @SuppressWarnings("unchecked")
+                final List<PackageInfo> packageInfos = mIPackageManager
+                        .getPackagesHoldingPermissions(mPermissions, 0, profileId).getList();
+                final int packageInfoCount = packageInfos != null ? packageInfos.size() : 0;
+                for (int i = 0; i < packageInfoCount; i++) {
+                    final PackageInfo packageInfo = packageInfos.get(i);
+                    final PermissionState pe = entriesForProfile.get(packageInfo.packageName);
+                    if (pe != null) {
+                        pe.packageInfo = packageInfo;
+                        pe.staticPermissionGranted = true;
+                    }
+                }
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "PackageManager is dead. Can't get list of packages granted "
+                    + mPermissions, e);
+            return;
+        }
+    }
+
+    /*
+     * This method will set the appOpMode field of the associated PermissionState, which describes
+     * a particular package.
+     */
+    private void loadAppOpsStates(SparseArray<ArrayMap<String, PermissionState>> entries) {
+        // Find out which packages have been granted permission from AppOps.
+        final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps(
+                mAppOpsOpCodes);
+        final int packageOpsCount = packageOps != null ? packageOps.size() : 0;
+        for (int i = 0; i < packageOpsCount; i++) {
+            final AppOpsManager.PackageOps packageOp = packageOps.get(i);
+            final int userId = UserHandle.getUserId(packageOp.getUid());
+            if (!isThisUserAProfileOfCurrentUser(userId)) {
+                // This AppOp does not belong to any of this user's profiles.
+                continue;
+            }
+
+            final ArrayMap<String, PermissionState> entriesForProfile = entries.get(userId);
+            if (entriesForProfile == null) {
+                continue;
+            }
+            final PermissionState pe = entriesForProfile.get(packageOp.getPackageName());
+            if (pe == null) {
+                Log.w(TAG, "AppOp permission exists for package " + packageOp.getPackageName()
+                        + " of user " + userId + " but package doesn't exist or did not request "
+                        + mPermissions + " access");
+                continue;
+            }
+
+            if (packageOp.getOps().size() < 1) {
+                Log.w(TAG, "No AppOps permission exists for package " + packageOp.getPackageName());
+                continue;
+            }
+            pe.appOpMode = packageOp.getOps().get(0).getMode();
+        }
+    }
+
+    /*
+     * Check for packages that should be ignored for further processing
+     */
+    private boolean shouldIgnorePackage(String packageName) {
+        return packageName.equals("android") || packageName.equals(mContext.getPackageName());
+    }
+
+    public int getNumPackagesDeclaredPermission() {
+        SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
+        if (entries == null) {
+            return 0;
+        }
+        final ArrayMap<String, PermissionState> entriesForProfile = entries.get(mUserManager
+                .getUserHandle());
+        if (entriesForProfile == null) {
+            return 0;
+        }
+        return entriesForProfile.size();
+    }
+
+    public int getNumPackagesAllowedByAppOps() {
+        SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
+        if (entries == null) {
+            return 0;
+        }
+        loadPermissionsStates(entries);
+        loadAppOpsStates(entries);
+        final ArrayMap<String, PermissionState> entriesForProfile = entries.get(mUserManager
+                .getUserHandle());
+        if (entriesForProfile == null) {
+            return 0;
+        }
+        Collection<PermissionState> permStates = entriesForProfile.values();
+        int result = 0;
+        for (PermissionState permState : permStates) {
+            if (permState.isPermissible()) {
+                result++;
+            }
+        }
+        return result;
+    }
+
+    public static class PermissionState {
+        public final String packageName;
+        public final UserHandle userHandle;
+        public PackageInfo packageInfo;
+        public boolean staticPermissionGranted;
+        public boolean permissionDeclared;
+        public int appOpMode;
+
+        public PermissionState(String packageName, UserHandle userHandle) {
+            this.packageName = packageName;
+            this.appOpMode = AppOpsManager.MODE_DEFAULT;
+            this.userHandle = userHandle;
+        }
+
+        public boolean isPermissible() {
+            // defining the default behavior as permissible as long as the package requested this
+            // permission (this means pre-M gets approval during install time; M apps gets approval
+            // during runtime.
+            if (appOpMode == AppOpsManager.MODE_DEFAULT) {
+                return staticPermissionGranted;
+            }
+            return appOpMode == AppOpsManager.MODE_ALLOWED;
+        }
+    }
+}
diff --git a/src/com/android/settings/applications/AppStateOverlayBridge.java b/src/com/android/settings/applications/AppStateOverlayBridge.java
new file mode 100644 (file)
index 0000000..71a6037
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.applications;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.AppFilter;
+
+/*
+ * Connects info of apps that draw overlay to the ApplicationsState. Wraps around the generic
+ * AppStateAppOpsBridge class to tailor to the semantics of SYSTEM_ALERT_WINDOW. Also provides app
+ * filters that can use the info.
+ */
+public class AppStateOverlayBridge extends AppStateAppOpsBridge {
+
+    private static final String TAG = "AppStateOverlayBridge";
+    private static final int APP_OPS_OP_CODE = AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+    private static final String PM_SYSTEM_ALERT_WINDOW = Manifest.permission.SYSTEM_ALERT_WINDOW;
+    private static final String[] PM_PERMISSION = {
+            PM_SYSTEM_ALERT_WINDOW
+    };
+
+    public AppStateOverlayBridge(Context context, ApplicationsState appState, Callback callback) {
+        super(context, appState, callback, APP_OPS_OP_CODE, PM_PERMISSION);
+    }
+
+    @Override
+    protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
+        app.extraInfo = getOverlayInfo(pkg, uid);
+    }
+
+    public OverlayState getOverlayInfo(String pkg, int uid) {
+        PermissionState permissionState = super.getPermissionInfo(pkg, uid);
+        return new OverlayState(permissionState);
+    }
+
+    // TODO: figure out how to filter out system apps for this method
+    public int getNumberOfPackagesWithPermission() {
+        return super.getNumPackagesDeclaredPermission();
+    }
+
+    // TODO: figure out how to filter out system apps for this method
+    public int getNumberOfPackagesCanDrawOverlay() {
+        return super.getNumPackagesAllowedByAppOps();
+    }
+
+    public static class OverlayState extends AppStateAppOpsBridge.PermissionState {
+
+        public OverlayState(PermissionState permissionState) {
+            super(permissionState.packageName, permissionState.userHandle);
+            this.packageInfo = permissionState.packageInfo;
+            this.appOpMode = permissionState.appOpMode;
+            this.permissionDeclared = permissionState.permissionDeclared;
+            this.staticPermissionGranted = permissionState.staticPermissionGranted;
+        }
+    }
+
+    public static final AppFilter FILTER_SYSTEM_ALERT_WINDOW = new AppFilter() {
+        @Override
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry info) {
+            return info.extraInfo != null;
+        }
+    };
+}
index 069c901..e25b9c9 100644 (file)
@@ -56,7 +56,7 @@ public class AppStatePowerBridge extends AppStateBaseBridge {
     }
 
     public static final AppFilter FILTER_POWER_WHITELISTED = new CompoundFilter(
-            ApplicationsState.FILTER_PERSONAL, new AppFilter() {
+            ApplicationsState.FILTER_PERSONAL_WITHOUT_DISABLED_UNTIL_USED, new AppFilter() {
         @Override
         public void init() {
         }
index c06492c..70a3ae6 100644 (file)
 package com.android.settings.applications;
 
 import android.Manifest;
-import android.app.AppGlobals;
 import android.app.AppOpsManager;
-import android.app.AppOpsManager.PackageOps;
 import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.ArrayMap;
 import android.util.Log;
-import android.util.SparseArray;
 
 import com.android.settingslib.applications.ApplicationsState;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
 import com.android.settingslib.applications.ApplicationsState.AppFilter;
 
-import java.util.List;
-
 /*
- * Connects app usage info to the ApplicationsState.
- * Also provides app filters that can use the info.
+ * Connects app usage info to the ApplicationsState. Wraps around the generic AppStateAppOpsBridge
+ * class to tailor to the semantics of PACKAGE_USAGE_STATS. Also provides app filters that can use
+ * the info.
  */
-public class AppStateUsageBridge extends AppStateBaseBridge {
+public class AppStateUsageBridge extends AppStateAppOpsBridge {
 
     private static final String TAG = "AppStateUsageBridge";
 
-    private static final String[] PM_USAGE_STATS_PERMISSION = {
-            Manifest.permission.PACKAGE_USAGE_STATS
-    };
-
-    private static final int[] APP_OPS_OP_CODES = {
-            AppOpsManager.OP_GET_USAGE_STATS
+    private static final String PM_USAGE_STATS = Manifest.permission.PACKAGE_USAGE_STATS;
+    private static final int APP_OPS_OP_CODE = AppOpsManager.OP_GET_USAGE_STATS;
+    private static final String[] PM_PERMISSION = {
+            PM_USAGE_STATS
     };
 
-    private final IPackageManager mIPackageManager;
-    private final UserManager mUserManager;
-    private final List<UserHandle> mProfiles;
-    private final AppOpsManager mAppOpsManager;
-    private final Context mContext;
-
     public AppStateUsageBridge(Context context, ApplicationsState appState, Callback callback) {
-        super(appState, callback);
-        mContext = context;
-        mIPackageManager = AppGlobals.getPackageManager();
-        mUserManager = UserManager.get(context);
-        mProfiles = mUserManager.getUserProfiles();
-        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-    }
-
-    private boolean isThisUserAProfileOfCurrentUser(final int userId) {
-        final int profilesMax = mProfiles.size();
-        for (int i = 0; i < profilesMax; i++) {
-            if (mProfiles.get(i).getIdentifier() == userId) {
-                return true;
-            }
-        }
-        return false;
+        super(context, appState, callback, APP_OPS_OP_CODE, PM_PERMISSION);
     }
 
     @Override
@@ -83,173 +49,18 @@ public class AppStateUsageBridge extends AppStateBaseBridge {
     }
 
     public UsageState getUsageInfo(String pkg, int uid) {
-        UsageState usageState = new UsageState(pkg, new UserHandle(UserHandle.getUserId(uid)));
-        try {
-            usageState.packageInfo = mIPackageManager.getPackageInfo(pkg,
-                    PackageManager.GET_PERMISSIONS, usageState.userHandle.getIdentifier());
-            // Check permission state.
-            String[] requestedPermissions = usageState.packageInfo.requestedPermissions;
-            int[] permissionFlags = usageState.packageInfo.requestedPermissionsFlags;
-            if (requestedPermissions != null) {
-                for (int i = 0; i < requestedPermissions.length; i++) {
-                    if (Manifest.permission.PACKAGE_USAGE_STATS.equals(requestedPermissions[i])
-                            && (permissionFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED)
-                            != 0) {
-                        usageState.permissionGranted = true;
-                        break;
-                    }
-                }
-            }
-            // Check app op state.
-            List<PackageOps> ops = mAppOpsManager.getOpsForPackage(uid, pkg, APP_OPS_OP_CODES);
-            if (ops != null && ops.size() > 0 && ops.get(0).getOps().size() > 0) {
-                usageState.appOpMode = ops.get(0).getOps().get(0).getMode();
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, "PackageManager is dead. Can't get package info " + pkg, e);
-        }
-        return usageState;
+        PermissionState permissionState = super.getPermissionInfo(pkg, uid);
+        return new UsageState(permissionState);
     }
 
-    @Override
-    protected void loadAllExtraInfo() {
-        SparseArray<ArrayMap<String, UsageState>> entries = getEntries();
-
-        // Load state info.
-        loadPermissionsStates(entries);
-        loadAppOpsStates(entries);
-
-        // Map states to application info.
-        List<AppEntry> apps = mAppSession.getAllApps();
-        final int N = apps.size();
-        for (int i = 0; i < N; i++) {
-            AppEntry app = apps.get(i);
-            int userId = UserHandle.getUserId(app.info.uid);
-            ArrayMap<String, UsageState> userMap = entries.get(userId);
-            app.extraInfo = userMap != null ? userMap.get(app.info.packageName) : null;
-        }
-    }
-
-    private SparseArray<ArrayMap<String, UsageState>> getEntries() {
-        try {
-            final String[] packages = mIPackageManager.getAppOpPermissionPackages(
-                    Manifest.permission.PACKAGE_USAGE_STATS);
-
-            if (packages == null) {
-                // No packages are requesting permission to use the UsageStats API.
-                return null;
-            }
-
-            SparseArray<ArrayMap<String, UsageState>> entries = new SparseArray<>();
-            for (final UserHandle profile : mProfiles) {
-                final ArrayMap<String, UsageState> entriesForProfile = new ArrayMap<>();
-                final int profileId = profile.getIdentifier();
-                entries.put(profileId, entriesForProfile);
-                for (final String packageName : packages) {
-                    final boolean isAvailable = mIPackageManager.isPackageAvailable(packageName,
-                            profileId);
-                    if (!shouldIgnorePackage(packageName) && isAvailable) {
-                        final UsageState newEntry = new UsageState(packageName, profile);
-                        entriesForProfile.put(packageName, newEntry);
-                    }
-                }
-            }
-
-            return entries;
-        } catch (RemoteException e) {
-            Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting "
-                    + Manifest.permission.PACKAGE_USAGE_STATS, e);
-            return null;
-        }
-    }
-
-    private void loadPermissionsStates(SparseArray<ArrayMap<String, UsageState>> entries) {
-         // Load the packages that have been granted the PACKAGE_USAGE_STATS permission.
-        try {
-            for (final UserHandle profile : mProfiles) {
-                final int profileId = profile.getIdentifier();
-                final ArrayMap<String, UsageState> entriesForProfile = entries.get(profileId);
-                if (entriesForProfile == null) {
-                    continue;
-                }
-                @SuppressWarnings("unchecked")
-                final List<PackageInfo> packageInfos = mIPackageManager
-                        .getPackagesHoldingPermissions(PM_USAGE_STATS_PERMISSION, 0, profileId)
-                        .getList();
-                final int packageInfoCount = packageInfos != null ? packageInfos.size() : 0;
-                for (int i = 0; i < packageInfoCount; i++) {
-                    final PackageInfo packageInfo = packageInfos.get(i);
-                    final UsageState pe = entriesForProfile.get(packageInfo.packageName);
-                    if (pe != null) {
-                        pe.packageInfo = packageInfo;
-                        pe.permissionGranted = true;
-                    }
-                }
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, "PackageManager is dead. Can't get list of packages granted "
-                    + Manifest.permission.PACKAGE_USAGE_STATS, e);
-            return;
-        }
-    }
-
-    private void loadAppOpsStates(SparseArray<ArrayMap<String, UsageState>> entries) {
-        // Find out which packages have been granted permission from AppOps.
-        final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps(
-                APP_OPS_OP_CODES);
-        final int packageOpsCount = packageOps != null ? packageOps.size() : 0;
-        for (int i = 0; i < packageOpsCount; i++) {
-            final AppOpsManager.PackageOps packageOp = packageOps.get(i);
-            final int userId = UserHandle.getUserId(packageOp.getUid());
-            if (!isThisUserAProfileOfCurrentUser(userId)) {
-                // This AppOp does not belong to any of this user's profiles.
-                continue;
-            }
-
-            final ArrayMap<String, UsageState> entriesForProfile = entries.get(userId);
-            if (entriesForProfile == null) {
-                continue;
-            }
-            final UsageState pe = entriesForProfile.get(packageOp.getPackageName());
-            if (pe == null) {
-                Log.w(TAG, "AppOp permission exists for package " + packageOp.getPackageName()
-                        + " of user " + userId +
-                        " but package doesn't exist or did not request UsageStats access");
-                continue;
-            }
-
-            if (packageOp.getOps().size() < 1) {
-                Log.w(TAG, "No AppOps permission exists for package "
-                        + packageOp.getPackageName());
-                continue;
-            }
-
-            pe.appOpMode = packageOp.getOps().get(0).getMode();
-        }
-    }
-
-    private boolean shouldIgnorePackage(String packageName) {
-        return packageName.equals("android") || packageName.equals(mContext.getPackageName());
-    }
-
-    public static class UsageState {
-        public final String packageName;
-        public final UserHandle userHandle;
-        public PackageInfo packageInfo;
-        public boolean permissionGranted;
-        public int appOpMode;
-
-        public UsageState(String packageName, UserHandle userHandle) {
-            this.packageName = packageName;
-            this.appOpMode = AppOpsManager.MODE_DEFAULT;
-            this.userHandle = userHandle;
-        }
+    public static class UsageState extends AppStateAppOpsBridge.PermissionState {
 
-        public boolean hasAccess() {
-            if (appOpMode == AppOpsManager.MODE_DEFAULT) {
-                return permissionGranted;
-            }
-            return appOpMode == AppOpsManager.MODE_ALLOWED;
+        public UsageState(PermissionState permissionState) {
+            super(permissionState.packageName, permissionState.userHandle);
+            this.packageInfo = permissionState.packageInfo;
+            this.appOpMode = permissionState.appOpMode;
+            this.permissionDeclared = permissionState.permissionDeclared;
+            this.staticPermissionGranted = permissionState.staticPermissionGranted;
         }
     }
 
diff --git a/src/com/android/settings/applications/AppStateWriteSettingsBridge.java b/src/com/android/settings/applications/AppStateWriteSettingsBridge.java
new file mode 100644 (file)
index 0000000..7cdf7ea
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.applications;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.AppFilter;
+
+/*
+ * Connects info of apps that draw overlay to the ApplicationsState. Wraps around the generic
+ * AppStateAppOpsBridge class to tailor to the semantics of SYSTEM_ALERT_WINDOW. Also provides app
+ * filters that can use the info.
+ */
+public class AppStateWriteSettingsBridge extends AppStateAppOpsBridge {
+
+    private static final String TAG = "AppStateWriteSettingsBridge";
+    private static final int APP_OPS_OP_CODE = AppOpsManager.OP_WRITE_SETTINGS;
+    private static final String PM_WRITE_SETTINGS = Manifest.permission.WRITE_SETTINGS;
+    private static final String PM_CHANGE_NETWORK_STATE = Manifest.permission.CHANGE_NETWORK_STATE;
+    // CHANGE_NETWORK_STATE is now merged with WRITE_SETTINGS
+    private static final String[] PM_PERMISSIONS = {
+            PM_WRITE_SETTINGS,
+            PM_CHANGE_NETWORK_STATE
+    };
+
+    public AppStateWriteSettingsBridge(Context context, ApplicationsState appState, Callback
+            callback) {
+        super(context, appState, callback, APP_OPS_OP_CODE, PM_PERMISSIONS);
+    }
+
+    @Override
+    protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
+        app.extraInfo = getWriteSettingsInfo(pkg, uid);
+    }
+
+    public WriteSettingsState getWriteSettingsInfo(String pkg, int uid) {
+        PermissionState permissionState = super.getPermissionInfo(pkg, uid);
+        return new WriteSettingsState(permissionState);
+    }
+
+    // TODO: figure out how to filter out system apps for this method
+    public int getNumberOfPackagesWithPermission() {
+        return super.getNumPackagesDeclaredPermission();
+    }
+
+    // TODO: figure out how to filter out system apps for this method
+    public int getNumberOfPackagesCanWriteSettings() {
+        return super.getNumPackagesAllowedByAppOps();
+    }
+
+    public static class WriteSettingsState extends AppStateAppOpsBridge.PermissionState {
+        public WriteSettingsState(PermissionState permissionState) {
+            super(permissionState.packageName, permissionState.userHandle);
+            this.packageInfo = permissionState.packageInfo;
+            this.appOpMode = permissionState.appOpMode;
+            this.permissionDeclared = permissionState.permissionDeclared;
+            this.staticPermissionGranted = permissionState.staticPermissionGranted;
+        }
+    }
+
+    public static final AppFilter FILTER_WRITE_SETTINGS = new AppFilter() {
+        @Override
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry info) {
+            return info.extraInfo != null;
+        }
+    };
+}
index 739aa3c..29a4819 100644 (file)
@@ -18,15 +18,18 @@ package com.android.settings.applications;
 
 import android.app.ActivityManager;
 import android.app.AlertDialog;
+import android.app.AppGlobals;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageManager;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 import android.preference.Preference;
@@ -38,7 +41,6 @@ import android.view.View.OnClickListener;
 import android.widget.Button;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.settings.DropDownPreference;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.deviceinfo.StorageWizardMoveConfirm;
@@ -51,7 +53,7 @@ import java.util.List;
 import java.util.Objects;
 
 public class AppStorageSettings extends AppInfoWithHeader
-        implements OnClickListener, Callbacks, DropDownPreference.Callback {
+        implements OnClickListener, Callbacks, DialogInterface.OnClickListener {
     private static final String TAG = AppStorageSettings.class.getSimpleName();
 
     //internal constants used in Handler
@@ -70,7 +72,9 @@ public class AppStorageSettings extends AppInfoWithHeader
     private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
     private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 2;
 
-    private static final String KEY_MOVE_PREFERENCE = "app_location_setting";
+    private static final String KEY_STORAGE_USED = "storage_used";
+    private static final String KEY_CHANGE_STORAGE = "change_storage_button";
+    private static final String KEY_STORAGE_SPACE = "storage_space";
     private static final String KEY_STORAGE_CATEGORY = "storage_category";
 
     private static final String KEY_TOTAL_SIZE = "total_size";
@@ -94,7 +98,8 @@ public class AppStorageSettings extends AppInfoWithHeader
     private Button mClearDataButton;
     private Button mClearCacheButton;
 
-    private DropDownPreference mMoveDropDown;
+    private Preference mStorageUsed;
+    private Button mChangeStorageButton;
 
     private boolean mCanClearData = true;
     private boolean mHaveSizes = false;
@@ -113,6 +118,9 @@ public class AppStorageSettings extends AppInfoWithHeader
     private CharSequence mInvalidSizeStr;
     private CharSequence mComputingStr;
 
+    private VolumeInfo[] mCandidates;
+    private AlertDialog.Builder mDialogBuilder;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -146,8 +154,11 @@ public class AppStorageSettings extends AppInfoWithHeader
         mClearDataButton = (Button) ((LayoutPreference) findPreference(KEY_CLEAR_DATA))
                 .findViewById(R.id.button);
 
-        mMoveDropDown = (DropDownPreference) findPreference(KEY_MOVE_PREFERENCE);
-        mMoveDropDown.setCallback(this);
+        mStorageUsed = findPreference(KEY_STORAGE_USED);
+        mChangeStorageButton = (Button) ((LayoutPreference) findPreference(KEY_CHANGE_STORAGE))
+                .findViewById(R.id.button);
+        mChangeStorageButton.setText(R.string.change);
+        mChangeStorageButton.setOnClickListener(this);
 
         // Cache section
         mCacheSize = findPreference(KEY_CACHE_SIZE);
@@ -164,7 +175,7 @@ public class AppStorageSettings extends AppInfoWithHeader
                 mClearCacheObserver = new ClearCacheObserver();
             }
             mPm.deleteApplicationCacheFiles(mPackageName, mClearCacheObserver);
-        } else if(v == mClearDataButton) {
+        } else if (v == mClearDataButton) {
             if (mAppEntry.info.manageSpaceActivityName != null) {
                 if (!Utils.isMonkeyRunning()) {
                     Intent intent = new Intent(Intent.ACTION_DEFAULT);
@@ -175,15 +186,27 @@ public class AppStorageSettings extends AppInfoWithHeader
             } else {
                 showDialogInner(DLG_CLEAR_DATA, 0);
             }
+        } else if (v == mChangeStorageButton && mDialogBuilder != null && !isMoveInProgress()) {
+            mDialogBuilder.show();
+        }
+    }
+
+    private boolean isMoveInProgress() {
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        try {
+            // TODO: define a cleaner API for this
+            return pm.isPackageFrozen(mPackageName);
+        } catch (RemoteException e) {
+            return false;
         }
     }
 
     @Override
-    public boolean onItemSelected(int pos, Object value) {
+    public void onClick(DialogInterface dialog, int which) {
         final Context context = getActivity();
 
         // If not current volume, kick off move wizard
-        final VolumeInfo targetVol = (VolumeInfo) value;
+        final VolumeInfo targetVol = mCandidates[which];
         final VolumeInfo currentVol = context.getPackageManager().getPackageCurrentVolume(
                 mAppEntry.info);
         if (!Objects.equals(targetVol, currentVol)) {
@@ -192,8 +215,7 @@ public class AppStorageSettings extends AppInfoWithHeader
             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName);
             startActivity(intent);
         }
-
-        return true;
+        dialog.dismiss();
     }
 
     private String getSizeStr(long size) {
@@ -273,18 +295,23 @@ public class AppStorageSettings extends AppInfoWithHeader
     @Override
     protected boolean refreshUi() {
         retrieveAppEntry();
-        refreshButtons();
+        if (mAppEntry == null) {
+            return false;
+        }
         refreshSizeInfo();
 
         final VolumeInfo currentVol = getActivity().getPackageManager()
                 .getPackageCurrentVolume(mAppEntry.info);
-        mMoveDropDown.setSelectedValue(currentVol);
+        final StorageManager storage = getContext().getSystemService(StorageManager.class);
+        mStorageUsed.setSummary(storage.getBestVolumeDescription(currentVol));
+
+        refreshButtons();
 
         return true;
     }
 
     private void refreshButtons() {
-        initMoveDropDown();
+        initMoveDialog();
         initDataButtons();
     }
 
@@ -314,7 +341,7 @@ public class AppStorageSettings extends AppInfoWithHeader
         }
     }
 
-    private void initMoveDropDown() {
+    private void initMoveDialog() {
         final Context context = getActivity();
         final StorageManager storage = context.getSystemService(StorageManager.class);
 
@@ -323,14 +350,24 @@ public class AppStorageSettings extends AppInfoWithHeader
         if (candidates.size() > 1) {
             Collections.sort(candidates, VolumeInfo.getDescriptionComparator());
 
-            mMoveDropDown.clearItems();
-            for (VolumeInfo vol : candidates) {
-                final String volDescrip = storage.getBestVolumeDescription(vol);
-                mMoveDropDown.addItem(volDescrip, vol);
+            CharSequence[] labels = new CharSequence[candidates.size()];
+            int current = -1;
+            for (int i = 0; i < candidates.size(); i++) {
+                final String volDescrip = storage.getBestVolumeDescription(candidates.get(i));
+                if (Objects.equals(volDescrip, mStorageUsed.getSummary())) {
+                    current = i;
+                }
+                labels[i] = volDescrip;
             }
-            mMoveDropDown.setSelectable(!mAppControlRestricted);
+            mCandidates = candidates.toArray(new VolumeInfo[candidates.size()]);
+            mDialogBuilder = new AlertDialog.Builder(getContext())
+                    .setTitle(R.string.change_storage)
+                    .setSingleChoiceItems(labels, current, this)
+                    .setNegativeButton(R.string.cancel, null);
         } else {
-            removePreference(KEY_MOVE_PREFERENCE);
+            removePreference(KEY_STORAGE_USED);
+            removePreference(KEY_CHANGE_STORAGE);
+            removePreference(KEY_STORAGE_SPACE);
         }
     }
 
diff --git a/src/com/android/settings/applications/DrawOverlayDetails.java b/src/com/android/settings/applications/DrawOverlayDetails.java
new file mode 100644 (file)
index 0000000..ef92c21
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * 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.applications;
+
+import android.app.AlertDialog;
+import android.app.AppOpsManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.SwitchPreference;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.InstrumentedFragment;
+import com.android.settings.R;
+import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
+import com.android.settings.applications.AppStateOverlayBridge.OverlayState;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+
+import java.util.List;
+
+public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
+        OnPreferenceClickListener {
+
+    private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
+    private static final String KEY_APP_OPS_SETTINGS_PREFS = "app_ops_settings_preference";
+    private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
+    private static final String LOG_TAG = "DrawOverlayDetails";
+
+    private static final int [] APP_OPS_OP_CODE = {
+            AppOpsManager.OP_SYSTEM_ALERT_WINDOW
+    };
+
+    // Use a bridge to get the overlay details but don't initialize it to connect with all state.
+    // TODO: Break out this functionality into its own class.
+    private AppStateOverlayBridge mOverlayBridge;
+    private AppOpsManager mAppOpsManager;
+    private SwitchPreference mSwitchPref;
+    private Preference mOverlayPrefs;
+    private Preference mOverlayDesc;
+    private Intent mSettingsIntent;
+    private OverlayState mOverlayState;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Context context = getActivity();
+        mOverlayBridge = new AppStateOverlayBridge(context, mState, null);
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+
+        // find preferences
+        addPreferencesFromResource(R.xml.app_ops_permissions_details);
+        mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
+        mOverlayPrefs = findPreference(KEY_APP_OPS_SETTINGS_PREFS);
+        mOverlayDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
+
+        // set title/summary for all of them
+        getPreferenceScreen().setTitle(R.string.draw_overlay);
+        mSwitchPref.setTitle(R.string.permit_draw_overlay);
+        mOverlayPrefs.setTitle(R.string.app_overlay_permission_preference);
+        mOverlayDesc.setSummary(R.string.allow_overlay_description);
+
+        // install event listeners
+        mSwitchPref.setOnPreferenceChangeListener(this);
+        mOverlayPrefs.setOnPreferenceClickListener(this);
+
+        mSettingsIntent = new Intent(Intent.ACTION_MAIN)
+                .setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        if (preference == mOverlayPrefs) {
+            if (mSettingsIntent != null) {
+                try {
+                    getActivity().startActivityAsUser(mSettingsIntent, new UserHandle(mUserId));
+                } catch (ActivityNotFoundException e) {
+                    Log.w(LOG_TAG, "Unable to launch app draw overlay settings " + mSettingsIntent, e);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference == mSwitchPref) {
+            if (mOverlayState != null && (Boolean) newValue != mOverlayState.isPermissible()) {
+                setCanDrawOverlay(!mOverlayState.isPermissible());
+                refreshUi();
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private void setCanDrawOverlay(boolean newState) {
+        mAppOpsManager.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+                mPackageInfo.applicationInfo.uid, mPackageName, newState
+                ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
+    }
+
+    private boolean canDrawOverlay(String pkgName) {
+        int result = mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+                mPackageInfo.applicationInfo.uid, pkgName);
+        if (result == AppOpsManager.MODE_ALLOWED) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    protected boolean refreshUi() {
+        mOverlayState = mOverlayBridge.getOverlayInfo(mPackageName,
+                mPackageInfo.applicationInfo.uid);
+
+        boolean isAllowed = mOverlayState.isPermissible();
+        mSwitchPref.setChecked(isAllowed);
+        // you cannot ask a user to grant you a permission you did not have!
+        mSwitchPref.setEnabled(mOverlayState.permissionDeclared);
+        mOverlayPrefs.setEnabled(isAllowed);
+        getPreferenceScreen().removePreference(mOverlayPrefs);
+
+        return true;
+    }
+
+    @Override
+    protected AlertDialog createDialog(int id, int errorCode) {
+        return null;
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.SYSTEM_ALERT_WINDOW_APPS;
+    }
+
+    public static CharSequence getSummary(Context context, AppEntry entry) {
+        if (entry.extraInfo != null) {
+            return getSummary(context, new OverlayState((PermissionState)entry.extraInfo));
+        }
+
+        // fallback if for whatever reason entry.extrainfo is null - the result
+        // may be less accurate
+        return getSummary(context, entry.info.packageName);
+    }
+
+    public static CharSequence getSummary(Context context, OverlayState overlayState) {
+        return context.getString(overlayState.isPermissible() ?
+            R.string.system_alert_window_on : R.string.system_alert_window_off);
+    }
+
+    public static CharSequence getSummary(Context context, String pkg) {
+        // first check if pkg is a system pkg
+        PackageManager packageManager = context.getPackageManager();
+        int uid = -1;
+        try {
+            ApplicationInfo appInfo = packageManager.getApplicationInfo(pkg, 0);
+            uid = appInfo.uid;
+            if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                return context.getString(R.string.system_alert_window_on);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // pkg doesn't even exist?
+            Log.w(LOG_TAG, "Package " + pkg + " not found", e);
+            return context.getString(R.string.system_alert_window_off);
+        }
+
+        AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context
+                .APP_OPS_SERVICE);
+        if (uid == -1) {
+            return context.getString(R.string.system_alert_window_off);
+        }
+
+        int mode = appOpsManager.noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, pkg);
+        return context.getString((mode == AppOpsManager.MODE_ALLOWED) ?
+                R.string.system_alert_window_on : R.string.system_alert_window_off);
+    }
+}
index 94705d2..a6b35df 100755 (executable)
@@ -68,20 +68,20 @@ import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.settings.DataUsageSummary;
-import com.android.settings.DataUsageSummary.AppItem;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.Utils;
 import com.android.settings.applications.PermissionsSummaryHelper.PermissionsResultCallback;
 import com.android.settings.fuelgauge.BatteryEntry;
 import com.android.settings.fuelgauge.PowerUsageDetail;
-import com.android.settings.net.ChartData;
-import com.android.settings.net.ChartDataLoader;
 import com.android.settings.notification.AppNotificationSettings;
 import com.android.settings.notification.NotificationBackend;
 import com.android.settings.notification.NotificationBackend.AppRow;
-import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.AppItem;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.net.ChartData;
+import com.android.settingslib.net.ChartDataLoader;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -379,6 +379,9 @@ public class InstalledAppDetails extends AppInfoBase
 
     @Override
     public void onPrepareOptionsMenu(Menu menu) {
+        if (mFinishing) {
+            return;
+        }
         boolean showIt = true;
         if (mUpdatedSysApp) {
             showIt = false;
@@ -833,6 +836,9 @@ public class InstalledAppDetails extends AppInfoBase
 
         @Override
         protected ProcStatsPackageEntry doInBackground(Void... params) {
+            if (getActivity() == null) {
+                return null;
+            }
             if (mPackageInfo == null) {
                 return null;
             }
@@ -934,8 +940,7 @@ public class InstalledAppDetails extends AppInfoBase
 
         @Override
         public void onLoaderReset(Loader<ChartData> loader) {
-            mChartData = null;
-            mDataPreference.setSummary(getDataSummary());
+            // Leave last result.
         }
     };
 
index c1566d0..61c2ebb 100644 (file)
@@ -20,7 +20,6 @@ import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.Environment;
@@ -60,10 +59,14 @@ import com.android.settings.Settings.HighPowerApplicationsActivity;
 import com.android.settings.Settings.NotificationAppListActivity;
 import com.android.settings.Settings.StorageUseActivity;
 import com.android.settings.Settings.UsageAccessSettingsActivity;
+import com.android.settings.Settings.OverlaySettingsActivity;
+import com.android.settings.Settings.WriteSettingsActivity;
 import com.android.settings.SettingsActivity;
 import com.android.settings.Utils;
+import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
 import com.android.settings.applications.AppStateUsageBridge.UsageState;
 import com.android.settings.fuelgauge.HighPowerDetail;
+import com.android.settings.fuelgauge.PowerWhitelistBackend;
 import com.android.settings.notification.AppNotificationSettings;
 import com.android.settings.notification.NotificationBackend;
 import com.android.settings.notification.NotificationBackend.AppRow;
@@ -76,7 +79,6 @@ import com.android.settingslib.applications.ApplicationsState.VolumeFilter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.List;
 
 /**
  * Activity to pick an application that will be used to display installation information and
@@ -97,6 +99,8 @@ public class ManageApplications extends InstrumentedFragment
     public static final String EXTRA_VOLUME_NAME = "volumeName";
 
     private static final String EXTRA_SORT_ORDER = "sortOrder";
+    private static final String EXTRA_SHOW_SYSTEM = "showSystem";
+    private static final String EXTRA_HAS_ENTRIES = "hasEntries";
 
     // attributes used as keys when passing values to InstalledAppDetails activity
     public static final String APP_CHG = "chg";
@@ -124,6 +128,8 @@ public class ManageApplications extends InstrumentedFragment
     public static final int FILTER_APPS_WORK                    = 10;
     public static final int FILTER_APPS_WITH_DOMAIN_URLS        = 11;
     public static final int FILTER_APPS_USAGE_ACCESS            = 12;
+    public static final int FILTER_APPS_WITH_OVERLAY            = 13;
+    public static final int FILTER_APPS_WRITE_SETTINGS          = 14;
 
     // This is the string labels for the filter modes above, the order must be kept in sync.
     public static final int[] FILTER_LABELS = new int[] {
@@ -140,12 +146,16 @@ public class ManageApplications extends InstrumentedFragment
         R.string.filter_work_apps,     // Work
         R.string.filter_with_domain_urls_apps,     // Domain URLs
         R.string.filter_all_apps,      // Usage access screen, never displayed
+        R.string.filter_overlay_apps,   // Apps with overlay permission
+        R.string.filter_write_settings_apps,   // Apps that can write system settings
     };
     // This is the actual mapping to filters from FILTER_ constants above, the order must
     // be kept in sync.
     public static final AppFilter[] FILTERS = new AppFilter[] {
-        AppStatePowerBridge.FILTER_POWER_WHITELISTED,     // High power whitelist, on
-        ApplicationsState.FILTER_PERSONAL,    // All apps label, but personal filter
+        new CompoundFilter(AppStatePowerBridge.FILTER_POWER_WHITELISTED,
+                ApplicationsState.FILTER_ALL_ENABLED),     // High power whitelist, on
+        new CompoundFilter(ApplicationsState.FILTER_PERSONAL_WITHOUT_DISABLED_UNTIL_USED,
+                ApplicationsState.FILTER_ALL_ENABLED),     // All apps label, but personal filter
         ApplicationsState.FILTER_EVERYTHING,  // All apps
         ApplicationsState.FILTER_ALL_ENABLED, // Enabled
         ApplicationsState.FILTER_DISABLED,    // Disabled
@@ -157,6 +167,8 @@ public class ManageApplications extends InstrumentedFragment
         ApplicationsState.FILTER_WORK,        // Work
         ApplicationsState.FILTER_WITH_DOMAIN_URLS,   // Apps with Domain URLs
         AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
+        AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW,   // Apps that can draw overlays
+        AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS,  // Apps that can write system settings
     };
 
     // sort order
@@ -197,6 +209,8 @@ public class ManageApplications extends InstrumentedFragment
     public static final int LIST_TYPE_STORAGE      = 3;
     public static final int LIST_TYPE_USAGE_ACCESS = 4;
     public static final int LIST_TYPE_HIGH_POWER   = 5;
+    public static final int LIST_TYPE_OVERLAY      = 6;
+    public static final int LIST_TYPE_WRITE_SETTINGS = 7;
 
     private View mRootView;
 
@@ -244,16 +258,12 @@ public class ManageApplications extends InstrumentedFragment
             mListType = LIST_TYPE_HIGH_POWER;
             // Default to showing system.
             mShowSystem = true;
-            if (Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS.equals(intent.getAction())
-                    && intent.getData() != null) {
-                mCurrentPkgName = intent.getData().getSchemeSpecificPart();
-                if (mCurrentPkgName != null) {
-                    mCurrentUid = mApplicationsState.getEntry(mCurrentPkgName,
-                            UserHandle.myUserId()).info.uid;
-                    mFinishAfterDialog = true;
-                    startApplicationDetailsActivity();
-                }
-            }
+        } else if (className.equals(OverlaySettingsActivity.class.getName())) {
+            mListType = LIST_TYPE_OVERLAY;
+            getActivity().getActionBar().setTitle(R.string.system_alert_window_access_title);
+        } else if (className.equals(WriteSettingsActivity.class.getName())) {
+            mListType = LIST_TYPE_WRITE_SETTINGS;
+            getActivity().getActionBar().setTitle(R.string.write_settings_title);
         } else {
             mListType = LIST_TYPE_MAIN;
         }
@@ -261,6 +271,7 @@ public class ManageApplications extends InstrumentedFragment
 
         if (savedInstanceState != null) {
             mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
+            mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
         }
 
         mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
@@ -292,6 +303,10 @@ public class ManageApplications extends InstrumentedFragment
             lv.setTextFilterEnabled(true);
             mListView = lv;
             mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
+            if (savedInstanceState != null) {
+                mApplications.mHasReceivedLoadEntries =
+                        savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false);
+            }
             mListView.setAdapter(mApplications);
             mListView.setRecyclerListener(mApplications);
 
@@ -360,6 +375,10 @@ public class ManageApplications extends InstrumentedFragment
                 return FILTER_APPS_USAGE_ACCESS;
             case LIST_TYPE_HIGH_POWER:
                 return FILTER_APPS_POWER_WHITELIST;
+            case LIST_TYPE_OVERLAY:
+                return FILTER_APPS_WITH_OVERLAY;
+            case LIST_TYPE_WRITE_SETTINGS:
+                return FILTER_APPS_WRITE_SETTINGS;
             default:
                 return FILTER_APPS_ALL;
         }
@@ -380,6 +399,10 @@ public class ManageApplications extends InstrumentedFragment
                 return MetricsLogger.USAGE_ACCESS;
             case LIST_TYPE_HIGH_POWER:
                 return MetricsLogger.APPLICATIONS_HIGH_POWER_APPS;
+            case LIST_TYPE_OVERLAY:
+                return MetricsLogger.SYSTEM_ALERT_WINDOW_APPS;
+            case LIST_TYPE_WRITE_SETTINGS:
+                return MetricsLogger.SYSTEM_ALERT_WINDOW_APPS;
             default:
                 return MetricsLogger.VIEW_UNKNOWN;
         }
@@ -401,6 +424,8 @@ public class ManageApplications extends InstrumentedFragment
         super.onSaveInstanceState(outState);
         mResetAppsHelper.onSaveInstanceState(outState);
         outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
+        outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
+        outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries);
     }
 
     @Override
@@ -432,7 +457,8 @@ public class ManageApplications extends InstrumentedFragment
         if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
             if (mListType == LIST_TYPE_NOTIFICATION) {
                 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
-            } else if (mListType == LIST_TYPE_HIGH_POWER) {
+            } else if (mListType == LIST_TYPE_HIGH_POWER || mListType == LIST_TYPE_OVERLAY
+                    || mListType == LIST_TYPE_WRITE_SETTINGS) {
                 if (mFinishAfterDialog) {
                     getActivity().onBackPressed();
                 } else {
@@ -464,6 +490,12 @@ public class ManageApplications extends InstrumentedFragment
                 HighPowerDetail.show(this, mCurrentPkgName, INSTALLED_APP_DETAILS,
                         mFinishAfterDialog);
                 break;
+            case LIST_TYPE_OVERLAY:
+                startAppInfoFragment(DrawOverlayDetails.class, R.string.overlay_settings);
+                break;
+            case LIST_TYPE_WRITE_SETTINGS:
+                startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings);
+                break;
             // TODO: Figure out if there is a way where we can spin up the profile's settings
             // process ahead of time, to avoid a long load of data when user clicks on a managed app.
             // Maybe when they load the list of apps that contains managed profile apps.
@@ -511,8 +543,10 @@ public class ManageApplications extends InstrumentedFragment
         mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE
                 && mSortOrder != R.id.sort_order_size);
 
-        mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem);
-        mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem);
+        mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem
+                && mListType != LIST_TYPE_HIGH_POWER);
+        mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem
+                && mListType != LIST_TYPE_HIGH_POWER);
     }
 
     @Override
@@ -577,6 +611,9 @@ public class ManageApplications extends InstrumentedFragment
     }
 
     public void setHasDisabled(boolean hasDisabledApps) {
+        if (mListType == LIST_TYPE_HIGH_POWER) {
+            return;
+        }
         mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps);
         mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps);
     }
@@ -721,6 +758,10 @@ public class ManageApplications extends InstrumentedFragment
                 mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
             } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) {
                 mExtraInfoBridge = new AppStatePowerBridge(mState, this);
+            } else if (mManageApplications.mListType == LIST_TYPE_OVERLAY) {
+                mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this);
+            } else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) {
+                mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this);
             } else {
                 mExtraInfoBridge = null;
             }
@@ -968,6 +1009,15 @@ public class ManageApplications extends InstrumentedFragment
             return false;
         }
 
+        @Override
+        public boolean isEnabled(int position) {
+            if (mManageApplications.mListType != LIST_TYPE_HIGH_POWER) {
+                return true;
+            }
+            ApplicationsState.AppEntry entry = mEntries.get(position);
+            return !PowerWhitelistBackend.getInstance().isSysWhitelisted(entry.info.packageName);
+        }
+
         public View getView(int position, View convertView, ViewGroup parent) {
             // A ViewHolder keeps references to children views to avoid unnecessary calls
             // to findViewById() on each row.
@@ -999,6 +1049,7 @@ public class ManageApplications extends InstrumentedFragment
             }
             mActive.remove(convertView);
             mActive.add(convertView);
+            convertView.setEnabled(isEnabled(position));
             return convertView;
         }
 
@@ -1019,8 +1070,9 @@ public class ManageApplications extends InstrumentedFragment
 
                 case LIST_TYPE_USAGE_ACCESS:
                     if (holder.entry.extraInfo != null) {
-                        holder.summary.setText(((UsageState) holder.entry.extraInfo).hasAccess() ?
-                                R.string.switch_on_text : R.string.switch_off_text);
+                        holder.summary.setText((new UsageState((PermissionState)holder.entry
+                                .extraInfo)).isPermissible() ? R.string.switch_on_text :
+                                R.string.switch_off_text);
                     } else {
                         holder.summary.setText(null);
                     }
@@ -1030,6 +1082,15 @@ public class ManageApplications extends InstrumentedFragment
                     holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry));
                     break;
 
+                case LIST_TYPE_OVERLAY:
+                    holder.summary.setText(DrawOverlayDetails.getSummary(mContext, holder.entry));
+                    break;
+
+                case LIST_TYPE_WRITE_SETTINGS:
+                    holder.summary.setText(WriteSettingsDetails.getSummary(mContext,
+                            holder.entry));
+                    break;
+
                 default:
                     holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
                     break;
@@ -1047,14 +1108,15 @@ public class ManageApplications extends InstrumentedFragment
         }
 
         private CharSequence getDomainsSummary(String packageName) {
-            ArraySet<String> result = new ArraySet<>();
-            List<IntentFilterVerificationInfo> list =
-                    mPm.getIntentFilterVerifications(packageName);
-            for (IntentFilterVerificationInfo ivi : list) {
-                for (String host : ivi.getDomains()) {
-                    result.add(host);
-                }
+            // If the user has explicitly said "no" for this package, that's the
+            // string we should show.
+            int domainStatus = mPm.getIntentVerificationStatus(packageName, UserHandle.myUserId());
+            if (domainStatus == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+                return mContext.getString(R.string.domain_urls_summary_none);
             }
+            // Otherwise, ask package manager for the domains for this package,
+            // and show the first one (or none if there aren't any).
+            ArraySet<String> result = Utils.getHandledDomains(mPm, packageName);
             if (result.size() == 0) {
                 return mContext.getString(R.string.domain_urls_summary_none);
             } else if (result.size() == 1) {
index f4382f8..f1d70ee 100644 (file)
@@ -20,6 +20,7 @@ import android.app.AlertDialog;
 import android.content.ComponentName;
 import android.content.DialogInterface;
 import android.os.Bundle;
+import android.os.Handler;
 import android.preference.Preference;
 import android.preference.SwitchPreference;
 import android.provider.Settings;
@@ -45,6 +46,7 @@ public class ManageAssist extends SettingsPreferenceFragment
     private SwitchPreference mContextPref;
     private SwitchPreference mScreenshotPref;
     private VoiceInputListPreference mVoiceInputPref;
+    private Handler mHandler = new Handler();
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -78,6 +80,7 @@ public class ManageAssist extends SettingsPreferenceFragment
         if (preference == mContextPref) {
             Settings.Secure.putInt(getContentResolver(), Settings.Secure.ASSIST_STRUCTURE_ENABLED,
                     (boolean) newValue ? 1 : 0);
+            postUpdateUi();
             return true;
         }
         if (preference == mScreenshotPref) {
@@ -102,19 +105,47 @@ public class ManageAssist extends SettingsPreferenceFragment
         return false;
     }
 
+    private void postUpdateUi() {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                updateUi();
+            }
+        });
+    }
+
     private void updateUi() {
         mDefaultAssitPref.refreshAssistApps();
+        mVoiceInputPref.refreshVoiceInputs();
 
         final ComponentName currentAssist = mDefaultAssitPref.getCurrentAssist();
         final boolean hasAssistant = currentAssist != null;
         if (hasAssistant) {
             getPreferenceScreen().addPreference(mContextPref);
+            getPreferenceScreen().addPreference(mScreenshotPref);
         } else {
             getPreferenceScreen().removePreference(mContextPref);
+            getPreferenceScreen().removePreference(mScreenshotPref);
         }
 
-        mVoiceInputPref.setAssistRestrict(currentAssist);
-        mVoiceInputPref.refreshVoiceInputs();
+        if (isCurrentAssistVoiceService()) {
+            getPreferenceScreen().removePreference(mVoiceInputPref);
+        } else {
+            getPreferenceScreen().addPreference(mVoiceInputPref);
+            mVoiceInputPref.setAssistRestrict(currentAssist);
+        }
+
+        mScreenshotPref.setEnabled(mContextPref.isChecked());
+        if (!mContextPref.isChecked()) {
+            mScreenshotPref.setChecked(false);
+        }
+    }
+
+    private boolean isCurrentAssistVoiceService() {
+        ComponentName currentAssist = mDefaultAssitPref.getCurrentAssist();
+        ComponentName activeService = mVoiceInputPref.getCurrentService();
+        return currentAssist == null && activeService == null ||
+                currentAssist != null && currentAssist.equals(activeService);
     }
 
     private void confirmNewAssist(final String newAssitPackage) {
index cc83157..ad2ea02 100644 (file)
@@ -126,7 +126,7 @@ public class ResetAppsHelper implements DialogInterface.OnClickListener,
                     }
                 }
                 try {
-                    mIPm.resetPreferredActivities(UserHandle.myUserId());
+                    mIPm.resetApplicationPreferences(UserHandle.myUserId());
                 } catch (RemoteException e) {
                 }
                 mAom.resetAllModes();
index 2286a24..c63bcd8 100644 (file)
@@ -45,8 +45,8 @@ import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.settings.R;
-import com.android.settings.Utils;
 import com.android.settingslib.applications.InterestingConfigChanges;
+import com.android.settingslib.Utils;
 
 import java.util.ArrayList;
 import java.util.Collections;
index 6d5995b..7af1d9b 100644 (file)
@@ -40,8 +40,10 @@ import com.android.settings.applications.AppStateUsageBridge.UsageState;
 public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
         OnPreferenceClickListener {
 
-    private static final String KEY_USAGE_SWITCH = "usage_switch";
-    private static final String KEY_USAGE_PREFS = "app_usage_preference";
+    private static final String KEY_APP_OPS_PREFERENCE_SCREEN = "app_ops_preference_screen";
+    private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
+    private static final String KEY_APP_OPS_SETTINGS_PREFS = "app_ops_settings_preference";
+    private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
 
     // Use a bridge to get the usage stats but don't initialize it to connect with all state.
     // TODO: Break out this functionality into its own class.
@@ -49,6 +51,7 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
     private AppOpsManager mAppOpsManager;
     private SwitchPreference mSwitchPref;
     private Preference mUsagePrefs;
+    private Preference mUsageDesc;
     private Intent mSettingsIntent;
     private UsageState mUsageState;
     private DevicePolicyManager mDpm;
@@ -62,9 +65,15 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mDpm = context.getSystemService(DevicePolicyManager.class);
 
-        addPreferencesFromResource(R.xml.usage_access_details);
-        mSwitchPref = (SwitchPreference) findPreference(KEY_USAGE_SWITCH);
-        mUsagePrefs = findPreference(KEY_USAGE_PREFS);
+        addPreferencesFromResource(R.xml.app_ops_permissions_details);
+        mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
+        mUsagePrefs = findPreference(KEY_APP_OPS_SETTINGS_PREFS);
+        mUsageDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
+
+        getPreferenceScreen().setTitle(R.string.usage_access);
+        mSwitchPref.setTitle(R.string.permit_usage_access);
+        mUsagePrefs.setTitle(R.string.app_usage_preference);
+        mUsageDesc.setSummary(R.string.usage_access_description);
 
         mSwitchPref.setOnPreferenceChangeListener(this);
         mUsagePrefs.setOnPreferenceClickListener(this);
@@ -92,8 +101,8 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         if (preference == mSwitchPref) {
-            if (mUsageState != null && (Boolean) newValue != mUsageState.hasAccess()) {
-                if (mUsageState.hasAccess() && mDpm.isProfileOwnerApp(mPackageName)) {
+            if (mUsageState != null && (Boolean) newValue != mUsageState.isPermissible()) {
+                if (mUsageState.isPermissible() && mDpm.isProfileOwnerApp(mPackageName)) {
                     new AlertDialog.Builder(getContext())
                             .setIcon(com.android.internal.R.drawable.ic_dialog_alert_material)
                             .setTitle(android.R.string.dialog_alert_title)
@@ -101,7 +110,7 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
                             .setPositiveButton(R.string.okay, null)
                             .show();
                 }
-                setHasAccess(!mUsageState.hasAccess());
+                setHasAccess(!mUsageState.isPermissible());
                 refreshUi();
             }
             return true;
@@ -119,14 +128,15 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
         mUsageState = mUsageBridge.getUsageInfo(mPackageName,
                 mPackageInfo.applicationInfo.uid);
 
-        boolean hasAccess = mUsageState.hasAccess();
+        boolean hasAccess = mUsageState.isPermissible();
         mSwitchPref.setChecked(hasAccess);
+        mSwitchPref.setEnabled(mUsageState.permissionDeclared);
         mUsagePrefs.setEnabled(hasAccess);
 
         ResolveInfo resolveInfo = mPm.resolveActivityAsUser(mSettingsIntent,
                 PackageManager.GET_META_DATA, mUserId);
         if (resolveInfo != null) {
-            if (findPreference(KEY_USAGE_PREFS) == null) {
+            if (findPreference(KEY_APP_OPS_SETTINGS_PREFS) == null) {
                 getPreferenceScreen().addPreference(mUsagePrefs);
             }
             Bundle metaData = resolveInfo.activityInfo.metaData;
@@ -138,7 +148,7 @@ public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenc
                         metaData.getString(Settings.METADATA_USAGE_ACCESS_REASON));
             }
         } else {
-            if (findPreference(KEY_USAGE_PREFS) != null) {
+            if (findPreference(KEY_APP_OPS_SETTINGS_PREFS) != null) {
                 getPreferenceScreen().removePreference(mUsagePrefs);
             }
         }
diff --git a/src/com/android/settings/applications/WriteSettingsDetails.java b/src/com/android/settings/applications/WriteSettingsDetails.java
new file mode 100644 (file)
index 0000000..c89e7b3
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * 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.applications;
+
+import android.app.AlertDialog;
+import android.app.AppOpsManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.SwitchPreference;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.InstrumentedFragment;
+import com.android.settings.R;
+import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
+import com.android.settings.applications.AppStateWriteSettingsBridge.WriteSettingsState;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+
+import java.util.List;
+
+public class WriteSettingsDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
+        OnPreferenceClickListener {
+
+    private static final String KEY_APP_OPS_PREFERENCE_SCREEN = "app_ops_preference_screen";
+    private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch";
+    private static final String KEY_APP_OPS_SETTINGS_PREFS = "app_ops_settings_preference";
+    private static final String KEY_APP_OPS_SETTINGS_DESC = "app_ops_settings_description";
+    private static final String LOG_TAG = "WriteSettingsDetails";
+
+    private static final int [] APP_OPS_OP_CODE = {
+            AppOpsManager.OP_WRITE_SETTINGS
+    };
+
+    // Use a bridge to get the overlay details but don't initialize it to connect with all state.
+    // TODO: Break out this functionality into its own class.
+    private AppStateWriteSettingsBridge mAppBridge;
+    private AppOpsManager mAppOpsManager;
+    private SwitchPreference mSwitchPref;
+    private Preference mWriteSettingsPrefs;
+    private Preference mWriteSettingsDesc;
+    private Intent mSettingsIntent;
+    private WriteSettingsState mWriteSettingsState;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Context context = getActivity();
+        mAppBridge = new AppStateWriteSettingsBridge(context, mState, null);
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+
+        addPreferencesFromResource(R.xml.app_ops_permissions_details);
+        mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH);
+        mWriteSettingsPrefs = findPreference(KEY_APP_OPS_SETTINGS_PREFS);
+        mWriteSettingsDesc = findPreference(KEY_APP_OPS_SETTINGS_DESC);
+
+        getPreferenceScreen().setTitle(R.string.write_settings);
+        mSwitchPref.setTitle(R.string.permit_write_settings);
+        mWriteSettingsPrefs.setTitle(R.string.write_settings_preference);
+        mWriteSettingsDesc.setSummary(R.string.write_settings_description);
+
+        mSwitchPref.setOnPreferenceChangeListener(this);
+        mWriteSettingsPrefs.setOnPreferenceClickListener(this);
+
+        mSettingsIntent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Settings.INTENT_CATEGORY_USAGE_ACCESS_CONFIG)
+                .setPackage(mPackageName);
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        if (preference == mWriteSettingsPrefs) {
+            if (mSettingsIntent != null) {
+                try {
+                    getActivity().startActivityAsUser(mSettingsIntent, new UserHandle(mUserId));
+                } catch (ActivityNotFoundException e) {
+                    Log.w(LOG_TAG, "Unable to launch write system settings " + mSettingsIntent, e);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference == mSwitchPref) {
+            if (mWriteSettingsState != null && (Boolean) newValue != mWriteSettingsState
+                    .isPermissible()) {
+                setCanWriteSettings(!mWriteSettingsState.isPermissible());
+                refreshUi();
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private void setCanWriteSettings(boolean newState) {
+        mAppOpsManager.setMode(AppOpsManager.OP_WRITE_SETTINGS,
+                mPackageInfo.applicationInfo.uid, mPackageName, newState
+                ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
+    }
+
+    private boolean canWriteSettings(String pkgName) {
+        int result = mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_WRITE_SETTINGS,
+                mPackageInfo.applicationInfo.uid, pkgName);
+        if (result == AppOpsManager.MODE_ALLOWED) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    protected boolean refreshUi() {
+        mWriteSettingsState = mAppBridge.getWriteSettingsInfo(mPackageName,
+                mPackageInfo.applicationInfo.uid);
+
+        boolean canWrite = mWriteSettingsState.isPermissible();
+        mSwitchPref.setChecked(canWrite);
+        // you can't ask a user for a permission you didn't even declare!
+        mSwitchPref.setEnabled(mWriteSettingsState.permissionDeclared);
+        mWriteSettingsPrefs.setEnabled(canWrite);
+        getPreferenceScreen().removePreference(mWriteSettingsPrefs);
+
+        return true;
+    }
+
+    @Override
+    protected AlertDialog createDialog(int id, int errorCode) {
+        return null;
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.SYSTEM_ALERT_WINDOW_APPS;
+    }
+
+    public static CharSequence getSummary(Context context, AppEntry entry) {
+        if (entry.extraInfo != null) {
+            return getSummary(context, new WriteSettingsState((PermissionState)entry
+                    .extraInfo));
+        }
+
+        // fallback if entry.extrainfo is null - although this should not happen
+        return getSummary(context, entry.info.packageName);
+    }
+
+    public static CharSequence getSummary(Context context, WriteSettingsState writeSettingsState) {
+        return context.getString(writeSettingsState.isPermissible() ? R.string.write_settings_on :
+                R.string.write_settings_off);
+    }
+
+    public static CharSequence getSummary(Context context, String pkg) {
+        // first check if pkg is a system pkg
+        boolean isSystem = false;
+        PackageManager packageManager = context.getPackageManager();
+        try {
+            ApplicationInfo appInfo = packageManager.getApplicationInfo(pkg, 0);
+            if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                isSystem = true;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // pkg doesn't even exist?
+            Log.w(LOG_TAG, "Package " + pkg + " not found", e);
+            return context.getString(R.string.write_settings_off);
+        }
+
+        AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context
+                .APP_OPS_SERVICE);
+        List<AppOpsManager.PackageOps> packageOps = appOpsManager.getPackagesForOps(
+                APP_OPS_OP_CODE);
+        if (packageOps == null) {
+            return context.getString(R.string.write_settings_off);
+        }
+
+        int uid = isSystem ? 0 : -1;
+        for (AppOpsManager.PackageOps packageOp : packageOps) {
+            if (pkg.equals(packageOp.getPackageName())) {
+                uid = packageOp.getUid();
+                break;
+            }
+        }
+
+        if (uid == -1) {
+            return context.getString(R.string.write_settings_off);
+        }
+
+        int mode = appOpsManager.noteOpNoThrow(AppOpsManager.OP_WRITE_SETTINGS, uid, pkg);
+        return context.getString((mode == AppOpsManager.MODE_ALLOWED) ?
+                R.string.write_settings_on : R.string.write_settings_off);
+    }
+}
index 29cac62..7fa31b3 100755 (executable)
@@ -168,7 +168,8 @@ public final class BluetoothPairingDialog extends AlertActivity implements
 
     private void createUserEntryDialog() {
         final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.bluetooth_pairing_request);
+        p.mTitle = getString(R.string.bluetooth_pairing_request,
+                mCachedDeviceManager.getName(mDevice));
         p.mView = createPinEntryView();
         p.mPositiveButtonText = getString(android.R.string.ok);
         p.mPositiveButtonListener = this;
@@ -182,13 +183,22 @@ public final class BluetoothPairingDialog extends AlertActivity implements
 
     private View createPinEntryView() {
         View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null);
-        TextView messageViewCaption = (TextView) view.findViewById(R.id.message_caption);
         TextView messageViewCaptionHint = (TextView) view.findViewById(R.id.pin_values_hint);
-        TextView messageViewContent = (TextView) view.findViewById(R.id.message_subhead);
         TextView messageView2 = (TextView) view.findViewById(R.id.message_below_pin);
         CheckBox alphanumericPin = (CheckBox) view.findViewById(R.id.alphanumeric_pin);
         CheckBox contactSharing = (CheckBox) view.findViewById(
                 R.id.phonebook_sharing_message_entry_pin);
+        contactSharing.setText(getString(R.string.bluetooth_pairing_shares_phonebook,
+                mCachedDeviceManager.getName(mDevice)));
+        if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_ALLOWED) {
+            contactSharing.setChecked(true);
+        } else if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_REJECTED){
+            contactSharing.setChecked(false);
+        } else {
+            contactSharing.setChecked(true);
+            mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
+        }
+
         contactSharing.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
@@ -209,8 +219,7 @@ public final class BluetoothPairingDialog extends AlertActivity implements
         mPairingView.addTextChangedListener(this);
         alphanumericPin.setOnCheckedChangeListener(this);
 
-        int messageId1;
-        int messageId2;
+        int messageId;
         int messageIdHint = R.string.bluetooth_pin_values_hint;
         int maxLength;
         switch (mType) {
@@ -218,15 +227,13 @@ public final class BluetoothPairingDialog extends AlertActivity implements
                 messageIdHint = R.string.bluetooth_pin_values_hint_16_digits;
                 // FALLTHROUGH
             case BluetoothDevice.PAIRING_VARIANT_PIN:
-                messageId1 = R.string.bluetooth_enter_pin_msg;
-                messageId2 = R.string.bluetooth_enter_pin_other_device;
+                messageId = R.string.bluetooth_enter_pin_other_device;
                 // Maximum of 16 characters in a PIN
                 maxLength = BLUETOOTH_PIN_MAX_LENGTH;
                 break;
 
             case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
-                messageId1 = R.string.bluetooth_enter_pin_msg;
-                messageId2 = R.string.bluetooth_enter_passkey_other_device;
+                messageId = R.string.bluetooth_enter_passkey_other_device;
                 // Maximum of 6 digits for passkey
                 maxLength = BLUETOOTH_PASSKEY_MAX_LENGTH;
                 alphanumericPin.setVisibility(View.GONE);
@@ -237,10 +244,8 @@ public final class BluetoothPairingDialog extends AlertActivity implements
                 return null;
         }
 
-        messageViewCaption.setText(messageId1);
         messageViewCaptionHint.setText(messageIdHint);
-        messageViewContent.setText(mCachedDeviceManager.getName(mDevice));
-        messageView2.setText(messageId2);
+        messageView2.setText(messageId);
         mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER);
         mPairingView.setFilters(new InputFilter[] {
                 new LengthFilter(maxLength) });
@@ -250,15 +255,22 @@ public final class BluetoothPairingDialog extends AlertActivity implements
 
     private View createView() {
         View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_confirm, null);
-        // Escape device name to avoid HTML injection.
-        String name = Html.escapeHtml(mCachedDeviceManager.getName(mDevice));
-        TextView messageViewCaption = (TextView) view.findViewById(R.id.message_caption);
-        TextView messageViewContent = (TextView) view.findViewById(R.id.message_subhead);
         TextView pairingViewCaption = (TextView) view.findViewById(R.id.pairing_caption);
         TextView pairingViewContent = (TextView) view.findViewById(R.id.pairing_subhead);
         TextView messagePairing = (TextView) view.findViewById(R.id.pairing_code_message);
         CheckBox contactSharing = (CheckBox) view.findViewById(
                 R.id.phonebook_sharing_message_confirm_pin);
+        contactSharing.setText(getString(R.string.bluetooth_pairing_shares_phonebook,
+                mCachedDeviceManager.getName(mDevice)));
+        if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_ALLOWED) {
+            contactSharing.setChecked(true);
+        } else if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_REJECTED){
+            contactSharing.setChecked(false);
+        } else {
+            contactSharing.setChecked(true);
+            mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
+        }
+
         contactSharing.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
@@ -283,14 +295,12 @@ public final class BluetoothPairingDialog extends AlertActivity implements
             case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
                 messagePairing.setVisibility(View.VISIBLE);
             case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
-                messageCaption = getString(R.string.bluetooth_enter_pin_msg);
                 pairingContent = mPairingKey;
                 break;
 
             case BluetoothDevice.PAIRING_VARIANT_CONSENT:
             case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
                 messagePairing.setVisibility(View.VISIBLE);
-                messageCaption = getString(R.string.bluetooth_enter_pin_msg);
                 break;
 
             default:
@@ -298,11 +308,6 @@ public final class BluetoothPairingDialog extends AlertActivity implements
                 return null;
         }
 
-        if (messageViewCaption != null) {
-            messageViewCaption.setText(messageCaption);
-            messageViewContent.setText(name);
-        }
-
         if (pairingContent != null) {
             pairingViewCaption.setVisibility(View.VISIBLE);
             pairingViewContent.setVisibility(View.VISIBLE);
@@ -314,7 +319,8 @@ public final class BluetoothPairingDialog extends AlertActivity implements
 
     private void createConfirmationDialog() {
         final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.bluetooth_pairing_request);
+        p.mTitle = getString(R.string.bluetooth_pairing_request,
+                mCachedDeviceManager.getName(mDevice));
         p.mView = createView();
         p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
         p.mPositiveButtonListener = this;
@@ -325,7 +331,8 @@ public final class BluetoothPairingDialog extends AlertActivity implements
 
     private void createConsentDialog() {
         final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.bluetooth_pairing_request);
+        p.mTitle = getString(R.string.bluetooth_pairing_request,
+                mCachedDeviceManager.getName(mDevice));
         p.mView = createView();
         p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
         p.mPositiveButtonListener = this;
@@ -336,7 +343,8 @@ public final class BluetoothPairingDialog extends AlertActivity implements
 
     private void createDisplayPasskeyOrPinDialog() {
         final AlertController.AlertParams p = mAlertParams;
-        p.mTitle = getString(R.string.bluetooth_pairing_request);
+        p.mTitle = getString(R.string.bluetooth_pairing_request,
+                mCachedDeviceManager.getName(mDevice));
         p.mView = createView();
         p.mNegativeButtonText = getString(android.R.string.cancel);
         p.mNegativeButtonListener = this;
index 7a83a85..ca764b3 100644 (file)
@@ -83,7 +83,7 @@ public class SearchResultsSummary extends InstrumentedFragment {
         @Override
         protected void onPostExecute(Cursor cursor) {
             if (!isCancelled()) {
-                MetricsLogger.action(getContext(), InstrumentedFragment.ACTION_SEARCH_RESULTS,
+                MetricsLogger.action(getContext(), MetricsLogger.ACTION_SEARCH_RESULTS,
                         cursor.getCount());
                 setResultsCursor(cursor);
                 setResultsVisibility(cursor.getCount() > 0);
index dea4310..779026b 100644 (file)
@@ -39,6 +39,8 @@ import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
 import android.preference.Preference;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
 import android.provider.DocumentsContract;
 import android.text.TextUtils;
@@ -66,7 +68,6 @@ import com.google.android.collect.Lists;
 
 import java.io.File;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 
@@ -85,6 +86,18 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
 
     private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
 
+    private static final int[] ITEMS_NO_SHOW_SHARED = new int[] {
+            R.string.storage_detail_apps,
+    };
+
+    private static final int[] ITEMS_SHOW_SHARED = new int[] {
+            R.string.storage_detail_apps,
+            R.string.storage_detail_images,
+            R.string.storage_detail_videos,
+            R.string.storage_detail_audio,
+            R.string.storage_detail_other
+    };
+
     private StorageManager mStorageManager;
     private UserManager mUserManager;
 
@@ -97,13 +110,10 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
     private UserInfo mCurrentUser;
 
     private StorageSummaryPreference mSummary;
-    private StorageItemPreference mApps;
-    private StorageItemPreference mImages;
-    private StorageItemPreference mVideos;
-    private StorageItemPreference mAudio;
-    private StorageItemPreference mOther;
-    private StorageItemPreference mCache;
-    private List<StorageItemPreference> mUsers = Lists.newArrayList();
+    private List<StorageItemPreference> mItemPreferencePool = Lists.newArrayList();
+    private List<PreferenceCategory> mHeaderPreferencePool = Lists.newArrayList();
+    private int mHeaderPoolIndex;
+    private int mItemPoolIndex;
 
     private Preference mExplore;
 
@@ -144,22 +154,7 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
         getPreferenceScreen().setOrderingAsAdded(true);
 
         mSummary = new StorageSummaryPreference(context);
-
-        mApps = buildItem(R.string.storage_detail_apps);
-        mImages = buildItem(R.string.storage_detail_images);
-        mVideos = buildItem(R.string.storage_detail_videos);
-        mAudio = buildItem(R.string.storage_detail_audio);
-        mOther = buildItem(R.string.storage_detail_other);
-        mCache = buildItem(R.string.storage_detail_cached);
-
         mCurrentUser = mUserManager.getUserInfo(UserHandle.myUserId());
-        final List<UserInfo> otherUsers = getUsersExcluding(mCurrentUser);
-        for (int i = 0; i < otherUsers.size(); i++) {
-            final UserInfo user = otherUsers.get(i);
-            final StorageItemPreference userPref = new StorageItemPreference(
-                    context, user.name, user.id);
-            mUsers.add(userPref);
-        }
 
         mExplore = buildAction(R.string.storage_menu_explore);
 
@@ -182,39 +177,46 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
 
         screen.removeAll();
 
-        addPreference(mSummary);
+        addPreference(screen, mSummary);
 
-        final boolean showUsers = !mUsers.isEmpty();
+        List<UserInfo> allUsers = mUserManager.getUsers();
+        final int userCount = allUsers.size();
+        final boolean showHeaders = userCount > 1;
         final boolean showShared = (mSharedVolume != null) && mSharedVolume.isMountedReadable();
 
-        if (showUsers) {
-            addPreference(new PreferenceHeader(context, mCurrentUser.name));
-        }
-        addPreference(mApps);
-        if (showShared) {
-            addPreference(mImages);
-            addPreference(mVideos);
-            addPreference(mAudio);
-            addPreference(mOther);
-        }
-        addPreference(mCache);
-        if (showShared) {
-            addPreference(mExplore);
-        }
-        if (showUsers) {
-            addPreference(new PreferenceHeader(context, R.string.storage_other_users));
-            for (Preference pref : mUsers) {
-                addPreference(pref);
+        mItemPoolIndex = 0;
+        mHeaderPoolIndex = 0;
+
+        int addedUserCount = 0;
+        // Add current user and its profiles first
+        for (int userIndex = 0; userIndex < userCount; ++userIndex) {
+            final UserInfo userInfo = allUsers.get(userIndex);
+            if (isProfileOf(mCurrentUser, userInfo)) {
+                final PreferenceGroup details = showHeaders ?
+                        addCategory(screen, userInfo.name) : screen;
+                addDetailItems(details, showShared, userInfo.id);
+                ++addedUserCount;
             }
         }
 
-        for (int i = 0; i < screen.getPreferenceCount(); i++) {
-            final Preference pref = screen.getPreference(i);
-            if (pref instanceof StorageItemPreference) {
-                ((StorageItemPreference) pref).setLoading();
+        // Add rest of users
+        if (userCount - addedUserCount > 0) {
+            PreferenceGroup otherUsers = addCategory(screen,
+                    getText(R.string.storage_other_users));
+            for (int userIndex = 0; userIndex < userCount; ++userIndex) {
+                final UserInfo userInfo = allUsers.get(userIndex);
+                if (!isProfileOf(mCurrentUser, userInfo)) {
+                    addItem(otherUsers, /* titleRes */ 0, userInfo.name, userInfo.id);
+                }
             }
         }
 
+        addItem(screen, R.string.storage_detail_cached, null, UserHandle.USER_NULL);
+
+        if (showShared) {
+            addPreference(screen, mExplore);
+        }
+
         final File file = mVolume.getPath();
         final long totalBytes = file.getTotalSpace();
         final long freeBytes = file.getFreeSpace();
@@ -230,13 +232,56 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
         mMeasure.forceMeasure();
     }
 
-    private void addPreference(Preference pref) {
+    private void addPreference(PreferenceGroup group, Preference pref) {
         pref.setOrder(Preference.DEFAULT_ORDER);
-        getPreferenceScreen().addPreference(pref);
+        group.addPreference(pref);
     }
 
-    private StorageItemPreference buildItem(int titleRes) {
-        return new StorageItemPreference(getActivity(), titleRes);
+    private PreferenceCategory addCategory(PreferenceGroup group, CharSequence title) {
+        PreferenceCategory category;
+        if (mHeaderPoolIndex < mHeaderPreferencePool.size()) {
+            category = mHeaderPreferencePool.get(mHeaderPoolIndex);
+        } else {
+            category = new PreferenceCategory(getActivity(), null,
+                    com.android.internal.R.attr.preferenceCategoryStyle);
+            mHeaderPreferencePool.add(category);
+        }
+        category.setTitle(title);
+        category.removeAll();
+        addPreference(group, category);
+        ++mHeaderPoolIndex;
+        return category;
+    }
+
+    private void addDetailItems(PreferenceGroup category, boolean showShared, int userId) {
+        final int[] itemsToAdd = (showShared ? ITEMS_SHOW_SHARED : ITEMS_NO_SHOW_SHARED);
+        for (int i = 0; i < itemsToAdd.length; ++i) {
+            addItem(category, itemsToAdd[i], null, userId);
+        }
+    }
+
+    private void addItem(PreferenceGroup group, int titleRes, CharSequence title, int userId) {
+        StorageItemPreference item;
+        if (mItemPoolIndex < mItemPreferencePool.size()) {
+            item = mItemPreferencePool.get(mItemPoolIndex);
+        } else {
+            item = buildItem();
+            mItemPreferencePool.add(item);
+        }
+        if (title != null) {
+            item.setTitle(title);
+        } else {
+            item.setTitle(titleRes);
+        }
+        item.setSummary(R.string.memory_calculating_size);
+        item.userHandle = userId;
+        addPreference(group, item);
+        ++mItemPoolIndex;
+    }
+
+    private StorageItemPreference buildItem() {
+        final StorageItemPreference item = new StorageItemPreference(getActivity());
+        return item;
     }
 
     private Preference buildAction(int titleRes) {
@@ -276,6 +321,7 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
         inflater.inflate(R.menu.storage_volume, menu);
     }
 
@@ -308,7 +354,9 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
         // Only offer to migrate when not current storage
         final VolumeInfo privateVol = getActivity().getPackageManager()
                 .getPrimaryStorageCurrentVolume();
-        migrate.setVisible(!Objects.equals(mVolume, privateVol));
+        migrate.setVisible((privateVol != null)
+                && (privateVol.getType() == VolumeInfo.TYPE_PRIVATE)
+                && !Objects.equals(mVolume, privateVol));
     }
 
     @Override
@@ -345,52 +393,67 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference pref) {
         // TODO: launch better intents for specific volume
 
+        final int userId = (pref instanceof StorageItemPreference ?
+                ((StorageItemPreference)pref).userHandle : -1);
+        final int itemTitleId = pref.getTitleRes();
         Intent intent = null;
-        if (pref == mApps) {
-            Bundle args = new Bundle();
-            args.putString(ManageApplications.EXTRA_CLASSNAME, StorageUseActivity.class.getName());
-            args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
-            args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription());
-            intent = Utils.onBuildStartFragmentIntent(getActivity(),
-                    ManageApplications.class.getName(), args, null, R.string.apps_storage, null,
-                    false);
-
-        } else if (pref == mImages) {
-            intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
-            intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "images_root"));
-            intent.addCategory(Intent.CATEGORY_DEFAULT);
-
-        } else if (pref == mVideos) {
-            intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
-            intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "videos_root"));
-            intent.addCategory(Intent.CATEGORY_DEFAULT);
-
-        } else if (pref == mAudio) {
-            intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
-            intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "audio_root"));
-            intent.addCategory(Intent.CATEGORY_DEFAULT);
-
-        } else if (pref == mOther) {
-            OtherInfoFragment.show(this, mStorageManager.getBestVolumeDescription(mVolume),
-                    mSharedVolume);
-            return true;
-
-        } else if (pref == mCache) {
-            ConfirmClearCacheFragment.show(this);
-            return true;
+        switch (itemTitleId) {
+            case R.string.storage_detail_apps: {
+                Bundle args = new Bundle();
+                args.putString(ManageApplications.EXTRA_CLASSNAME,
+                        StorageUseActivity.class.getName());
+                args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
+                args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription());
+                intent = Utils.onBuildStartFragmentIntent(getActivity(),
+                        ManageApplications.class.getName(), args, null, R.string.apps_storage, null,
+                        false);
+
+            } break;
+            case R.string.storage_detail_images: {
+                intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
+                intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "images_root"));
+                intent.addCategory(Intent.CATEGORY_DEFAULT);
+
+            } break;
+            case R.string.storage_detail_videos: {
+                intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
+                intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "videos_root"));
+                intent.addCategory(Intent.CATEGORY_DEFAULT);
+
+            } break;
+            case R.string.storage_detail_audio: {
+                intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
+                intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "audio_root"));
+                intent.addCategory(Intent.CATEGORY_DEFAULT);
+
+            } break;
+            case R.string.storage_detail_other: {
+                OtherInfoFragment.show(this, mStorageManager.getBestVolumeDescription(mVolume),
+                        mSharedVolume);
+                return true;
 
-        } else if (pref == mExplore) {
-            intent = mSharedVolume.buildBrowseIntent();
-        }
+            }
+            case R.string.storage_detail_cached: {
+                ConfirmClearCacheFragment.show(this);
+                return true;
 
-        if (mUsers.contains(pref)) {
-            UserInfoFragment.show(this, pref.getTitle(), pref.getSummary());
-            return true;
+            }
+            case R.string.storage_menu_explore: {
+                intent = mSharedVolume.buildBrowseIntent();
+            } break;
+            case 0: {
+                UserInfoFragment.show(this, pref.getTitle(), pref.getSummary());
+                return true;
+            }
         }
 
         if (intent != null) {
             try {
-                startActivity(intent);
+                if (userId == -1) {
+                    startActivity(intent);
+                } else {
+                    getActivity().startActivityAsUser(intent, new UserHandle(userId));
+                }
             } catch (ActivityNotFoundException e) {
                 Log.w(TAG, "No activity found for " + intent);
             }
@@ -407,26 +470,43 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
     };
 
     private void updateDetails(MeasurementDetails details) {
-        updatePreference(mApps, details.appsSize);
-
-        final long imagesSize = totalValues(details.mediaSize, Environment.DIRECTORY_DCIM,
-                Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES);
-        updatePreference(mImages, imagesSize);
-
-        final long videosSize = totalValues(details.mediaSize, Environment.DIRECTORY_MOVIES);
-        updatePreference(mVideos, videosSize);
-
-        final long audioSize = totalValues(details.mediaSize, Environment.DIRECTORY_MUSIC,
-                Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
-                Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
-        updatePreference(mAudio, audioSize);
-
-        updatePreference(mCache, details.cacheSize);
-        updatePreference(mOther, details.miscSize);
-
-        for (StorageItemPreference userPref : mUsers) {
-            final long userSize = details.usersSize.get(userPref.userHandle);
-            updatePreference(userPref, userSize);
+        for (int i = 0; i < mItemPoolIndex; ++i) {
+            StorageItemPreference item = mItemPreferencePool.get(i);
+            final int userId = item.userHandle;
+            final int itemTitleId = item.getTitleRes();
+            switch (itemTitleId) {
+                case R.string.storage_detail_apps: {
+                    updatePreference(item, details.appsSize.get(userId));
+                } break;
+                case R.string.storage_detail_images: {
+                    final long imagesSize = totalValues(details, userId,
+                            Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES,
+                            Environment.DIRECTORY_PICTURES);
+                    updatePreference(item, imagesSize);
+                } break;
+                case R.string.storage_detail_videos: {
+                    final long videosSize = totalValues(details, userId,
+                            Environment.DIRECTORY_MOVIES);
+                    updatePreference(item, videosSize);
+                } break;
+                case R.string.storage_detail_audio: {
+                    final long audioSize = totalValues(details, userId,
+                            Environment.DIRECTORY_MUSIC,
+                            Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
+                            Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
+                    updatePreference(item, audioSize);
+                } break;
+                case R.string.storage_detail_other: {
+                    updatePreference(item, details.miscSize.get(userId));
+                } break;
+                case R.string.storage_detail_cached: {
+                    updatePreference(item, details.cacheSize);
+                } break;
+                case 0: {
+                    final long userSize = details.usersSize.get(userId);
+                    updatePreference(item, userSize);
+                } break;
+            }
         }
     }
 
@@ -434,26 +514,23 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
         pref.setSummary(Formatter.formatFileSize(getActivity(), size));
     }
 
-    /**
-     * Return list of other users, excluding the current user.
-     */
-    private List<UserInfo> getUsersExcluding(UserInfo excluding) {
-        final List<UserInfo> users = mUserManager.getUsers();
-        final Iterator<UserInfo> i = users.iterator();
-        while (i.hasNext()) {
-            if (i.next().id == excluding.id) {
-                i.remove();
-            }
-        }
-        return users;
+    private boolean isProfileOf(UserInfo user, UserInfo profile) {
+        return user.id == profile.id ||
+                (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+                && user.profileGroupId == profile.profileGroupId);
     }
 
-    private static long totalValues(HashMap<String, Long> map, String... keys) {
+    private static long totalValues(MeasurementDetails details, int userId, String... keys) {
         long total = 0;
-        for (String key : keys) {
-            if (map.containsKey(key)) {
-                total += map.get(key);
+        HashMap<String, Long> map = details.mediaSize.get(userId);
+        if (map != null) {
+            for (String key : keys) {
+                if (map.containsKey(key)) {
+                    total += map.get(key);
+                }
             }
+        } else {
+            Log.w(TAG, "MeasurementDetails mediaSize array does not have key for user " + userId);
         }
         return total;
     }
@@ -644,26 +721,14 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment {
         public void onRemoveCompleted(final String packageName, final boolean succeeded) {
             synchronized (this) {
                 if (--mRemaining == 0) {
-                    mTarget.update();
+                    mTarget.getActivity().runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            mTarget.update();
+                        }
+                    });
                 }
             }
         }
     }
-
-    public static class PreferenceHeader extends Preference {
-        public PreferenceHeader(Context context, int titleRes) {
-            super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
-            setTitle(titleRes);
-        }
-
-        public PreferenceHeader(Context context, CharSequence title) {
-            super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
-            setTitle(title);
-        }
-
-        @Override
-        public boolean isEnabled() {
-            return false;
-        }
-    }
 }
index 4c77d21..c9b4beb 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.settings.deviceinfo;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -81,7 +82,8 @@ public class PublicVolumeSettings extends SettingsPreferenceFragment {
 
         final Context context = getActivity();
 
-        mIsPermittedToAdopt = UserManager.get(context).isAdminUser();
+        mIsPermittedToAdopt = UserManager.get(context).isAdminUser()
+                && !ActivityManager.isUserAMonkey();
 
         mStorageManager = context.getSystemService(StorageManager.class);
 
index d6a3bf5..83043c7 100644 (file)
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -149,7 +150,7 @@ public class SimStatus extends InstrumentedPreferenceActivity {
             mSir = mSelectableSubInfos.size() > 0 ? mSelectableSubInfos.get(0) : null;
 
             if (mSelectableSubInfos.size() > 1) {
-                setContentView(R.layout.sim_information);
+                setContentView(com.android.internal.R.layout.common_tab_settings);
 
                 mTabHost = (TabHost) findViewById(android.R.id.tabhost);
                 mTabWidget = (TabWidget) findViewById(android.R.id.tabs);
@@ -246,6 +247,19 @@ public class SimStatus extends InstrumentedPreferenceActivity {
             networktype = mTelephonyManager.getNetworkTypeName(actualVoiceNetworkType);
         }
 
+        boolean show4GForLTE = false;
+        try {
+            Context con = createPackageContext("com.android.systemui", 0);
+            int id = con.getResources().getIdentifier("config_show4GForLTE",
+                    "bool", "com.android.systemui");
+            show4GForLTE = con.getResources().getBoolean(id);
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "NameNotFoundException for show4GFotLTE");
+        }
+
+        if (networktype != null && networktype.equals("LTE") && show4GForLTE) {
+            networktype = "4G";
+        }
         setSummaryText(KEY_NETWORK_TYPE, networktype);
     }
 
index 41d84c6..677f9ad 100644 (file)
 package com.android.settings.deviceinfo;
 
 import android.content.Context;
-import android.os.UserHandle;
 import android.preference.Preference;
 
-import com.android.settings.R;
-
 public class StorageItemPreference extends Preference {
-    public final int userHandle;
-
-    public StorageItemPreference(Context context, int titleRes) {
-        this(context, context.getText(titleRes), UserHandle.USER_NULL);
-    }
+    public int userHandle;
 
-    public StorageItemPreference(Context context, CharSequence title, int userHandle) {
+    public StorageItemPreference(Context context) {
         super(context);
-
-        setTitle(title);
-        setSummary(R.string.memory_calculating_size);
-
-        this.userHandle = userHandle;
-    }
-
-    public void setLoading() {
-        setSummary(R.string.memory_calculating_size);
     }
 }
index e508cd1..a32109c 100644 (file)
@@ -176,6 +176,10 @@ public abstract class StorageWizardBase extends Activity {
         }
     }
 
+    protected void setKeepScreenOn(boolean keepScreenOn) {
+        getSetupWizardLayout().setKeepScreenOn(keepScreenOn);
+    }
+
     public void onNavigateNext() {
         throw new UnsupportedOperationException();
     }
index 8a03f83..6f89ff5 100644 (file)
@@ -24,6 +24,7 @@ import android.app.DialogFragment;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.IPackageMoveObserver;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.storage.DiskInfo;
@@ -36,6 +37,8 @@ import android.widget.Toast;
 
 import com.android.settings.R;
 
+import java.util.Objects;
+
 public class StorageWizardFormatProgress extends StorageWizardBase {
     private static final String TAG_SLOW_WARNING = "slow_warning";
 
@@ -51,6 +54,7 @@ public class StorageWizardFormatProgress extends StorageWizardBase {
             return;
         }
         setContentView(R.layout.storage_wizard_progress);
+        setKeepScreenOn(true);
 
         mFormatPrivate = getIntent().getBooleanExtra(
                 StorageWizardFormatConfirm.EXTRA_FORMAT_PRIVATE, false);
@@ -97,7 +101,18 @@ public class StorageWizardFormatProgress extends StorageWizardBase {
                     publishProgress(60);
 
                     final VolumeInfo privateVol = activity.findFirstVolume(VolumeInfo.TYPE_PRIVATE);
-                    mPrivateBench = storage.benchmark(privateVol.id);
+                    mPrivateBench = storage.benchmark(privateVol.getId());
+
+                    // If we just adopted the device that had been providing
+                    // physical storage, then automatically move storage to the
+                    // new emulated volume.
+                    if (activity.mDisk.isDefaultPrimary()
+                            && Objects.equals(storage.getPrimaryStorageUuid(),
+                                    StorageManager.UUID_PRIMARY_PHYSICAL)) {
+                        Log.d(TAG, "Just formatted primary physical; silently moving "
+                                + "storage to new emulated volume");
+                        storage.setPrimaryStorageUuid(privateVol.getFsUuid(), new SilentObserver());
+                    }
 
                 } else {
                     storage.partitionPublic(activity.mDisk.getId());
@@ -129,13 +144,19 @@ public class StorageWizardFormatProgress extends StorageWizardBase {
                 return;
             }
 
-            final float pct = (float) mInternalBench / (float) mPrivateBench;
-            Log.d(TAG, "New volume is " + pct + "x the speed of internal");
+            if (activity.mFormatPrivate) {
+                final float pct = (float) mInternalBench / (float) mPrivateBench;
+                Log.d(TAG, "New volume is " + pct + "x the speed of internal");
 
-            // TODO: refine this warning threshold
-            if (mPrivateBench > 2000000000) {
-                final SlowWarningFragment dialog = new SlowWarningFragment();
-                dialog.show(activity.getFragmentManager(), TAG_SLOW_WARNING);
+                // To help set user expectations around device performance, we
+                // warn if the adopted media is 0.25x the speed of internal
+                // storage or slower.
+                if (Float.isNaN(pct) || pct < 0.25) {
+                    final SlowWarningFragment dialog = new SlowWarningFragment();
+                    dialog.showAllowingStateLoss(activity.getFragmentManager(), TAG_SLOW_WARNING);
+                } else {
+                    activity.onFormatFinished();
+                }
             } else {
                 activity.onFormatFinished();
             }
@@ -213,4 +234,16 @@ public class StorageWizardFormatProgress extends StorageWizardBase {
         }
         finishAffinity();
     }
+
+    private static class SilentObserver extends IPackageMoveObserver.Stub {
+        @Override
+        public void onCreated(int moveId, Bundle extras) {
+            // Ignored
+        }
+
+        @Override
+        public void onStatusChanged(int moveId, int status, long estMillis) {
+            // Ignored
+        }
+    }
 }
index 284e67d..c3c2430 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.settings.deviceinfo;
 
+import android.app.ActivityManager;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.UserManager;
@@ -42,7 +43,8 @@ public class StorageWizardInit extends StorageWizardBase {
         }
         setContentView(R.layout.storage_wizard_init);
 
-        mIsPermittedToAdopt = UserManager.get(this).isAdminUser();
+        mIsPermittedToAdopt = UserManager.get(this).isAdminUser()
+                && !ActivityManager.isUserAMonkey();
 
         setIllustrationInternal(true);
         setHeaderText(R.string.storage_wizard_init_title, mDisk.getDescription());
diff --git a/src/com/android/settings/deviceinfo/UsbBackend.java b/src/com/android/settings/deviceinfo/UsbBackend.java
new file mode 100644 (file)
index 0000000..846d6e3
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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.deviceinfo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.os.UserManager;
+
+public class UsbBackend {
+
+    private static final int MODE_POWER_MASK  = 0x01;
+    public static final int MODE_POWER_SINK   = 0x00;
+    public static final int MODE_POWER_SOURCE = 0x01;
+
+    private static final int MODE_DATA_MASK  = 0x03 << 1;
+    public static final int MODE_DATA_NONE   = 0x00 << 1;
+    public static final int MODE_DATA_MTP    = 0x01 << 1;
+    public static final int MODE_DATA_PTP    = 0x02 << 1;
+    public static final int MODE_DATA_MIDI   = 0x03 << 1;
+
+    private final boolean mRestricted;
+
+    private UserManager mUserManager;
+    private UsbManager mUsbManager;
+    private UsbPort mPort;
+    private UsbPortStatus mPortStatus;
+
+    private boolean mIsUnlocked;
+
+    public UsbBackend(Context context) {
+        Intent intent = context.registerReceiver(null,
+                new IntentFilter(UsbManager.ACTION_USB_STATE));
+        mIsUnlocked = intent.getBooleanExtra(UsbManager.USB_DATA_UNLOCKED, false);
+
+        mUserManager = UserManager.get(context);
+        mUsbManager = context.getSystemService(UsbManager.class);
+
+        mRestricted = mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
+        UsbPort[] ports = mUsbManager.getPorts();
+        // For now look for a connected port, in the future we should identify port in the
+        // notification and pick based on that.
+        final int N = ports.length;
+        for (int i = 0; i < N; i++) {
+            UsbPortStatus status = mUsbManager.getPortStatus(ports[i]);
+            if (status.isConnected()) {
+                mPort = ports[i];
+                mPortStatus = status;
+                break;
+            }
+        }
+    }
+
+    public int getCurrentMode() {
+        if (mPort != null) {
+            int power = mPortStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE
+                    ? MODE_POWER_SOURCE : MODE_POWER_SINK;
+            return power | (mPortStatus.getCurrentDataRole() == UsbPort.DATA_ROLE_DEVICE
+                    ? getUsbDataMode() : MODE_DATA_NONE);
+        }
+        return MODE_POWER_SINK | getUsbDataMode();
+    }
+
+    public int getUsbDataMode() {
+        if (!mIsUnlocked) {
+            return MODE_DATA_NONE;
+        } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MTP)) {
+            return MODE_DATA_MTP;
+        } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP)) {
+            return MODE_DATA_PTP;
+        } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MIDI)) {
+            return MODE_DATA_MIDI;
+        }
+        return MODE_DATA_NONE; // ...
+    }
+
+    private void setUsbFunction(int mode) {
+        switch (mode) {
+            case MODE_DATA_MTP:
+                mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP);
+                mUsbManager.setUsbDataUnlocked(true);
+                break;
+            case MODE_DATA_PTP:
+                mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP);
+                mUsbManager.setUsbDataUnlocked(true);
+                break;
+            case MODE_DATA_MIDI:
+                mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MIDI);
+                mUsbManager.setUsbDataUnlocked(true);
+                break;
+            default:
+                mUsbManager.setCurrentFunction(null);
+                mUsbManager.setUsbDataUnlocked(false);
+                break;
+        }
+    }
+
+    public void setMode(int mode) {
+        if (mPort != null) {
+            int powerRole = modeToPower(mode);
+            // If we aren't using any data modes and we support host mode, then go to host mode
+            // so maybe? the other device can provide data if it wants, otherwise go into device
+            // mode because we have no choice.
+            int dataRole = (mode & MODE_DATA_MASK) == MODE_DATA_NONE
+                    && mPortStatus.isRoleCombinationSupported(powerRole, UsbPort.DATA_ROLE_HOST)
+                    ? UsbPort.DATA_ROLE_HOST : UsbPort.DATA_ROLE_DEVICE;
+            mUsbManager.setPortRoles(mPort, powerRole, dataRole);
+        }
+        setUsbFunction(mode & MODE_DATA_MASK);
+    }
+
+    private int modeToPower(int mode) {
+        return (mode & MODE_POWER_MASK) == MODE_POWER_SOURCE
+                    ? UsbPort.POWER_ROLE_SOURCE : UsbPort.POWER_ROLE_SINK;
+    }
+
+    public boolean isModeSupported(int mode) {
+        if (mRestricted && (mode & MODE_DATA_MASK) != MODE_DATA_NONE
+                && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) {
+            // No USB data modes are supported.
+            return false;
+        }
+        if (mPort != null) {
+            int power = modeToPower(mode);
+            if ((mode & MODE_DATA_MASK) != 0) {
+                // We have a port and data, need to be in device mode.
+                return mPortStatus.isRoleCombinationSupported(power,
+                        UsbPort.DATA_ROLE_DEVICE);
+            } else {
+                // No data needed, we can do this power mode in either device or host.
+                return mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_DEVICE)
+                        || mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_HOST);
+            }
+        }
+        // No port, support sink modes only.
+        return (mode & MODE_POWER_MASK) != MODE_POWER_SOURCE;
+    }
+}
\ No newline at end of file
index 268f87d..77fc388 100644 (file)
@@ -20,96 +20,119 @@ import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlertDialog;
-import android.content.Context;
 import android.content.DialogInterface;
-import android.hardware.usb.UsbManager;
 import android.os.Bundle;
-import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Checkable;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.settings.R;
 
 /**
  * UI for the USB chooser dialog.
  *
- * TODO: Don't show some UI elements if UserManager.DISALLOW_USB_FILE_TRANSFER is disabled.
  */
 public class UsbModeChooserActivity extends Activity {
 
-    private UsbManager mUsbManager;
+    public static final int[] DEFAULT_MODES = {
+        UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_NONE,
+        UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_NONE,
+        UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MTP,
+        UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_PTP,
+        UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MIDI
+    };
+
+    private UsbBackend mBackend;
+    private AlertDialog mDialog;
+    private LayoutInflater mLayoutInflater;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
+
         super.onCreate(savedInstanceState);
-        mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
-        final AlertDialog levelDialog;
-        AlertDialog.Builder builder = new AlertDialog.Builder(this);
-        builder.setTitle(R.string.usb_use);
-        builder.setSingleChoiceItems(R.array.usb_available_functions, getCurrentFunction(),
-                new DialogInterface.OnClickListener() {
+
+        mLayoutInflater = LayoutInflater.from(this);
+
+        mDialog = new AlertDialog.Builder(this)
+                .setTitle(R.string.usb_use)
+                .setView(R.layout.usb_dialog_container)
+                .setOnDismissListener(new DialogInterface.OnDismissListener() {
+                    @Override
+                    public void onDismiss(DialogInterface dialog) {
+                        finish();
+                    }
+                })
+                .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
-                        if (!ActivityManager.isUserAMonkey()) {
-                            setCurrentFunction(which);
-                        }
-                        dialog.dismiss();
-                        UsbModeChooserActivity.this.finish();
+                        finish();
                     }
-                });
-        builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
-            @Override
-            public void onDismiss(DialogInterface dialog) {
-                UsbModeChooserActivity.this.finish();
+                }).create();
+        mDialog.show();
+
+        LinearLayout container = (LinearLayout) mDialog.findViewById(R.id.container);
+
+        mBackend = new UsbBackend(this);
+        int current = mBackend.getCurrentMode();
+        for (int i = 0; i < DEFAULT_MODES.length; i++) {
+            if (mBackend.isModeSupported(DEFAULT_MODES[i])) {
+                inflateOption(DEFAULT_MODES[i], current == DEFAULT_MODES[i], container);
             }
-        });
-        builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+        }
+    }
+
+    private void inflateOption(final int mode, boolean selected, LinearLayout container) {
+        View v = mLayoutInflater.inflate(R.layout.radio_with_summary, container, false);
+
+        ((TextView) v.findViewById(android.R.id.title)).setText(getTitle(mode));
+        ((TextView) v.findViewById(android.R.id.summary)).setText(getSummary(mode));
+
+        v.setOnClickListener(new OnClickListener() {
             @Override
-            public void onClick(DialogInterface dialog, int which) {
-                UsbModeChooserActivity.this.finish();
+            public void onClick(View v) {
+                if (!ActivityManager.isUserAMonkey()) {
+                    mBackend.setMode(mode);
+                }
+                mDialog.dismiss();
+                finish();
             }
         });
-        levelDialog = builder.create();
-        levelDialog.show();
+        ((Checkable) v).setChecked(selected);
+        container.addView(v);
     }
 
-    /*
-     * If you change the numbers here, you also need to change R.array.usb_available_functions
-     * so that everything matches.
-     */
-    private int getCurrentFunction() {
-        if (!mUsbManager.isUsbDataUnlocked()) {
-            return 0;
-        } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MTP)) {
-            return 1;
-        } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP)) {
-            return 2;
-        } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MIDI)) {
-            return 3;
+    private static int getSummary(int mode) {
+        switch (mode) {
+            case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_NONE:
+                return R.string.usb_use_charging_only_desc;
+            case UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_NONE:
+                return R.string.usb_use_power_only_desc;
+            case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MTP:
+                return R.string.usb_use_file_transfers_desc;
+            case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_PTP:
+                return R.string.usb_use_photo_transfers_desc;
+            case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MIDI:
+                return R.string.usb_use_MIDI_desc;
         }
         return 0;
     }
 
-    /*
-     * If you change the numbers here, you also need to change R.array.usb_available_functions
-     * so that everything matches.
-     */
-    private void setCurrentFunction(int which) {
-        switch (which) {
-            case 0:
-                mUsbManager.setCurrentFunction(null);
-                mUsbManager.setUsbDataUnlocked(false);
-                break;
-            case 1:
-                mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP);
-                mUsbManager.setUsbDataUnlocked(true);
-                break;
-            case 2:
-                mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP);
-                mUsbManager.setUsbDataUnlocked(true);
-                break;
-            case 3:
-                mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MIDI);
-                mUsbManager.setUsbDataUnlocked(true);
-                break;
+    private static int getTitle(int mode) {
+        switch (mode) {
+            case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_NONE:
+                return R.string.usb_use_charging_only;
+            case UsbBackend.MODE_POWER_SOURCE | UsbBackend.MODE_DATA_NONE:
+                return R.string.usb_use_power_only;
+            case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MTP:
+                return R.string.usb_use_file_transfers;
+            case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_PTP:
+                return R.string.usb_use_photo_transfers;
+            case UsbBackend.MODE_POWER_SINK | UsbBackend.MODE_DATA_MIDI:
+                return R.string.usb_use_MIDI;
         }
+        return 0;
     }
 }
diff --git a/src/com/android/settings/drawable/CircleFramedDrawable.java b/src/com/android/settings/drawable/CircleFramedDrawable.java
deleted file mode 100644 (file)
index 31b8922..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2013 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.drawable;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-
-import com.android.settings.R;
-
-/**
- * Converts the user avatar icon to a circularly clipped one.
- * TODO: Move this to an internal framework class and share with the one in Keyguard.
- */
-public class CircleFramedDrawable extends Drawable {
-
-    private final Bitmap mBitmap;
-    private final int mSize;
-    private final Paint mPaint;
-
-    private float mScale;
-    private Rect mSrcRect;
-    private RectF mDstRect;
-
-    public static CircleFramedDrawable getInstance(Context context, Bitmap icon) {
-        Resources res = context.getResources();
-        float iconSize = res.getDimension(R.dimen.circle_avatar_size);
-
-        CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize);
-        return instance;
-    }
-
-    public CircleFramedDrawable(Bitmap icon, int size) {
-        super();
-        mSize = size;
-
-        mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
-        final Canvas canvas = new Canvas(mBitmap);
-
-        final int width = icon.getWidth();
-        final int height = icon.getHeight();
-        final int square = Math.min(width, height);
-
-        final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, square, square);
-        final RectF circleRect = new RectF(0f, 0f, mSize, mSize);
-
-        final Path fillPath = new Path();
-        fillPath.addArc(circleRect, 0f, 360f);
-
-        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
-
-        // opaque circle matte
-        mPaint = new Paint();
-        mPaint.setAntiAlias(true);
-        mPaint.setColor(Color.BLACK);
-        mPaint.setStyle(Paint.Style.FILL);
-        canvas.drawPath(fillPath, mPaint);
-
-        // mask in the icon where the bitmap is opaque
-        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
-        canvas.drawBitmap(icon, cropRect, circleRect, mPaint);
-
-        // prepare paint for frame drawing
-        mPaint.setXfermode(null);
-
-        mScale = 1f;
-
-        mSrcRect = new Rect(0, 0, mSize, mSize);
-        mDstRect = new RectF(0, 0, mSize, mSize);
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        final float inside = mScale * mSize;
-        final float pad = (mSize - inside) / 2f;
-
-        mDstRect.set(pad, pad, mSize - pad, mSize - pad);
-        canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
-    }
-
-    public void setScale(float scale) {
-        mScale = scale;
-    }
-
-    public float getScale() {
-        return mScale;
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-    }
-    
-    @Override
-    public int getIntrinsicWidth() {
-        return mSize;
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return mSize;
-    }
-}
index 6e6fe4b..781efa3 100644 (file)
@@ -20,7 +20,6 @@ import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.Intent;
 import android.graphics.Color;
-import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.view.View;
@@ -29,6 +28,7 @@ import android.widget.Button;
 import android.widget.TextView;
 
 import com.android.settings.ChooseLockSettingsHelper;
+import com.android.settings.InstrumentedActivity;
 import com.android.settings.R;
 import com.android.setupwizardlib.SetupWizardLayout;
 import com.android.setupwizardlib.view.NavigationBar;
@@ -36,7 +36,8 @@ import com.android.setupwizardlib.view.NavigationBar;
 /**
  * Base activity for all fingerprint enrollment steps.
  */
-public class FingerprintEnrollBase extends Activity implements View.OnClickListener {
+public abstract class FingerprintEnrollBase extends InstrumentedActivity
+        implements View.OnClickListener {
 
     /**
      * Used by the choose fingerprint wizard to indicate the wizard is
@@ -49,6 +50,12 @@ public class FingerprintEnrollBase extends Activity implements View.OnClickListe
      */
     protected static final int RESULT_FINISHED = RESULT_FIRST_USER;
 
+    /**
+     * Used by the enrolling screen during setup wizard to skip over setting up fingerprint, which
+     * will be useful if the user accidentally entered this flow.
+     */
+    protected static final int RESULT_SKIP = RESULT_FIRST_USER + 1;
+
     protected byte[] mToken;
 
     @Override
@@ -72,6 +79,10 @@ public class FingerprintEnrollBase extends Activity implements View.OnClickListe
     @Override
     protected void onPostCreate(@Nullable Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
+        initViews();
+    }
+
+    protected void initViews() {
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS |
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
                 WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
index 81d6042..3d2ce4d 100644 (file)
@@ -38,6 +38,7 @@ import android.widget.ImageView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.settings.ChooseLockSettingsHelper;
 import com.android.settings.R;
 
@@ -213,14 +214,17 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
     }
 
     private void launchFinish(byte[] token) {
-        Intent intent = new Intent();
-        intent.setClassName("com.android.settings", FingerprintEnrollFinish.class.getName());
+        Intent intent = getFinishIntent();
+        intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
         startActivity(intent);
-        setResult(RESULT_FINISHED);
         finish();
     }
 
+    protected Intent getFinishIntent() {
+        return new Intent(this, FingerprintEnrollFinish.class);
+    }
+
     private void updateDescription() {
         if (mSidecar.getEnrollmentSteps() == -1) {
             setHeaderText(R.string.security_settings_fingerprint_enroll_start_title);
@@ -377,6 +381,11 @@ public class FingerprintEnrollEnrolling extends FingerprintEnrollBase
         }
     };
 
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_ENROLLING;
+    }
+
     public static class IconTouchDialog extends DialogFragment {
 
         @Override
index 552ed71..8864822 100644 (file)
 
 package com.android.settings.fingerprint;
 
+import android.content.Context;
 import android.content.Intent;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.settings.ChooseLockSettingsHelper;
 import com.android.settings.R;
 
@@ -75,6 +77,17 @@ public class FingerprintEnrollFindSensor extends FingerprintEnrollBase {
             if (resultCode == RESULT_FINISHED) {
                 setResult(RESULT_FINISHED);
                 finish();
+            } else if (resultCode == RESULT_SKIP) {
+                setResult(RESULT_SKIP);
+                finish();
+            } else {
+                FingerprintManager fpm = getSystemService(FingerprintManager.class);
+                int enrolled = fpm.getEnrolledFingerprints().size();
+                int max = getResources().getInteger(
+                        com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
+                if (enrolled >= max) {
+                    finish();
+                }
             }
         } else {
             super.onActivityResult(requestCode, resultCode, data);
@@ -93,4 +106,9 @@ public class FingerprintEnrollFindSensor extends FingerprintEnrollBase {
             finish();
         }
     }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_FIND_SENSOR;
+    }
 }
index d45f3c2..6691e20 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.settings.fingerprint;
 
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
@@ -24,6 +25,7 @@ import android.preference.Preference;
 import android.view.View;
 import android.widget.Button;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.settings.R;
 import com.android.settings.fingerprint.FingerprintSettings.FingerprintPreference;
 
@@ -55,15 +57,23 @@ public class FingerprintEnrollFinish extends FingerprintEnrollBase {
 
     @Override
     protected void onNextButtonClick() {
+        setResult(RESULT_FINISHED);
         finish();
     }
 
     @Override
     public void onClick(View v) {
         if (v.getId() == R.id.add_another_button) {
+            final Intent intent = getEnrollingIntent();
+            intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            startActivity(intent);
             finish();
-            startActivity(getEnrollingIntent());
         }
         super.onClick(v);
     }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_ENROLL_FINISH;
+    }
 }
index a488358..beb1a8f 100644 (file)
 
 package com.android.settings.fingerprint;
 
+import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.view.View;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.ChooseLockSettingsHelper;
 import com.android.settings.HelpUtils;
 import com.android.settings.R;
 
@@ -28,7 +32,6 @@ import com.android.settings.R;
  */
 public class FingerprintEnrollIntroduction extends FingerprintEnrollBase {
 
-    public static final String EXTRA_HAS_PASSWORD = "fp_existing_password";
     private boolean mHasPassword;
 
     @Override
@@ -38,27 +41,36 @@ public class FingerprintEnrollIntroduction extends FingerprintEnrollBase {
         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);
+        final int passwordQuality = new ChooseLockSettingsHelper(this).utils()
+                .getActivePasswordQuality(UserHandle.myUserId());
+        mHasPassword = passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
     }
 
     @Override
     protected void onNextButtonClick() {
-        Intent intent = new Intent();
-        final String clazz;
+        Intent intent;
         if (!mHasPassword) {
             // No fingerprints registered, launch into enrollment wizard.
-            clazz = FingerprintEnrollOnboard.class.getName();
+            intent = getOnboardIntent();
         } else {
             // Lock thingy is already set up, launch directly into find sensor step from wizard.
-            clazz = FingerprintEnrollFindSensor.class.getName();
+            intent = getFindSensorIntent();
         }
-        intent.setClassName("com.android.settings", clazz);
         startActivityForResult(intent, 0);
     }
 
+    protected Intent getOnboardIntent() {
+        return new Intent(this, FingerprintEnrollOnboard.class);
+    }
+
+    protected Intent getFindSensorIntent() {
+        return new Intent(this, FingerprintEnrollFindSensor.class);
+    }
+
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (resultCode == RESULT_FINISHED) {
+            setResult(RESULT_OK);
             finish();
         } else {
             super.onActivityResult(requestCode, resultCode, data);
@@ -81,4 +93,9 @@ public class FingerprintEnrollIntroduction extends FingerprintEnrollBase {
                 getString(R.string.help_url_fingerprint), getClass().getName());
         startActivity(helpIntent);
     }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_ENROLL_INTRO;
+    }
 }
index b78636a..0990459 100644 (file)
@@ -21,6 +21,7 @@ import android.content.Intent;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.settings.ChooseLockGeneric;
 import com.android.settings.ChooseLockSettingsHelper;
 import com.android.settings.R;
@@ -57,22 +58,34 @@ public class FingerprintEnrollOnboard extends FingerprintEnrollBase {
     }
 
     private void launchChooseLock() {
-        Intent intent = new Intent();
+        Intent intent = getChooseLockIntent();
         long challenge = getSystemService(FingerprintManager.class).preEnroll();
-        intent.setClassName("com.android.settings", ChooseLockGeneric.class.getName());
         intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
                 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
         intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true);
         startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST);
     }
 
+    protected Intent getChooseLockIntent() {
+        return new Intent(this, ChooseLockGeneric.class);
+    }
+
     private void launchFindSensor(byte[] token) {
-        Intent intent = new Intent();
-        intent.setClassName("com.android.settings", FingerprintEnrollFindSensor.class.getName());
+        Intent intent = getFindSensorIntent();
         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
         startActivity(intent);
         finish();
     }
+
+    protected Intent getFindSensorIntent() {
+        return new Intent(this, FingerprintEnrollFindSensor.class);
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_ENROLL_ONBOARD;
+    }
 }
index 6a47dc4..5b677f8 100644 (file)
@@ -24,12 +24,14 @@ import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.settings.ChooseLockSettingsHelper;
+import com.android.settings.InstrumentedFragment;
 
 /**
  * Sidecar fragment to handle the state around fingerprint enrollment.
  */
-public class FingerprintEnrollSidecar extends Fragment {
+public class FingerprintEnrollSidecar extends InstrumentedFragment {
 
     private int mEnrollmentSteps = -1;
     private int mEnrollmentRemaining = 0;
@@ -140,6 +142,11 @@ public class FingerprintEnrollSidecar extends Fragment {
         }
     };
 
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_ENROLL_SIDECAR;
+    }
+
     public interface Listener {
         void onEnrollmentHelp(CharSequence helpString);
         void onEnrollmentError(CharSequence errString);
index 5b2461a..1a6da2f 100644 (file)
@@ -63,7 +63,6 @@ import com.android.settings.HelpUtils;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.SubSettings;
-import com.android.settings.search.Indexable;
 
 import java.util.List;
 
@@ -81,6 +80,7 @@ public class FingerprintSettings extends SubSettings {
      * result.
      */
     static final int RESULT_FINISHED = RESULT_FIRST_USER;
+    private static final long LOCKOUT_DURATION = 30000; // time we have to wait for fp to reset, ms
 
     @Override
     public Intent getIntent() {
@@ -103,7 +103,7 @@ public class FingerprintSettings extends SubSettings {
     }
 
     public static class FingerprintSettingsFragment extends SettingsPreferenceFragment
-        implements OnPreferenceChangeListener, Indexable {
+        implements OnPreferenceChangeListener {
         private static final int MAX_RETRY_ATTEMPTS = 20;
         private static final int RESET_HIGHLIGHT_DELAY_MS = 500;
 
@@ -129,7 +129,7 @@ public class FingerprintSettings extends SubSettings {
 
         private FingerprintManager mFingerprintManager;
         private CancellationSignal mFingerprintCancel;
-        private int mMaxFingerprintAttempts;
+        private boolean mInFingerprintLockout;
         private byte[] mToken;
         private boolean mLaunchedConfirm;
         private Drawable mHighlightDrawable;
@@ -141,6 +141,7 @@ public class FingerprintSettings extends SubSettings {
                 mHandler.obtainMessage(MSG_FINGER_AUTH_SUCCESS, fingerId, 0).sendToTarget();
             }
 
+            @Override
             public void onAuthenticationFailed() {
                 mHandler.obtainMessage(MSG_FINGER_AUTH_FAIL).sendToTarget();
             };
@@ -174,6 +175,7 @@ public class FingerprintSettings extends SubSettings {
             }
         };
         private final Handler mHandler = new Handler() {
+            @Override
             public void handleMessage(android.os.Message msg) {
                 switch (msg.what) {
                     case MSG_REFRESH_FINGERPRINT_TEMPLATES:
@@ -183,32 +185,16 @@ public class FingerprintSettings extends SubSettings {
                     case MSG_FINGER_AUTH_SUCCESS:
                         mFingerprintCancel = null;
                         highlightFingerprintItem(msg.arg1);
-                        retryFingerprint(true);
+                        retryFingerprint();
                     break;
                     case MSG_FINGER_AUTH_FAIL:
-                        mFingerprintCancel = null;
-                        retryFingerprint(true);
+                        // No action required... fingerprint will allow up to 5 of these
                     break;
-                    case MSG_FINGER_AUTH_ERROR: {
-                        mFingerprintCancel = null;
-                        // get activity will be null on a screen rotation
-                        final Activity activity = getActivity();
-                        if (activity != null) {
-                            CharSequence errString = (CharSequence) msg.obj;
-                            Toast.makeText(activity, errString , Toast.LENGTH_SHORT);
-                        }
-                        final int errMsgId = msg.arg1;
-                        if (errMsgId != FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
-                            retryFingerprint(false);
-                        }
-                    }
+                    case MSG_FINGER_AUTH_ERROR:
+                        handleError(msg.arg1 /* errMsgId */, (CharSequence) msg.obj /* errStr */ );
                     break;
                     case MSG_FINGER_AUTH_HELP: {
-                        final Activity activity = getActivity();
-                        if (activity != null) {
-                            CharSequence helpString = (CharSequence) msg.obj;
-                            Toast.makeText(activity, helpString , Toast.LENGTH_SHORT);
-                        }
+                        // Not used
                     }
                     break;
                 }
@@ -216,22 +202,45 @@ public class FingerprintSettings extends SubSettings {
         };
 
         private void stopFingerprint() {
-            if (mFingerprintCancel != null) {
+            if (mFingerprintCancel != null && !mFingerprintCancel.isCanceled()) {
                 mFingerprintCancel.cancel();
-                mFingerprintCancel = null;
             }
+            mFingerprintCancel = null;
         }
 
-        private void retryFingerprint(boolean resetAttempts) {
-            if (resetAttempts) {
-                mMaxFingerprintAttempts = 0;
+        /**
+         * @param errMsgId
+         */
+        protected void handleError(int errMsgId, CharSequence msg) {
+            mFingerprintCancel = null;
+            switch (errMsgId) {
+                case FingerprintManager.FINGERPRINT_ERROR_CANCELED:
+                    return; // Only happens if we get preempted by another activity. Ignored.
+                case FingerprintManager.FINGERPRINT_ERROR_LOCKOUT:
+                    mInFingerprintLockout = true;
+                    // We've been locked out.  Reset after 30s.
+                    if (!mHandler.hasCallbacks(mFingerprintLockoutReset)) {
+                        mHandler.postDelayed(mFingerprintLockoutReset,
+                                LOCKOUT_DURATION);
+                    }
+                    // Fall through to show message
+                default:
+                    // Activity can be null on a screen rotation.
+                    final Activity activity = getActivity();
+                    if (activity != null) {
+                        Toast.makeText(activity, msg , Toast.LENGTH_SHORT);
+                    }
+                break;
             }
-            if (mMaxFingerprintAttempts < MAX_RETRY_ATTEMPTS && mFingerprintCancel == null) {
+            retryFingerprint(); // start again
+        }
+
+        private void retryFingerprint() {
+            if (!mInFingerprintLockout) {
                 mFingerprintCancel = new CancellationSignal();
                 mFingerprintManager.authenticate(null, mFingerprintCancel, 0 /* flags */,
                         mAuthCallback, null);
             }
-            mMaxFingerprintAttempts++;
         }
 
         @Override
@@ -359,7 +368,7 @@ public class FingerprintSettings extends SubSettings {
 
         private void updatePreferences() {
             createPreferenceHierarchy();
-            retryFingerprint(true);
+            retryFingerprint();
         }
 
         @Override
@@ -394,7 +403,7 @@ public class FingerprintSettings extends SubSettings {
         }
 
         private void showRenameDeleteDialog(final Fingerprint fp) {
-            RenameDeleteDialog renameDeleteDialog = new RenameDeleteDialog();
+            RenameDeleteDialog renameDeleteDialog = new RenameDeleteDialog(getContext());
             Bundle args = new Bundle();
             args.putParcelable("fingerprint", fp);
             renameDeleteDialog.setArguments(args);
@@ -439,6 +448,17 @@ public class FingerprintSettings extends SubSettings {
             }
         }
 
+        @Override
+        public void onDestroy() {
+            super.onDestroy();
+            if (getActivity().isFinishing()) {
+                int result = mFingerprintManager.postEnroll();
+                if (result < 0) {
+                    Log.w(TAG, "postEnroll failed: result = " + result);
+                }
+            }
+        }
+
         private Drawable getHighlightDrawable() {
             if (mHighlightDrawable == null) {
                 final Activity activity = getActivity();
@@ -497,8 +517,17 @@ public class FingerprintSettings extends SubSettings {
             updatePreferences();
         }
 
+        private final Runnable mFingerprintLockoutReset = new Runnable() {
+            @Override
+            public void run() {
+                mInFingerprintLockout = false;
+                retryFingerprint();
+            }
+        };
+
         public static class RenameDeleteDialog extends DialogFragment {
 
+            private final Context mContext;
             private Fingerprint mFp;
             private EditText mDialogTextField;
             private String mFingerName;
@@ -506,6 +535,10 @@ public class FingerprintSettings extends SubSettings {
             private int mTextSelectionStart;
             private int mTextSelectionEnd;
 
+            public RenameDeleteDialog(Context context) {
+                mContext = context;
+            }
+
             @Override
             public Dialog onCreateDialog(Bundle savedInstanceState) {
                 mFp = getArguments().getParcelable("fingerprint");
@@ -528,6 +561,9 @@ public class FingerprintSettings extends SubSettings {
                                             if (DEBUG) {
                                                 Log.v(TAG, "rename " + name + " to " + newName);
                                             }
+                                            MetricsLogger.action(mContext,
+                                                    MetricsLogger.ACTION_FINGERPRINT_RENAME,
+                                                    mFp.getFingerId());
                                             FingerprintSettingsFragment parent
                                                     = (FingerprintSettingsFragment)
                                                     getTargetFragment();
@@ -542,11 +578,7 @@ public class FingerprintSettings extends SubSettings {
                                 new DialogInterface.OnClickListener() {
                                     @Override
                                     public void onClick(DialogInterface dialog, int which) {
-                                        if (DEBUG) Log.v(TAG, "Removing fpId=" + mFp.getFingerId());
-                                        FingerprintSettingsFragment parent
-                                                = (FingerprintSettingsFragment) getTargetFragment();
-                                        parent.deleteFingerPrint(mFp);
-                                        dialog.dismiss();
+                                        onDeleteClick(dialog);
                                     }
                                 })
                         .create();
@@ -572,6 +604,26 @@ public class FingerprintSettings extends SubSettings {
                 return alertDialog;
             }
 
+            private void onDeleteClick(DialogInterface dialog) {
+                if (DEBUG) Log.v(TAG, "Removing fpId=" + mFp.getFingerId());
+                MetricsLogger.action(mContext, MetricsLogger.ACTION_FINGERPRINT_DELETE,
+                        mFp.getFingerId());
+                FingerprintSettingsFragment parent
+                        = (FingerprintSettingsFragment) getTargetFragment();
+                if (parent.mFingerprintManager.getEnrolledFingerprints().size() > 1) {
+                    parent.deleteFingerPrint(mFp);
+                } else {
+                    ConfirmLastDeleteDialog lastDeleteDialog = new ConfirmLastDeleteDialog();
+                    Bundle args = new Bundle();
+                    args.putParcelable("fingerprint", mFp);
+                    lastDeleteDialog.setArguments(args);
+                    lastDeleteDialog.setTargetFragment(getTargetFragment(), 0);
+                    lastDeleteDialog.show(getFragmentManager(),
+                            ConfirmLastDeleteDialog.class.getName());
+                }
+                dialog.dismiss();
+            }
+
             @Override
             public void onSaveInstanceState(Bundle outState) {
                 super.onSaveInstanceState(outState);
@@ -583,6 +635,39 @@ public class FingerprintSettings extends SubSettings {
                 }
             }
         }
+
+        public static class ConfirmLastDeleteDialog extends DialogFragment {
+
+            private Fingerprint mFp;
+
+            @Override
+            public Dialog onCreateDialog(Bundle savedInstanceState) {
+                mFp = getArguments().getParcelable("fingerprint");
+                final AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
+                        .setTitle(R.string.fingerprint_last_delete_title)
+                        .setMessage(R.string.fingerprint_last_delete_message)
+                        .setPositiveButton(R.string.fingerprint_last_delete_confirm,
+                                new DialogInterface.OnClickListener() {
+                                    @Override
+                                    public void onClick(DialogInterface dialog, int which) {
+                                        FingerprintSettingsFragment parent
+                                                = (FingerprintSettingsFragment) getTargetFragment();
+                                        parent.deleteFingerPrint(mFp);
+                                        dialog.dismiss();
+                                    }
+                                })
+                        .setNegativeButton(
+                                R.string.cancel,
+                                new DialogInterface.OnClickListener() {
+                                    @Override
+                                    public void onClick(DialogInterface dialog, int which) {
+                                        dialog.dismiss();
+                                    }
+                                })
+                        .create();
+                return alertDialog;
+            }
+        }
     }
 
     public static class FingerprintPreference extends Preference {
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollEnrolling.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollEnrolling.java
new file mode 100644 (file)
index 0000000..0a3073c
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.content.res.Resources;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.R;
+import com.android.settings.SetupWizardUtils;
+import com.android.setupwizardlib.view.NavigationBar;
+
+public class SetupFingerprintEnrollEnrolling extends FingerprintEnrollEnrolling
+        implements NavigationBar.NavigationBarListener {
+
+    @Override
+    protected Intent getFinishIntent() {
+        final Intent intent = new Intent(this, SetupFingerprintEnrollFinish.class);
+        SetupWizardUtils.copySetupExtras(getIntent(), intent);
+        return intent;
+    }
+
+    @Override
+    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
+        resid = SetupWizardUtils.getTheme(getIntent());
+        super.onApplyThemeResource(theme, resid, first);
+    }
+
+    @Override
+    protected void initViews() {
+        SetupWizardUtils.setImmersiveMode(this);
+
+        final View buttonBar = findViewById(R.id.button_bar);
+        if (buttonBar != null) {
+            buttonBar.setVisibility(View.GONE);
+        }
+
+        final NavigationBar navigationBar = getNavigationBar();
+        navigationBar.setNavigationBarListener(this);
+        navigationBar.getNextButton().setText(R.string.skip_label);
+        navigationBar.getBackButton().setVisibility(View.GONE);
+    }
+
+    @Override
+    protected Button getNextButton() {
+        return getNavigationBar().getNextButton();
+    }
+
+    @Override
+    public void onNavigateBack() {
+        onBackPressed();
+    }
+
+    @Override
+    public void onNavigateNext() {
+        setResult(RESULT_SKIP);
+        finish();
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_ENROLLING_SETUP;
+    }
+}
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensor.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollFindSensor.java
new file mode 100644 (file)
index 0000000..1483c83
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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.content.res.Resources;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.ChooseLockSettingsHelper;
+import com.android.settings.R;
+import com.android.settings.SetupWizardUtils;
+import com.android.setupwizardlib.view.NavigationBar;
+
+public class SetupFingerprintEnrollFindSensor extends FingerprintEnrollFindSensor
+        implements NavigationBar.NavigationBarListener {
+
+    @Override
+    protected Intent getEnrollingIntent() {
+        Intent intent = new Intent(this, SetupFingerprintEnrollEnrolling.class);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
+        SetupWizardUtils.copySetupExtras(getIntent(), intent);
+        return intent;
+    }
+
+    @Override
+    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
+        resid = SetupWizardUtils.getTheme(getIntent());
+        super.onApplyThemeResource(theme, resid, first);
+    }
+
+    @Override
+    protected void initViews() {
+        SetupWizardUtils.setImmersiveMode(this);
+
+        final View nextButton = findViewById(R.id.next_button);
+        if (nextButton != null) {
+            nextButton.setVisibility(View.GONE);
+        }
+
+        getNavigationBar().setNavigationBarListener(this);
+        getNavigationBar().getBackButton().setVisibility(View.GONE);
+    }
+
+    @Override
+    protected Button getNextButton() {
+        return getNavigationBar().getNextButton();
+    }
+
+    @Override
+    public void onNavigateBack() {
+        onBackPressed();
+    }
+
+    @Override
+    public void onNavigateNext() {
+        onNextButtonClick();
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_FIND_SENSOR_SETUP;
+    }
+}
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollFinish.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollFinish.java
new file mode 100644 (file)
index 0000000..8f37a11
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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.content.res.Resources;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.ChooseLockSettingsHelper;
+import com.android.settings.R;
+import com.android.settings.SetupWizardUtils;
+import com.android.setupwizardlib.view.NavigationBar;
+
+public class SetupFingerprintEnrollFinish extends FingerprintEnrollFinish
+        implements NavigationBar.NavigationBarListener {
+
+    @Override
+    protected Intent getEnrollingIntent() {
+        Intent intent = new Intent(this, SetupFingerprintEnrollEnrolling.class);
+        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, mToken);
+        SetupWizardUtils.copySetupExtras(getIntent(), intent);
+        return intent;
+    }
+
+    @Override
+    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
+        resid = SetupWizardUtils.getTheme(getIntent());
+        super.onApplyThemeResource(theme, resid, first);
+    }
+
+    @Override
+    protected void initViews() {
+        SetupWizardUtils.setImmersiveMode(this);
+
+        final View nextButton = findViewById(R.id.next_button);
+        if (nextButton != null) {
+            nextButton.setVisibility(View.GONE);
+        }
+
+        final NavigationBar navigationBar = getNavigationBar();
+        navigationBar.setNavigationBarListener(this);
+        navigationBar.getBackButton().setVisibility(View.GONE);
+
+        final TextView message = (TextView) findViewById(R.id.message);
+        message.setText(R.string.setup_fingerprint_enroll_finish_message);
+
+        final TextView secondaryMessage = (TextView) findViewById(R.id.message_secondary);
+        secondaryMessage.setVisibility(View.VISIBLE);
+    }
+
+    @Override
+    protected Button getNextButton() {
+        return getNavigationBar().getNextButton();
+    }
+
+    @Override
+    public void onNavigateBack() {
+        onBackPressed();
+    }
+
+    @Override
+    public void onNavigateNext() {
+        onNextButtonClick();
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_ENROLL_FINISH_SETUP;
+    }
+}
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
new file mode 100644 (file)
index 0000000..c7e39e5
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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.content.res.Resources;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.R;
+import com.android.settings.SetupWizardUtils;
+import com.android.setupwizardlib.view.NavigationBar;
+
+public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntroduction
+        implements NavigationBar.NavigationBarListener {
+
+    @Override
+    protected Intent getOnboardIntent() {
+        final Intent intent = new Intent(this, SetupFingerprintEnrollOnboard.class);
+        SetupWizardUtils.copySetupExtras(getIntent(), intent);
+        return intent;
+    }
+
+    @Override
+    protected Intent getFindSensorIntent() {
+        final Intent intent = new Intent(this, SetupFingerprintEnrollFindSensor.class);
+        SetupWizardUtils.copySetupExtras(getIntent(), intent);
+        return intent;
+    }
+
+    @Override
+    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
+        resid = SetupWizardUtils.getTheme(getIntent());
+        super.onApplyThemeResource(theme, resid, first);
+    }
+
+    @Override
+    protected void initViews() {
+        SetupWizardUtils.setImmersiveMode(this);
+
+        final View buttonBar = findViewById(R.id.button_bar);
+        if (buttonBar != null) {
+            buttonBar.setVisibility(View.GONE);
+        }
+
+        getNavigationBar().setNavigationBarListener(this);
+    }
+
+    @Override
+    protected Button getNextButton() {
+        return getNavigationBar().getNextButton();
+    }
+
+    @Override
+    public void onNavigateBack() {
+        onBackPressed();
+    }
+
+    @Override
+    public void onNavigateNext() {
+        onNextButtonClick();
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_ENROLL_INTRO_SETUP;
+    }
+}
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollOnboard.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollOnboard.java
new file mode 100644 (file)
index 0000000..7fca35a
--- /dev/null
@@ -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.content.res.Resources;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.R;
+import com.android.settings.SetupChooseLockGeneric;
+import com.android.settings.SetupWizardUtils;
+import com.android.setupwizardlib.view.NavigationBar;
+
+public class SetupFingerprintEnrollOnboard extends FingerprintEnrollOnboard
+        implements NavigationBar.NavigationBarListener {
+
+    @Override
+    protected Intent getChooseLockIntent() {
+        Intent intent = new Intent(this, SetupChooseLockGeneric.class);
+        SetupWizardUtils.copySetupExtras(getIntent(), intent);
+        return intent;
+    }
+
+    @Override
+    protected Intent getFindSensorIntent() {
+        final Intent intent = new Intent(this, SetupFingerprintEnrollFindSensor.class);
+        SetupWizardUtils.copySetupExtras(getIntent(), intent);
+        return intent;
+    }
+
+    @Override
+    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
+        resid = SetupWizardUtils.getTheme(getIntent());
+        super.onApplyThemeResource(theme, resid, first);
+    }
+
+    @Override
+    protected void initViews() {
+        SetupWizardUtils.setImmersiveMode(this);
+
+        final View nextButton = findViewById(R.id.next_button);
+        if (nextButton != null) {
+            nextButton.setVisibility(View.GONE);
+        }
+
+        getNavigationBar().setNavigationBarListener(this);
+    }
+
+    @Override
+    protected Button getNextButton() {
+        return getNavigationBar().getNextButton();
+    }
+
+    @Override
+    public void onNavigateBack() {
+        onBackPressed();
+    }
+
+    @Override
+    public void onNavigateNext() {
+        onNextButtonClick();
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.FINGERPRINT_ENROLL_ONBOARD_SETUP;
+    }
+}
index edab729..b0e6969 100644 (file)
@@ -24,7 +24,6 @@ import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.graphics.drawable.Drawable;
-import android.os.BatteryStats;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -33,7 +32,7 @@ import android.util.Log;
 
 import com.android.internal.os.BatterySipper;
 import com.android.settings.R;
-import com.android.settings.Utils;
+import com.android.settingslib.Utils;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -191,7 +190,7 @@ public class BatteryEntry {
             icon = context.getDrawable(iconId);
         }
         if ((name == null || iconId == 0) && this.sipper.uidObj != null) {
-            getQuickNameIconForUid(this.sipper.uidObj);
+            getQuickNameIconForUid(this.sipper.uidObj.getUid());
         }
     }
 
@@ -206,8 +205,7 @@ public class BatteryEntry {
         return name;
     }
 
-    void getQuickNameIconForUid(BatteryStats.Uid uidObj) {
-        final int uid = uidObj.getUid();
+    void getQuickNameIconForUid(final int uid) {
         final String uidString = Integer.toString(uid);
         if (sUidCache.containsKey(uidString)) {
             UidToDetail utd = sUidCache.get(uidString);
@@ -217,10 +215,8 @@ public class BatteryEntry {
             return;
         }
         PackageManager pm = context.getPackageManager();
-        String[] packages = pm.getPackagesForUid(uid);
         icon = pm.getDefaultActivityIcon();
-        if (packages == null) {
-            //name = Integer.toString(uid);
+        if (pm.getPackagesForUid(uid) == null) {
             if (uid == 0) {
                 name = context.getResources().getString(R.string.process_kernel_label);
             } else if ("mediaserver".equals(name)) {
@@ -230,10 +226,8 @@ public class BatteryEntry {
             }
             iconId = R.drawable.ic_power_system;
             icon = context.getDrawable(iconId);
-            return;
-        } else {
-            //name = packages[0];
         }
+
         if (sHandler != null) {
             synchronized (mRequestQueue) {
                 mRequestQueue.add(this);
@@ -249,79 +243,82 @@ public class BatteryEntry {
         if (sipper.uidObj == null) {
             return;
         }
+
         PackageManager pm = context.getPackageManager();
         final int uid = sipper.uidObj.getUid();
-        final Drawable defaultActivityIcon = pm.getDefaultActivityIcon();
         sipper.mPackages = pm.getPackagesForUid(uid);
-        if (sipper.mPackages == null) {
-            name = Integer.toString(uid);
-            return;
-        }
-
-        String[] packageLabels = new String[sipper.mPackages.length];
-        System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length);
+        if (sipper.mPackages != null) {
+            String[] packageLabels = new String[sipper.mPackages.length];
+            System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length);
 
-        // Convert package names to user-facing labels where possible
-        IPackageManager ipm = AppGlobals.getPackageManager();
-        final int userId = UserHandle.getUserId(uid);
-        for (int i = 0; i < packageLabels.length; i++) {
-            try {
-                final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i],
-                        0 /* no flags */, userId);
-                if (ai == null) {
-                    Log.d(PowerUsageSummary.TAG, "Retrieving null app info for package "
-                            + packageLabels[i] + ", user " + userId);
-                    continue;
-                }
-                CharSequence label = ai.loadLabel(pm);
-                if (label != null) {
-                    packageLabels[i] = label.toString();
-                }
-                if (ai.icon != 0) {
-                    defaultPackageName = sipper.mPackages[i];
-                    icon = ai.loadIcon(pm);
-                    break;
-                }
-            } catch (RemoteException e) {
-                Log.d(PowerUsageSummary.TAG, "Error while retrieving app info for package "
-                        + packageLabels[i] + ", user " + userId, e);
-            }
-        }
-        if (icon == null) {
-            icon = defaultActivityIcon;
-        }
-
-        if (packageLabels.length == 1) {
-            name = packageLabels[0];
-        } else {
-            // Look for an official name for this UID.
-            for (String pkgName : sipper.mPackages) {
+            // Convert package names to user-facing labels where possible
+            IPackageManager ipm = AppGlobals.getPackageManager();
+            final int userId = UserHandle.getUserId(uid);
+            for (int i = 0; i < packageLabels.length; i++) {
                 try {
-                    final PackageInfo pi = ipm.getPackageInfo(pkgName, 0 /* no flags */, userId);
-                    if (pi == null) {
-                        Log.d(PowerUsageSummary.TAG, "Retrieving null package info for package "
-                                + pkgName + ", user " + userId);
+                    final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i],
+                            0 /* no flags */, userId);
+                    if (ai == null) {
+                        Log.d(PowerUsageSummary.TAG, "Retrieving null app info for package "
+                                + packageLabels[i] + ", user " + userId);
                         continue;
                     }
-                    if (pi.sharedUserLabel != 0) {
-                        final CharSequence nm = pm.getText(pkgName,
-                                pi.sharedUserLabel, pi.applicationInfo);
-                        if (nm != null) {
-                            name = nm.toString();
-                            if (pi.applicationInfo.icon != 0) {
-                                defaultPackageName = pkgName;
-                                icon = pi.applicationInfo.loadIcon(pm);
+                    CharSequence label = ai.loadLabel(pm);
+                    if (label != null) {
+                        packageLabels[i] = label.toString();
+                    }
+                    if (ai.icon != 0) {
+                        defaultPackageName = sipper.mPackages[i];
+                        icon = ai.loadIcon(pm);
+                        break;
+                    }
+                } catch (RemoteException e) {
+                    Log.d(PowerUsageSummary.TAG, "Error while retrieving app info for package "
+                            + packageLabels[i] + ", user " + userId, e);
+                }
+            }
+
+            if (packageLabels.length == 1) {
+                name = packageLabels[0];
+            } else {
+                // Look for an official name for this UID.
+                for (String pkgName : sipper.mPackages) {
+                    try {
+                        final PackageInfo pi = ipm.getPackageInfo(pkgName, 0 /* no flags */, userId);
+                        if (pi == null) {
+                            Log.d(PowerUsageSummary.TAG, "Retrieving null package info for package "
+                                    + pkgName + ", user " + userId);
+                            continue;
+                        }
+                        if (pi.sharedUserLabel != 0) {
+                            final CharSequence nm = pm.getText(pkgName,
+                                    pi.sharedUserLabel, pi.applicationInfo);
+                            if (nm != null) {
+                                name = nm.toString();
+                                if (pi.applicationInfo.icon != 0) {
+                                    defaultPackageName = pkgName;
+                                    icon = pi.applicationInfo.loadIcon(pm);
+                                }
+                                break;
                             }
-                            break;
                         }
+                    } catch (RemoteException e) {
+                        Log.d(PowerUsageSummary.TAG, "Error while retrieving package info for package "
+                                + pkgName + ", user " + userId, e);
                     }
-                } catch (RemoteException e) {
-                    Log.d(PowerUsageSummary.TAG, "Error while retrieving package info for package "
-                            + pkgName + ", user " + userId, e);
                 }
             }
         }
-        final String uidString = Integer.toString(sipper.uidObj.getUid());
+
+        final String uidString = Integer.toString(uid);
+        if (name == null) {
+            name = uidString;
+        }
+
+        if (icon == null) {
+            icon = pm.getDefaultActivityIcon();
+        }
+
         UidToDetail utd = new UidToDetail();
         utd.name = name;
         utd.icon = icon;
index 859b5b8..908666b 100644 (file)
@@ -79,7 +79,7 @@ public class HighPowerDetail extends DialogFragment implements OnClickListener,
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         AlertDialog.Builder b = new AlertDialog.Builder(getContext())
-                .setTitle(getString(R.string.ignore_optimizations_title, mLabel))
+                .setTitle(mLabel)
                 .setNegativeButton(R.string.cancel, null)
                 .setView(R.layout.ignore_optimizations_content);
         if (!mBackend.isSysWhitelisted(mPackageName)) {
@@ -141,8 +141,10 @@ public class HighPowerDetail extends DialogFragment implements OnClickListener,
     }
 
     public static CharSequence getSummary(Context context, String pkg) {
-        return context.getString(PowerWhitelistBackend.getInstance().isWhitelisted(pkg)
-                ? R.string.high_power_on : R.string.high_power_off);
+        PowerWhitelistBackend powerWhitelist = PowerWhitelistBackend.getInstance();
+        return context.getString(powerWhitelist.isSysWhitelisted(pkg) ? R.string.high_power_system
+                : powerWhitelist.isWhitelisted(pkg) ? R.string.high_power_filter_on
+                : R.string.high_power_off);
     }
 
     public static void show(Fragment caller, String packageName, int requestCode,
index 54cdf5d..52dea5a 100644 (file)
@@ -26,7 +26,7 @@ import android.preference.Preference;
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceGroup;
 
-import com.android.settings.InstrumentedFragment;
+import com.android.internal.logging.MetricsLogger;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 
@@ -38,7 +38,7 @@ public class InactiveApps extends SettingsPreferenceFragment implements OnPrefer
 
     @Override
     protected int getMetricsCategory() {
-        return InstrumentedFragment.UNDECLARED;
+        return MetricsLogger.FUELGAUGE_INACTIVE_APPS;
     }
 
     @Override
index 9e7fa48..129322b 100644 (file)
@@ -163,7 +163,9 @@ public class PowerUsageDetail extends PowerUsageBase implements Button.OnClickLi
                     printWriter.flush();
                     args.putString(PowerUsageDetail.EXTRA_REPORT_CHECKIN_DETAILS,
                             result.toString());
-                    userId = UserHandle.getUserId(uid.getUid());
+                    if (uid.getUid() != 0) {
+                        userId = UserHandle.getUserId(uid.getUid());
+                    }
                 }
             }
             break;
@@ -428,7 +430,8 @@ public class PowerUsageDetail extends PowerUsageBase implements Button.OnClickLi
             } else {
                 removePreference(KEY_TWO_BUTTONS);
             }
-            if (mApp != null) {
+            if (mApp != null
+                    && PowerWhitelistBackend.getInstance().isWhitelisted(mApp.packageName)) {
                 mHighPower = findPreference(KEY_HIGH_POWER);
                 mHighPower.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                     @Override
@@ -438,11 +441,11 @@ public class PowerUsageDetail extends PowerUsageBase implements Button.OnClickLi
                     }
                 });
             } else {
-                removePreference(KEY_HIGH_POWER);
+                mControlsParent.removePreference(findPreference(KEY_HIGH_POWER));
             }
         } else {
             removePreference(KEY_TWO_BUTTONS);
-            removePreference(KEY_HIGH_POWER);
+            mControlsParent.removePreference(findPreference(KEY_HIGH_POWER));
         }
 
         refreshStats();
@@ -643,6 +646,7 @@ public class PowerUsageDetail extends PowerUsageBase implements Button.OnClickLi
     private void addControl(int pageSummary, int actionTitle, final int action) {
         Preference pref = new Preference(getActivity());
         pref.setTitle(actionTitle);
+        pref.setLayoutResource(R.layout.horizontal_preference);
         pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
             @Override
             public boolean onPreferenceClick(Preference preference) {
index 1475034..445896d 100644 (file)
@@ -76,7 +76,7 @@ public class PowerUsageSummary extends PowerUsageBase {
     private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
 
     private static final int MIN_POWER_THRESHOLD_MILLI_AMP = 5;
-    private static final int MAX_ITEMS_TO_LIST = 10;
+    private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
     private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
     private static final int SECONDS_IN_HOUR = 60 * 60;
 
@@ -129,7 +129,6 @@ public class PowerUsageSummary extends PowerUsageBase {
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        super.onCreateOptionsMenu(menu, inflater);
         if (DEBUG) {
             menu.add(0, MENU_STATS_TYPE, 0, R.string.menu_stats_total)
                     .setIcon(com.android.internal.R.drawable.ic_menu_info_details)
@@ -140,12 +139,12 @@ public class PowerUsageSummary extends PowerUsageBase {
         batterySaver.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
 
         menu.add(0, MENU_HIGH_POWER_APPS, 0, R.string.high_power_apps);
+        super.onCreateOptionsMenu(menu, inflater);
+    }
 
-        String helpUrl;
-        if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_battery))) {
-            final MenuItem help = menu.add(0, MENU_HELP, 0, R.string.help_label);
-            HelpUtils.prepareHelpMenuItem(getActivity(), help, helpUrl, getClass().getName());
-        }
+    @Override
+    protected int getHelpResource() {
+        return R.string.help_url_battery;
     }
 
     @Override
@@ -182,11 +181,19 @@ public class PowerUsageSummary extends PowerUsageBase {
         mAppListGroup.addPreference(notAvailable);
     }
 
+    private static boolean isSharedGid(int uid) {
+        return UserHandle.getAppIdFromSharedAppGid(uid) > 0;
+    }
+
+    private static boolean isSystemUid(int uid) {
+        return uid >= Process.SYSTEM_UID && uid < Process.FIRST_APPLICATION_UID;
+    }
+
     /**
      * We want to coalesce some UIDs. For example, dex2oat runs under a shared gid that
      * exists for all users of the same app. We detect this case and merge the power use
      * for dex2oat to the device OWNER's use of the app.
-     * @return A sorted list of app's using power.
+     * @return A sorted list of apps using power.
      */
     private static List<BatterySipper> getCoalescedUsageList(final List<BatterySipper> sippers) {
         final SparseArray<BatterySipper> uidList = new SparseArray<>();
@@ -194,30 +201,62 @@ public class PowerUsageSummary extends PowerUsageBase {
         final ArrayList<BatterySipper> results = new ArrayList<>();
         final int numSippers = sippers.size();
         for (int i = 0; i < numSippers; i++) {
-            final BatterySipper sipper = sippers.get(i);
+            BatterySipper sipper = sippers.get(i);
             if (sipper.getUid() > 0) {
                 int realUid = sipper.getUid();
-                if (sipper.getUid() >= Process.FIRST_SHARED_APPLICATION_GID &&
-                        sipper.getUid() <= Process.LAST_SHARED_APPLICATION_GID) {
-                    // This is a shared gid being used to do work on behalf of an app across all
-                    // users. But we'll blame the power on the device OWNER.
+
+                // Check if this UID is a shared GID. If so, we combine it with the OWNER's
+                // actual app UID.
+                if (isSharedGid(sipper.getUid())) {
                     realUid = UserHandle.getUid(UserHandle.USER_OWNER,
                             UserHandle.getAppIdFromSharedAppGid(sipper.getUid()));
                 }
 
+                // Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
+                if (isSystemUid(realUid)
+                        && !"mediaserver".equals(sipper.packageWithHighestDrain)) {
+                    // Use the system UID for all UIDs running in their own sandbox that
+                    // are not apps. We exclude mediaserver because we already are expected to
+                    // report that as a separate item.
+                    realUid = Process.SYSTEM_UID;
+                }
+
+                if (realUid != sipper.getUid()) {
+                    // Replace the BatterySipper with a new one with the real UID set.
+                    BatterySipper newSipper = new BatterySipper(sipper.drainType,
+                            new FakeUid(realUid), 0.0);
+                    newSipper.add(sipper);
+                    newSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
+                    newSipper.mPackages = sipper.mPackages;
+                    sipper = newSipper;
+                }
+
                 int index = uidList.indexOfKey(realUid);
                 if (index < 0) {
+                    // New entry.
                     uidList.put(realUid, sipper);
                 } else {
-                    BatterySipper existingSipper = uidList.valueAt(index);
-                    if (existingSipper.getUid() >= Process.FIRST_SHARED_APPLICATION_GID &&
-                            existingSipper.getUid() <= Process.FIRST_SHARED_APPLICATION_GID) {
-                        // If the app already under this uid is a dex2oat run, then combine and
-                        // substitute it with the actual app.
-                        sipper.add(existingSipper);
-                        uidList.setValueAt(index, sipper);
-                    } else {
-                        existingSipper.add(sipper);
+                    // Combine BatterySippers if we already have one with this UID.
+                    final BatterySipper existingSipper = uidList.valueAt(index);
+                    existingSipper.add(sipper);
+                    if (existingSipper.packageWithHighestDrain == null
+                            && sipper.packageWithHighestDrain != null) {
+                        existingSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
+                    }
+
+                    final int existingPackageLen = existingSipper.mPackages != null ?
+                            existingSipper.mPackages.length : 0;
+                    final int newPackageLen = sipper.mPackages != null ?
+                            sipper.mPackages.length : 0;
+                    if (newPackageLen > 0) {
+                        String[] newPackages = new String[existingPackageLen + newPackageLen];
+                        if (existingPackageLen > 0) {
+                            System.arraycopy(existingSipper.mPackages, 0, newPackages, 0,
+                                    existingPackageLen);
+                        }
+                        System.arraycopy(sipper.mPackages, 0, newPackages, existingPackageLen,
+                                newPackageLen);
+                        existingSipper.mPackages = newPackages;
                     }
                 }
             } else {
@@ -317,7 +356,8 @@ public class PowerUsageSummary extends PowerUsageBase {
                 if (sipper.uidObj != null) {
                     pref.setKey(Integer.toString(sipper.uidObj.getUid()));
                 }
-                if (sipper.drainType != DrainType.APP && sipper.drainType != DrainType.USER) {
+                if ((sipper.drainType != DrainType.APP || sipper.uidObj.getUid() == 0)
+                         && sipper.drainType != DrainType.USER) {
                     pref.setTint(colorControl);
                 }
                 addedSome = true;
@@ -346,6 +386,8 @@ public class PowerUsageSummary extends PowerUsageBase {
         }
         stats.add(new BatterySipper(DrainType.APP,
                 new FakeUid(Process.FIRST_APPLICATION_UID), use));
+        stats.add(new BatterySipper(DrainType.APP,
+                new FakeUid(0), use));
 
         // Simulate dex2oat process.
         BatterySipper sipper = new BatterySipper(DrainType.APP,
@@ -353,6 +395,15 @@ public class PowerUsageSummary extends PowerUsageBase {
         sipper.packageWithHighestDrain = "dex2oat";
         stats.add(sipper);
 
+        sipper = new BatterySipper(DrainType.APP,
+                new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID + 1)), 10.0f);
+        sipper.packageWithHighestDrain = "dex2oat";
+        stats.add(sipper);
+
+        sipper = new BatterySipper(DrainType.APP,
+                new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)), 9.0f);
+        stats.add(sipper);
+
         return stats;
     }
 
diff --git a/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java b/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java
new file mode 100644 (file)
index 0000000..1c1e9df
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.fuelgauge;
+
+import android.Manifest;
+import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.IDeviceIdleController;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import com.android.settings.R;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+public class RequestIgnoreBatteryOptimizations extends AlertActivity implements
+        DialogInterface.OnClickListener {
+    static final String TAG = "RequestIgnoreBatteryOptimizations";
+
+    private static final String DEVICE_IDLE_SERVICE = "deviceidle";
+
+    IDeviceIdleController mDeviceIdleService;
+    String mPackageName;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mDeviceIdleService = IDeviceIdleController.Stub.asInterface(
+                ServiceManager.getService(DEVICE_IDLE_SERVICE));
+
+        Uri data = getIntent().getData();
+        if (data == null) {
+            Log.w(TAG, "No data supplied for IGNORE_BATTERY_OPTIMIZATION_SETTINGS in: "
+                    + getIntent());
+            finish();
+            return;
+        }
+        mPackageName = data.getSchemeSpecificPart();
+        if (mPackageName == null) {
+            Log.w(TAG, "No data supplied for IGNORE_BATTERY_OPTIMIZATION_SETTINGS in: "
+                    + getIntent());
+            finish();
+            return;
+        }
+
+        PowerManager power = getSystemService(PowerManager.class);
+        if (power.isIgnoringBatteryOptimizations(mPackageName)) {
+            Log.i(TAG, "Not should prompt, already ignoring optimizations: " + mPackageName);
+            finish();
+            return;
+        }
+
+        ApplicationInfo ai;
+        try {
+            ai = getPackageManager().getApplicationInfo(mPackageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Requested package doesn't exist: " + mPackageName);
+            finish();
+            return;
+        }
+
+        if (getPackageManager().checkPermission(
+                Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, mPackageName)
+                != PackageManager.PERMISSION_GRANTED) {
+            Log.w(TAG, "Requested package " + mPackageName + " does not hold permission "
+                    + Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+            finish();
+            return;
+        }
+
+        final AlertController.AlertParams p = mAlertParams;
+        p.mTitle = getText(R.string.high_power_prompt_title);
+        p.mMessage = getString(R.string.high_power_prompt_body, ai.loadLabel(getPackageManager()));
+        p.mPositiveButtonText = getText(R.string.yes);
+        p.mNegativeButtonText = getText(R.string.no);
+        p.mPositiveButtonListener = this;
+        p.mNegativeButtonListener = this;
+        setupAlert();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case BUTTON_POSITIVE:
+                try {
+                    mDeviceIdleService.addPowerSaveWhitelistApp(mPackageName);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Unable to reach IDeviceIdleController", e);
+                }
+                setResult(RESULT_OK);
+                break;
+            case BUTTON_NEGATIVE:
+                break;
+        }
+    }
+}
diff --git a/src/com/android/settings/net/ChartData.java b/src/com/android/settings/net/ChartData.java
deleted file mode 100644 (file)
index 0b8969e..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2011 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.net;
-
-import android.net.NetworkStatsHistory;
-
-public class ChartData {
-    public NetworkStatsHistory network;
-
-    public NetworkStatsHistory detail;
-    public NetworkStatsHistory detailDefault;
-    public NetworkStatsHistory detailForeground;
-}
diff --git a/src/com/android/settings/net/ChartDataLoader.java b/src/com/android/settings/net/ChartDataLoader.java
deleted file mode 100644 (file)
index e0336b7..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2011 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.net;
-
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.SET_FOREGROUND;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
-
-import android.content.AsyncTaskLoader;
-import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import com.android.settings.DataUsageSummary.AppItem;
-
-/**
- * Loader for historical chart data for both network and UID details.
- */
-public class ChartDataLoader extends AsyncTaskLoader<ChartData> {
-    private static final String KEY_TEMPLATE = "template";
-    private static final String KEY_APP = "app";
-    private static final String KEY_FIELDS = "fields";
-
-    private final INetworkStatsSession mSession;
-    private final Bundle mArgs;
-
-    public static Bundle buildArgs(NetworkTemplate template, AppItem app) {
-        return buildArgs(template, app, FIELD_RX_BYTES | FIELD_TX_BYTES);
-    }
-
-    public static Bundle buildArgs(NetworkTemplate template, AppItem app, int fields) {
-        final Bundle args = new Bundle();
-        args.putParcelable(KEY_TEMPLATE, template);
-        args.putParcelable(KEY_APP, app);
-        args.putInt(KEY_FIELDS, fields);
-        return args;
-    }
-
-    public ChartDataLoader(Context context, INetworkStatsSession session, Bundle args) {
-        super(context);
-        mSession = session;
-        mArgs = args;
-    }
-
-    @Override
-    protected void onStartLoading() {
-        super.onStartLoading();
-        forceLoad();
-    }
-
-    @Override
-    public ChartData loadInBackground() {
-        final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
-        final AppItem app = mArgs.getParcelable(KEY_APP);
-        final int fields = mArgs.getInt(KEY_FIELDS);
-
-        try {
-            return loadInBackground(template, app, fields);
-        } catch (RemoteException e) {
-            // since we can't do much without history, and we don't want to
-            // leave with half-baked UI, we bail hard.
-            throw new RuntimeException("problem reading network stats", e);
-        }
-    }
-
-    private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
-            throws RemoteException {
-        final ChartData data = new ChartData();
-        data.network = mSession.getHistoryForNetwork(template, fields);
-
-        if (app != null) {
-            // load stats for current uid and template
-            final int size = app.uids.size();
-            for (int i = 0; i < size; i++) {
-                final int uid = app.uids.keyAt(i);
-                data.detailDefault = collectHistoryForUid(
-                        template, uid, SET_DEFAULT, data.detailDefault);
-                data.detailForeground = collectHistoryForUid(
-                        template, uid, SET_FOREGROUND, data.detailForeground);
-            }
-
-            if (size > 0) {
-                data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
-                data.detail.recordEntireHistory(data.detailDefault);
-                data.detail.recordEntireHistory(data.detailForeground);
-            } else {
-                data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
-                data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
-                data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
-            }
-        }
-
-        return data;
-    }
-
-    @Override
-    protected void onStopLoading() {
-        super.onStopLoading();
-        cancelLoad();
-    }
-
-    @Override
-    protected void onReset() {
-        super.onReset();
-        cancelLoad();
-    }
-
-    /**
-     * Collect {@link NetworkStatsHistory} for the requested UID, combining with
-     * an existing {@link NetworkStatsHistory} if provided.
-     */
-    private NetworkStatsHistory collectHistoryForUid(
-            NetworkTemplate template, int uid, int set, NetworkStatsHistory existing)
-            throws RemoteException {
-        final NetworkStatsHistory history = mSession.getHistoryForUid(
-                template, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
-
-        if (existing != null) {
-            existing.recordEntireHistory(history);
-            return existing;
-        } else {
-            return history;
-        }
-    }
-}
index ec1dd38..59a8b92 100644 (file)
@@ -40,6 +40,7 @@ import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
+import com.android.settingslib.NetworkPolicyEditor;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/src/com/android/settings/net/NetworkPolicyEditor.java b/src/com/android/settings/net/NetworkPolicyEditor.java
deleted file mode 100644 (file)
index 1268c3f..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2011 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.net;
-
-import static android.net.NetworkPolicy.CYCLE_NONE;
-import static android.net.NetworkPolicy.LIMIT_DISABLED;
-import static android.net.NetworkPolicy.SNOOZE_NEVER;
-import static android.net.NetworkPolicy.WARNING_DISABLED;
-import static android.net.NetworkTemplate.MATCH_WIFI;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.net.NetworkPolicy;
-import android.net.NetworkPolicyManager;
-import android.net.NetworkTemplate;
-import android.net.wifi.WifiInfo;
-import android.os.AsyncTask;
-import android.text.TextUtils;
-import android.text.format.Time;
-
-import com.google.android.collect.Lists;
-
-import java.util.ArrayList;
-
-/**
- * Utility class to modify list of {@link NetworkPolicy}. Specifically knows
- * about which policies can coexist. This editor offers thread safety when
- * talking with {@link NetworkPolicyManager}.
- */
-public class NetworkPolicyEditor {
-    // TODO: be more robust when missing policies from service
-
-    public static final boolean ENABLE_SPLIT_POLICIES = false;
-
-    private NetworkPolicyManager mPolicyManager;
-    private ArrayList<NetworkPolicy> mPolicies = Lists.newArrayList();
-
-    public NetworkPolicyEditor(NetworkPolicyManager policyManager) {
-        mPolicyManager = checkNotNull(policyManager);
-    }
-
-    public void read() {
-        final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
-
-        boolean modified = false;
-        mPolicies.clear();
-        for (NetworkPolicy policy : policies) {
-            // TODO: find better place to clamp these
-            if (policy.limitBytes < -1) {
-                policy.limitBytes = LIMIT_DISABLED;
-                modified = true;
-            }
-            if (policy.warningBytes < -1) {
-                policy.warningBytes = WARNING_DISABLED;
-                modified = true;
-            }
-
-            mPolicies.add(policy);
-        }
-
-        // when we cleaned policies above, write back changes
-        if (modified) writeAsync();
-    }
-
-    public void writeAsync() {
-        // TODO: consider making more robust by passing through service
-        final NetworkPolicy[] policies = mPolicies.toArray(new NetworkPolicy[mPolicies.size()]);
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... params) {
-                write(policies);
-                return null;
-            }
-        }.execute();
-    }
-
-    public void write(NetworkPolicy[] policies) {
-        mPolicyManager.setNetworkPolicies(policies);
-    }
-
-    public boolean hasLimitedPolicy(NetworkTemplate template) {
-        final NetworkPolicy policy = getPolicy(template);
-        return policy != null && policy.limitBytes != LIMIT_DISABLED;
-    }
-
-    public NetworkPolicy getOrCreatePolicy(NetworkTemplate template) {
-        NetworkPolicy policy = getPolicy(template);
-        if (policy == null) {
-            policy = buildDefaultPolicy(template);
-            mPolicies.add(policy);
-        }
-        return policy;
-    }
-
-    public NetworkPolicy getPolicy(NetworkTemplate template) {
-        for (NetworkPolicy policy : mPolicies) {
-            if (policy.template.equals(template)) {
-                return policy;
-            }
-        }
-        return null;
-    }
-
-    public NetworkPolicy getPolicyMaybeUnquoted(NetworkTemplate template) {
-        NetworkPolicy policy = getPolicy(template);
-        if (policy != null) {
-            return policy;
-        } else {
-            return getPolicy(buildUnquotedNetworkTemplate(template));
-        }
-    }
-
-    @Deprecated
-    private static NetworkPolicy buildDefaultPolicy(NetworkTemplate template) {
-        // TODO: move this into framework to share with NetworkPolicyManagerService
-        final int cycleDay;
-        final String cycleTimezone;
-        final boolean metered;
-
-        if (template.getMatchRule() == MATCH_WIFI) {
-            cycleDay = CYCLE_NONE;
-            cycleTimezone = Time.TIMEZONE_UTC;
-            metered = false;
-        } else {
-            final Time time = new Time();
-            time.setToNow();
-            cycleDay = time.monthDay;
-            cycleTimezone = time.timezone;
-            metered = true;
-        }
-
-        return new NetworkPolicy(template, cycleDay, cycleTimezone, WARNING_DISABLED,
-                LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, metered, true);
-    }
-
-    public int getPolicyCycleDay(NetworkTemplate template) {
-        final NetworkPolicy policy = getPolicy(template);
-        return (policy != null) ? policy.cycleDay : -1;
-    }
-
-    public void setPolicyCycleDay(NetworkTemplate template, int cycleDay, String cycleTimezone) {
-        final NetworkPolicy policy = getOrCreatePolicy(template);
-        policy.cycleDay = cycleDay;
-        policy.cycleTimezone = cycleTimezone;
-        policy.inferred = false;
-        policy.clearSnooze();
-        writeAsync();
-    }
-
-    public long getPolicyWarningBytes(NetworkTemplate template) {
-        final NetworkPolicy policy = getPolicy(template);
-        return (policy != null) ? policy.warningBytes : WARNING_DISABLED;
-    }
-
-    public void setPolicyWarningBytes(NetworkTemplate template, long warningBytes) {
-        final NetworkPolicy policy = getOrCreatePolicy(template);
-        policy.warningBytes = warningBytes;
-        policy.inferred = false;
-        policy.clearSnooze();
-        writeAsync();
-    }
-
-    public long getPolicyLimitBytes(NetworkTemplate template) {
-        final NetworkPolicy policy = getPolicy(template);
-        return (policy != null) ? policy.limitBytes : LIMIT_DISABLED;
-    }
-
-    public void setPolicyLimitBytes(NetworkTemplate template, long limitBytes) {
-        final NetworkPolicy policy = getOrCreatePolicy(template);
-        policy.limitBytes = limitBytes;
-        policy.inferred = false;
-        policy.clearSnooze();
-        writeAsync();
-    }
-
-    public boolean getPolicyMetered(NetworkTemplate template) {
-        NetworkPolicy policy = getPolicy(template);
-        if (policy != null) {
-            return policy.metered;
-        } else {
-            return false;
-        }
-    }
-
-    public void setPolicyMetered(NetworkTemplate template, boolean metered) {
-        boolean modified = false;
-
-        NetworkPolicy policy = getPolicy(template);
-        if (metered) {
-            if (policy == null) {
-                policy = buildDefaultPolicy(template);
-                policy.metered = true;
-                policy.inferred = false;
-                mPolicies.add(policy);
-                modified = true;
-            } else if (!policy.metered) {
-                policy.metered = true;
-                policy.inferred = false;
-                modified = true;
-            }
-
-        } else {
-            if (policy == null) {
-                // ignore when policy doesn't exist
-            } else if (policy.metered) {
-                policy.metered = false;
-                policy.inferred = false;
-                modified = true;
-            }
-        }
-
-        // Remove legacy unquoted policies while we're here
-        final NetworkTemplate unquoted = buildUnquotedNetworkTemplate(template);
-        final NetworkPolicy unquotedPolicy = getPolicy(unquoted);
-        if (unquotedPolicy != null) {
-            mPolicies.remove(unquotedPolicy);
-            modified = true;
-        }
-
-        if (modified) writeAsync();
-    }
-
-    /**
-     * Build a revised {@link NetworkTemplate} that matches the same rule, but
-     * with an unquoted {@link NetworkTemplate#getNetworkId()}. Used to work
-     * around legacy bugs.
-     */
-    private static NetworkTemplate buildUnquotedNetworkTemplate(NetworkTemplate template) {
-        if (template == null) return null;
-        final String networkId = template.getNetworkId();
-        final String strippedNetworkId = WifiInfo.removeDoubleQuotes(networkId);
-        if (!TextUtils.equals(strippedNetworkId, networkId)) {
-            return new NetworkTemplate(
-                    template.getMatchRule(), template.getSubscriberId(), strippedNetworkId);
-        } else {
-            return null;
-        }
-    }
-}
diff --git a/src/com/android/settings/net/SummaryForAllUidLoader.java b/src/com/android/settings/net/SummaryForAllUidLoader.java
deleted file mode 100644 (file)
index 68dc799..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2011 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.net;
-
-import android.content.AsyncTaskLoader;
-import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStats;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-public class SummaryForAllUidLoader extends AsyncTaskLoader<NetworkStats> {
-    private static final String KEY_TEMPLATE = "template";
-    private static final String KEY_START = "start";
-    private static final String KEY_END = "end";
-
-    private final INetworkStatsSession mSession;
-    private final Bundle mArgs;
-
-    public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
-        final Bundle args = new Bundle();
-        args.putParcelable(KEY_TEMPLATE, template);
-        args.putLong(KEY_START, start);
-        args.putLong(KEY_END, end);
-        return args;
-    }
-
-    public SummaryForAllUidLoader(Context context, INetworkStatsSession session, Bundle args) {
-        super(context);
-        mSession = session;
-        mArgs = args;
-    }
-
-    @Override
-    protected void onStartLoading() {
-        super.onStartLoading();
-        forceLoad();
-    }
-
-    @Override
-    public NetworkStats loadInBackground() {
-        final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
-        final long start = mArgs.getLong(KEY_START);
-        final long end = mArgs.getLong(KEY_END);
-
-        try {
-            return mSession.getSummaryForAllUid(template, start, end, false);
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-    @Override
-    protected void onStopLoading() {
-        super.onStopLoading();
-        cancelLoad();
-    }
-
-    @Override
-    protected void onReset() {
-        super.onReset();
-        cancelLoad();
-    }
-}
diff --git a/src/com/android/settings/net/UidDetail.java b/src/com/android/settings/net/UidDetail.java
deleted file mode 100644 (file)
index 0b14254..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2011 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.net;
-
-import android.graphics.drawable.Drawable;
-
-public class UidDetail {
-    public CharSequence label;
-    public CharSequence contentDescription;
-    public CharSequence[] detailLabels;
-    public CharSequence[] detailContentDescriptions;
-    public Drawable icon;
-}
diff --git a/src/com/android/settings/net/UidDetailProvider.java b/src/com/android/settings/net/UidDetailProvider.java
deleted file mode 100644 (file)
index a08c7de..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2011 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.net;
-
-import android.app.AppGlobals;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.net.ConnectivityManager;
-import android.net.TrafficStats;
-import android.os.UserManager;
-import android.os.UserHandle;
-import android.os.RemoteException;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.settings.R;
-import com.android.settings.Utils;
-
-/**
- * Return details about a specific UID, handling special cases like
- * {@link TrafficStats#UID_TETHERING} and {@link UserInfo}.
- */
-public class UidDetailProvider {
-    private static final String TAG = "DataUsage";
-    private final Context mContext;
-    private final SparseArray<UidDetail> mUidDetailCache;
-
-    public static final int OTHER_USER_RANGE_START = -2000;
-
-    public static int buildKeyForUser(int userHandle) {
-        return OTHER_USER_RANGE_START - userHandle;
-    }
-
-    public static boolean isKeyForUser(int key) {
-        return key <= OTHER_USER_RANGE_START;
-    }
-
-    public static int getUserIdForKey(int key) {
-        return OTHER_USER_RANGE_START - key;
-    }
-
-    public UidDetailProvider(Context context) {
-        mContext = context.getApplicationContext();
-        mUidDetailCache = new SparseArray<UidDetail>();
-    }
-
-    public void clearCache() {
-        synchronized (mUidDetailCache) {
-            mUidDetailCache.clear();
-        }
-    }
-
-    /**
-     * Resolve best descriptive label for the given UID.
-     */
-    public UidDetail getUidDetail(int uid, boolean blocking) {
-        UidDetail detail;
-
-        synchronized (mUidDetailCache) {
-            detail = mUidDetailCache.get(uid);
-        }
-
-        if (detail != null) {
-            return detail;
-        } else if (!blocking) {
-            return null;
-        }
-
-        detail = buildUidDetail(uid);
-
-        synchronized (mUidDetailCache) {
-            mUidDetailCache.put(uid, detail);
-        }
-
-        return detail;
-    }
-
-    /**
-     * Build {@link UidDetail} object, blocking until all {@link Drawable}
-     * lookup is finished.
-     */
-    private UidDetail buildUidDetail(int uid) {
-        final Resources res = mContext.getResources();
-        final PackageManager pm = mContext.getPackageManager();
-
-        final UidDetail detail = new UidDetail();
-        detail.label = pm.getNameForUid(uid);
-        detail.icon = pm.getDefaultActivityIcon();
-
-        // handle special case labels
-        switch (uid) {
-            case android.os.Process.SYSTEM_UID:
-                detail.label = res.getString(R.string.process_kernel_label);
-                detail.icon = pm.getDefaultActivityIcon();
-                return detail;
-            case TrafficStats.UID_REMOVED:
-                detail.label = res.getString(UserManager.supportsMultipleUsers()
-                        ? R.string.data_usage_uninstalled_apps_users
-                        : R.string.data_usage_uninstalled_apps);
-                detail.icon = pm.getDefaultActivityIcon();
-                return detail;
-            case TrafficStats.UID_TETHERING:
-                final ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
-                        Context.CONNECTIVITY_SERVICE);
-                detail.label = res.getString(Utils.getTetheringLabel(cm));
-                detail.icon = pm.getDefaultActivityIcon();
-                return detail;
-        }
-
-        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-
-        // Handle keys that are actually user handles
-        if (isKeyForUser(uid)) {
-            final int userHandle = getUserIdForKey(uid);
-            final UserInfo info = um.getUserInfo(userHandle);
-            if (info != null) {
-                detail.label = Utils.getUserLabel(mContext, info);
-                detail.icon = Utils.getUserIcon(mContext, um, info);
-                return detail;
-            }
-        }
-
-        // otherwise fall back to using packagemanager labels
-        final String[] packageNames = pm.getPackagesForUid(uid);
-        final int length = packageNames != null ? packageNames.length : 0;
-        try {
-            final int userId = UserHandle.getUserId(uid);
-            UserHandle userHandle = new UserHandle(userId);
-            IPackageManager ipm = AppGlobals.getPackageManager();
-            if (length == 1) {
-                final ApplicationInfo info = ipm.getApplicationInfo(packageNames[0],
-                        0 /* no flags */, userId);
-                if (info != null) {
-                    detail.label = info.loadLabel(pm).toString();
-                    detail.icon = um.getBadgedIconForUser(info.loadIcon(pm),
-                            new UserHandle(userId));
-                }
-            } else if (length > 1) {
-                detail.detailLabels = new CharSequence[length];
-                detail.detailContentDescriptions = new CharSequence[length];
-                for (int i = 0; i < length; i++) {
-                    final String packageName = packageNames[i];
-                    final PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
-                    final ApplicationInfo appInfo = ipm.getApplicationInfo(packageName,
-                            0 /* no flags */, userId);
-
-                    if (appInfo != null) {
-                        detail.detailLabels[i] = appInfo.loadLabel(pm).toString();
-                        detail.detailContentDescriptions[i] = um.getBadgedLabelForUser(
-                                detail.detailLabels[i], userHandle);
-                        if (packageInfo.sharedUserLabel != 0) {
-                            detail.label = pm.getText(packageName, packageInfo.sharedUserLabel,
-                                    packageInfo.applicationInfo).toString();
-                            detail.icon = um.getBadgedIconForUser(appInfo.loadIcon(pm), userHandle);
-                        }
-                    }
-                }
-            }
-            detail.contentDescription = um.getBadgedLabelForUser(detail.label, userHandle);
-        } catch (NameNotFoundException e) {
-            Log.w(TAG, "Error while building UI detail for uid "+uid, e);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error while building UI detail for uid "+uid, e);
-        }
-
-        if (TextUtils.isEmpty(detail.label)) {
-            detail.label = Integer.toString(uid);
-        }
-
-        return detail;
-    }
-}
index 4f4398f..8ee5535 100644 (file)
@@ -40,19 +40,12 @@ public class NfcForegroundPreference extends DropDownPreference implements
         PaymentBackend.PaymentAppInfo defaultApp = mPaymentBackend.getDefaultApp();
         boolean foregroundMode = mPaymentBackend.isForegroundMode();
         setPersistent(false);
-        setTitle(getContext().getString(R.string.nfc_payment_open_app));
+        setTitle(getContext().getString(R.string.nfc_payment_use_default));
         CharSequence favorOpen;
         CharSequence favorDefault;
         clearItems();
-        if (defaultApp == null) {
-            favorOpen = getContext().getString(R.string.nfc_payment_favor_open_default_unknown);
-            favorDefault = getContext().getString(R.string.nfc_payment_favor_default_default_unknown);
-        } else {
-            favorOpen = getContext().getString(R.string.nfc_payment_favor_open, defaultApp.label);
-            favorDefault = getContext().getString(R.string.nfc_payment_favor_default, defaultApp.label);
-        }
-        addItem(favorOpen.toString(), true);
-        addItem(favorDefault.toString(), false);
+        addItem(getContext().getString(R.string.nfc_payment_favor_open), true);
+        addItem(getContext().getString(R.string.nfc_payment_favor_default), false);
         if (foregroundMode) {
             setSelectedValue(true);
         } else {
index 3ad64fa..e8dcf0b 100644 (file)
@@ -82,8 +82,7 @@ public class NfcPaymentPreference extends DialogPreference implements
         }
         setTitle(R.string.nfc_payment_default);
         if (defaultApp != null) {
-            setSummary(mContext.getString(R.string.nfc_payment_app_and_desc,
-                    defaultApp.label, defaultApp.description));
+            setSummary(defaultApp.label);
         } else {
             setSummary(mContext.getString(R.string.nfc_payment_default_not_set));
         }
index 8e995fe..2ed6d85 100644 (file)
@@ -74,6 +74,7 @@ public class AppNotificationSettings extends SettingsPreferenceFragment {
     private AppRow mAppRow;
     private boolean mCreated;
     private boolean mIsSystemPackage;
+    private int mUid;
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
@@ -110,22 +111,22 @@ public class AppNotificationSettings extends SettingsPreferenceFragment {
         final String pkg = args != null && args.containsKey(AppInfoBase.ARG_PACKAGE_NAME)
                 ? args.getString(AppInfoBase.ARG_PACKAGE_NAME)
                 : intent.getStringExtra(Settings.EXTRA_APP_PACKAGE);
-        final int uid = args != null && args.containsKey(AppInfoBase.ARG_PACKAGE_UID)
+        mUid = args != null && args.containsKey(AppInfoBase.ARG_PACKAGE_UID)
                 ? args.getInt(AppInfoBase.ARG_PACKAGE_UID)
                 : intent.getIntExtra(Settings.EXTRA_APP_UID, -1);
-        if (uid == -1 || TextUtils.isEmpty(pkg)) {
+        if (mUid == -1 || TextUtils.isEmpty(pkg)) {
             Log.w(TAG, "Missing extras: " + Settings.EXTRA_APP_PACKAGE + " was " + pkg + ", "
-                    + Settings.EXTRA_APP_UID + " was " + uid);
+                    + Settings.EXTRA_APP_UID + " was " + mUid);
             toastAndFinish();
             return;
         }
 
-        if (DEBUG) Log.d(TAG, "Load details for pkg=" + pkg + " uid=" + uid);
+        if (DEBUG) Log.d(TAG, "Load details for pkg=" + pkg + " uid=" + mUid);
         final PackageManager pm = getPackageManager();
-        final PackageInfo info = findPackageInfo(pm, pkg, uid);
+        final PackageInfo info = findPackageInfo(pm, pkg, mUid);
         if (info == null) {
             Log.w(TAG, "Failed to find package info: " + Settings.EXTRA_APP_PACKAGE + " was " + pkg
-                    + ", " + Settings.EXTRA_APP_UID + " was " + uid);
+                    + ", " + Settings.EXTRA_APP_UID + " was " + mUid);
             toastAndFinish();
             return;
         }
@@ -157,7 +158,7 @@ public class AppNotificationSettings extends SettingsPreferenceFragment {
                 if (banned) {
                     MetricsLogger.action(getActivity(), MetricsLogger.ACTION_BAN_APP_NOTES, pkg);
                 }
-                final boolean success =  mBackend.setNotificationsBanned(pkg, uid, banned);
+                final boolean success =  mBackend.setNotificationsBanned(pkg, mUid, banned);
                 if (success) {
                     updateDependents(banned);
                 }
@@ -169,7 +170,7 @@ public class AppNotificationSettings extends SettingsPreferenceFragment {
             @Override
             public boolean onPreferenceChange(Preference preference, Object newValue) {
                 final boolean priority = (Boolean) newValue;
-                return mBackend.setHighPriority(pkg, uid, priority);
+                return mBackend.setHighPriority(pkg, mUid, priority);
             }
         });
 
@@ -177,7 +178,7 @@ public class AppNotificationSettings extends SettingsPreferenceFragment {
             @Override
             public boolean onPreferenceChange(Preference preference, Object newValue) {
                 final boolean peekable = (Boolean) newValue;
-                return mBackend.setPeekable(pkg, uid, peekable);
+                return mBackend.setPeekable(pkg, mUid, peekable);
             }
         });
 
@@ -185,7 +186,7 @@ public class AppNotificationSettings extends SettingsPreferenceFragment {
             @Override
             public boolean onPreferenceChange(Preference preference, Object newValue) {
                 final boolean sensitive = (Boolean) newValue;
-                return mBackend.setSensitive(pkg, uid, sensitive);
+                return mBackend.setSensitive(pkg, mUid, sensitive);
             }
         });
 
@@ -203,6 +204,15 @@ public class AppNotificationSettings extends SettingsPreferenceFragment {
         }
     }
 
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mUid != -1 && getPackageManager().getPackagesForUid(mUid) == null) {
+            // App isn't around anymore, must have been removed.
+            finish();
+        }
+    }
+
     private void updateDependents(boolean banned) {
         final boolean lockscreenSecure = new LockPatternUtils(getActivity()).isSecure(
                 UserHandle.myUserId());
index e7a154c..e0d5e8c 100644 (file)
@@ -88,6 +88,7 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
         KEY_MEDIA_VOLUME,
         KEY_ALARM_VOLUME,
         KEY_RING_VOLUME,
+        KEY_NOTIFICATION_VOLUME,
         KEY_ZEN_ACCESS,
         KEY_ZEN_MODE,
     };
index a0d1219..a47b703 100644 (file)
@@ -60,7 +60,9 @@ public class RedactionInterstitial extends SettingsActivity {
                     .putExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, true)
                     .putExtra(EXTRA_PREFS_SET_BACK_TEXT, (String) null)
                     .putExtra(EXTRA_PREFS_SET_NEXT_TEXT, ctx.getString(
-                            R.string.app_notifications_dialog_done));
+                            R.string.app_notifications_dialog_done))
+                    .putExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID,
+                            R.string.lock_screen_notifications_interstitial_title);
         }
     }
 
index 9ba7e92..0c85eca 100644 (file)
@@ -33,7 +33,9 @@ import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.view.View;
 import android.widget.Switch;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import com.android.internal.logging.MetricsLogger;
@@ -231,7 +233,7 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase
     }
 
     private void showDeleteRuleDialog() {
-        new AlertDialog.Builder(mContext)
+        final AlertDialog dialog = new AlertDialog.Builder(mContext)
                 .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, mRule.name))
                 .setNegativeButton(R.string.cancel, null)
                 .setPositiveButton(R.string.zen_mode_delete_rule_button, new OnClickListener() {
@@ -244,6 +246,10 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase
                     }
                 })
                 .show();
+        final View messageView = dialog.findViewById(android.R.id.message);
+        if (messageView != null) {
+            messageView.setTextDirection(View.TEXT_DIRECTION_LOCALE);
+        }
     }
 
     private void toastAndFinish() {
index ab77101..a4c07bd 100644 (file)
@@ -37,8 +37,8 @@ import android.widget.EditText;
 import android.widget.ImageView;
 
 import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.drawable.CircleFramedDrawable;
+import com.android.settingslib.Utils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
 
 /**
  * This class encapsulates a Dialog for editing the user nickname and photo.
index 82e550e..f9f867d 100644 (file)
@@ -45,7 +45,7 @@ import android.widget.ListAdapter;
 import android.widget.ListPopupWindow;
 
 import com.android.settings.R;
-import com.android.settings.drawable.CircleFramedDrawable;
+import com.android.settingslib.drawable.CircleFramedDrawable;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -343,4 +343,4 @@ public class EditUserPhotoController {
             return title;
         }
     }
-}
\ No newline at end of file
+}
index b0b86e4..2531aaa 100644 (file)
@@ -86,7 +86,7 @@ public class RestrictedProfileSettings extends AppRestrictionsFragment
         } else {
             ((TextView) mHeaderView.findViewById(android.R.id.title)).setText(info.name);
             ((ImageView) mHeaderView.findViewById(android.R.id.icon)).setImageDrawable(
-                    Utils.getUserIcon(getActivity(), mUserManager, info));
+                    com.android.settingslib.Utils.getUserIcon(getActivity(), mUserManager, info));
         }
     }
 
index 1849a9a..85d2700 100644 (file)
@@ -64,10 +64,10 @@ import com.android.settings.SelectableEditTextPreference;
 import com.android.settings.SettingsActivity;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
-import com.android.settings.drawable.CircleFramedDrawable;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
+import com.android.settingslib.drawable.CircleFramedDrawable;
 
 import java.util.ArrayList;
 import java.util.Collections;
index 4477e92..d8a00c7 100644 (file)
@@ -41,7 +41,7 @@ abstract public class VoiceSettingsActivity extends Activity {
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        if (isVoiceInteraction() || savedInstanceState == null) {
+        if (isVoiceInteractionRoot()) {
             // Only permit if this is a voice interaction.
             if (onVoiceSettingInteraction(getIntent())) {
                 // If it's complete, finish.
@@ -60,7 +60,7 @@ abstract public class VoiceSettingsActivity extends Activity {
     abstract protected boolean onVoiceSettingInteraction(Intent intent);
 
     /**
-     * Send a notification that the interaction was successful. If {@link prompt} is
+     * Send a notification that the interaction was successful. If {@param prompt} is
      * not null, then it will be read to the user.
      */
     protected void notifySuccess(CharSequence prompt) {
index a131d21..ddb56d1 100644 (file)
@@ -121,6 +121,16 @@ public class VoiceInputListPreference extends AppListPreferenceWithSettings {
         }
     }
 
+    public ComponentName getCurrentService() {
+        if (mHelper.mCurrentVoiceInteraction != null) {
+            return mHelper.mCurrentVoiceInteraction;
+        } else if (mHelper.mCurrentRecognizer != null) {
+            return mHelper.mCurrentRecognizer;
+        } else {
+            return null;
+        }
+    }
+
     private class CustomAdapter extends ArrayAdapter<CharSequence> {
 
         public CustomAdapter(Context context, CharSequence[] objects) {
index ab1c84b..6b035fd 100644 (file)
@@ -71,6 +71,7 @@ class ConfigDialog extends AlertDialog implements TextWatcher,
     private Spinner mIpsecCaCert;
     private Spinner mIpsecServerCert;
     private CheckBox mSaveLogin;
+    private CheckBox mShowOptions;
 
     ConfigDialog(Context context, DialogInterface.OnClickListener listener,
             VpnProfile profile, boolean editing, boolean exists) {
@@ -106,6 +107,7 @@ class ConfigDialog extends AlertDialog implements TextWatcher,
         mIpsecCaCert = (Spinner) mView.findViewById(R.id.ipsec_ca_cert);
         mIpsecServerCert = (Spinner) mView.findViewById(R.id.ipsec_server_cert);
         mSaveLogin = (CheckBox) mView.findViewById(R.id.save_login);
+        mShowOptions = (CheckBox) mView.findViewById(R.id.show_options);
 
         // Second, copy values from the profile.
         mName.setText(mProfile.name);
@@ -140,8 +142,9 @@ class ConfigDialog extends AlertDialog implements TextWatcher,
         mRoutes.addTextChangedListener(this);
         mIpsecSecret.addTextChangedListener(this);
         mIpsecUserCert.setOnItemSelectedListener(this);
+        mShowOptions.setOnClickListener(this);
 
-        // Forth, determine to do editing or connecting.
+        // Fourth, determine whether to do editing or connecting.
         boolean valid = validate(true);
         mEditing = mEditing || !valid;
 
@@ -154,13 +157,10 @@ class ConfigDialog extends AlertDialog implements TextWatcher,
             // Show type-specific fields.
             changeType(mProfile.type);
 
-            // Show advanced options directly if any of them is set.
-            View showOptions = mView.findViewById(R.id.show_options);
-            if (mProfile.searchDomains.isEmpty() && mProfile.dnsServers.isEmpty() &&
-                    mProfile.routes.isEmpty()) {
-                showOptions.setOnClickListener(this);
-            } else {
-                onClick(showOptions);
+            // Switch to advanced view immediately if any advanced options are on
+            if (!mProfile.searchDomains.isEmpty() || !mProfile.dnsServers.isEmpty() ||
+                    !mProfile.routes.isEmpty()) {
+                showAdvancedOptions();
             }
 
             // Create a button to forget the profile if it has already been saved..
@@ -200,6 +200,17 @@ class ConfigDialog extends AlertDialog implements TextWatcher,
     }
 
     @Override
+    public void onRestoreInstanceState(Bundle savedState) {
+        super.onRestoreInstanceState(savedState);
+
+        // Visibility isn't restored by super.onRestoreInstanceState, so re-show the advanced
+        // options here if they were already revealed or set.
+        if (mShowOptions.isChecked()) {
+            showAdvancedOptions();
+        }
+    }
+
+    @Override
     public void afterTextChanged(Editable field) {
         getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validate(mEditing));
     }
@@ -213,9 +224,10 @@ class ConfigDialog extends AlertDialog implements TextWatcher,
     }
 
     @Override
-    public void onClick(View showOptions) {
-        showOptions.setVisibility(View.GONE);
-        mView.findViewById(R.id.options).setVisibility(View.VISIBLE);
+    public void onClick(View view) {
+        if (view == mShowOptions) {
+            showAdvancedOptions();
+        }
     }
 
     @Override
@@ -230,6 +242,11 @@ class ConfigDialog extends AlertDialog implements TextWatcher,
     public void onNothingSelected(AdapterView<?> parent) {
     }
 
+    private void showAdvancedOptions() {
+        mView.findViewById(R.id.options).setVisibility(View.VISIBLE);
+        mShowOptions.setVisibility(View.GONE);
+    }
+
     private void changeType(int type) {
         // First, hide everything.
         mMppe.setVisibility(View.GONE);
index 26b7982..80f9fcd 100644 (file)
@@ -144,7 +144,7 @@ public class ConfigDialogFragment extends DialogFragment implements
 
     private void disconnect(VpnProfile profile) {
         try {
-            LegacyVpnInfo connected = mService.getLegacyVpnInfo();
+            LegacyVpnInfo connected = mService.getLegacyVpnInfo(UserHandle.myUserId());
             if (connected != null && profile.key.equals(connected.key)) {
                 mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN,
                         UserHandle.myUserId());
index 4192d2e..b0a08c8 100644 (file)
@@ -236,7 +236,7 @@ public class VpnSettings extends SettingsPreferenceFragment implements
         try {
             // Legacy VPNs
             mConnectedLegacyVpn = null;
-            LegacyVpnInfo info = mConnectivityService.getLegacyVpnInfo();
+            LegacyVpnInfo info = mConnectivityService.getLegacyVpnInfo(UserHandle.myUserId());
             if (info != null) {
                 ConfigPreference preference = mConfigPreferences.get(info.key);
                 if (preference != null) {
index 1f5aaf4..7f1f747 100644 (file)
@@ -945,6 +945,8 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
                     .getUriFor(Settings.System.SCREEN_BRIGHTNESS), false, this);
             resolver.registerContentObserver(Settings.System
                     .getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), false, this);
+            resolver.registerContentObserver(Settings.System
+                    .getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ), false, this);
         }
 
         void stopObserving() {
index b9612a3..264f681 100644 (file)
@@ -23,6 +23,7 @@ import android.net.wifi.WifiConfiguration;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.preference.Preference;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.view.View;
@@ -49,6 +50,14 @@ public class AccessPointPreference extends Preference {
     private AccessPoint mAccessPoint;
     private Drawable mBadge;
     private int mLevel;
+    private CharSequence mContentDescription;
+
+    static final int[] WIFI_CONNECTION_STRENGTH = {
+        R.string.accessibility_wifi_one_bar,
+        R.string.accessibility_wifi_two_bars,
+        R.string.accessibility_wifi_three_bars,
+        R.string.accessibility_wifi_signal_full
+    };
 
     // Used for dummy pref.
     public AccessPointPreference(Context context, AttributeSet attrs) {
@@ -98,6 +107,7 @@ public class AccessPointPreference extends Preference {
             mTitleView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, mBadge, null);
             mTitleView.setCompoundDrawablePadding(mBadgePadding);
         }
+        view.setContentDescription(mContentDescription);
     }
 
     protected void updateIcon(int level, Context context) {
@@ -155,6 +165,15 @@ public class AccessPointPreference extends Preference {
 
         setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
                 : mAccessPoint.getSettingsSummary());
+
+        mContentDescription = getTitle();
+        if (getSummary() != null) {
+            mContentDescription = TextUtils.concat(mContentDescription, ",", getSummary());
+        }
+        if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
+            mContentDescription = TextUtils.concat(mContentDescription, ",",
+                    getContext().getString(WIFI_CONNECTION_STRENGTH[level]));
+        }
     }
 
     @Override
index 72abe1e..45aafdf 100644 (file)
@@ -147,7 +147,7 @@ public class SavedAccessPointsWifiSettings extends SettingsPreferenceFragment
                 final boolean hideForgetButton = WifiSettings.isEditabilityLockedDown(getActivity(),
                         mDlgAccessPoint.getConfig());
                 mDialog = new WifiDialog(getActivity(), this, mDlgAccessPoint,
-                        false /* not editting */, true /* hide the submit button */,
+                        false /* not editting */, false, true /* hide the submit button */,
                         hideForgetButton);
                 return mDialog;
 
index b43a7ac..254c386 100644 (file)
@@ -34,6 +34,7 @@ import android.widget.CheckBox;
 import android.widget.EditText;
 import android.widget.Spinner;
 import android.widget.TextView;
+import java.nio.charset.Charset;
 
 import com.android.settings.R;
 
@@ -213,9 +214,11 @@ public class WifiApDialog extends AlertDialog implements View.OnClickListener,
     }
 
     private void validate() {
-        if ((mSsid != null && mSsid.length() == 0) ||
-                   ((mSecurityTypeIndex == WPA2_INDEX)&&
-                        mPassword.length() < 8)) {
+        String mSsidString = mSsid.getText().toString();
+        if ((mSsid != null && mSsid.length() == 0)
+                || ((mSecurityTypeIndex == WPA2_INDEX) && mPassword.length() < 8)
+                || (mSsid != null &&
+                Charset.forName("UTF-8").encode(mSsidString).limit() > 32)) {
             getButton(BUTTON_SUBMIT).setEnabled(false);
         } else {
             getButton(BUTTON_SUBMIT).setEnabled(true);
index bf2fa38..741c4a7 100644 (file)
@@ -47,8 +47,15 @@ public class WifiApEnabler {
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) {
-                handleWifiApStateChanged(intent.getIntExtra(
-                        WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED));
+                int state = intent.getIntExtra(
+                        WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
+                if (state == WifiManager.WIFI_AP_STATE_FAILED) {
+                    int reason = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON,
+                            WifiManager.SAP_START_FAILURE_GENERAL);
+                    handleWifiApStateChanged(state, reason);
+                } else {
+                    handleWifiApStateChanged(state, WifiManager.SAP_START_FAILURE_GENERAL);
+                }
             } else if (ConnectivityManager.ACTION_TETHER_STATE_CHANGED.equals(action)) {
                 ArrayList<String> available = intent.getStringArrayListExtra(
                         ConnectivityManager.EXTRA_AVAILABLE_TETHER);
@@ -142,7 +149,7 @@ public class WifiApEnabler {
         }
     }
 
-    private void handleWifiApStateChanged(int state) {
+    private void handleWifiApStateChanged(int state, int reason) {
         switch (state) {
             case WifiManager.WIFI_AP_STATE_ENABLING:
                 mSwitch.setSummary(R.string.wifi_tether_starting);
@@ -169,7 +176,11 @@ public class WifiApEnabler {
                 break;
             default:
                 mSwitch.setChecked(false);
-                mSwitch.setSummary(R.string.wifi_error);
+                if (reason == WifiManager.SAP_START_FAILURE_NO_CHANNEL) {
+                    mSwitch.setSummary(R.string.wifi_sap_no_channel_error);
+                } else {
+                    mSwitch.setSummary(R.string.wifi_error);
+                }
                 enableWifiSwitch();
         }
     }
index 2777185..774c54b 100644 (file)
@@ -142,12 +142,14 @@ public class WifiConfigController implements TextWatcher,
 
     private String[] mLevels;
     private boolean mEdit;
+    private boolean mModify;
     private TextView mSsidView;
 
     private Context mContext;
 
     public WifiConfigController(
-            WifiConfigUiBase parent, View view, AccessPoint accessPoint, boolean edit) {
+            WifiConfigUiBase parent, View view, AccessPoint accessPoint, boolean edit,
+            boolean modify) {
         mConfigUi = parent;
 
         mView = view;
@@ -155,6 +157,7 @@ public class WifiConfigController implements TextWatcher,
         mAccessPointSecurity = (accessPoint == null) ? AccessPoint.SECURITY_NONE :
                 accessPoint.getSecurity();
         mEdit = edit;
+        mModify = modify;
 
         mTextViewChangedHandler = new Handler();
         mContext = mConfigUi.getContext();
@@ -245,7 +248,7 @@ public class WifiConfigController implements TextWatcher,
                 }
             }
 
-            if (mEdit) {
+            if (mModify) {
                 mConfigUi.setSubmitButton(res.getString(R.string.wifi_save));
             } else {
                 final DetailedState state = mAccessPoint.getDetailedState();
@@ -303,12 +306,7 @@ public class WifiConfigController implements TextWatcher,
             }
         }
 
-        if ((mEdit) || (mAccessPoint != null
-                && mAccessPoint.getDetailedState() == null && mAccessPoint.getLevel() != -1)){
-            mConfigUi.setCancelButton(res.getString(R.string.wifi_cancel));
-        }else{
-            mConfigUi.setCancelButton(res.getString(R.string.wifi_display_options_done));
-        }
+        mConfigUi.setCancelButton(res.getString(R.string.wifi_cancel));
         if (mConfigUi.getSubmitButton() != null) {
             enableSubmitIfAppropriate();
         }
@@ -370,7 +368,7 @@ public class WifiConfigController implements TextWatcher,
     }
 
     /* package */ WifiConfiguration getConfig() {
-        if (mAccessPoint != null && mAccessPoint.isSaved() && !mEdit) {
+        if (!mEdit) {
             return null;
         }
 
@@ -460,9 +458,17 @@ public class WifiConfigController implements TextWatcher,
                 String clientCert = (String) mEapUserCertSpinner.getSelectedItem();
                 if (clientCert.equals(unspecifiedCert)) clientCert = "";
                 config.enterpriseConfig.setClientCertificateAlias(clientCert);
-                config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString());
-                config.enterpriseConfig.setAnonymousIdentity(
-                        mEapAnonymousView.getText().toString());
+                if (eapMethod == Eap.SIM || eapMethod == Eap.AKA || eapMethod == Eap.AKA_PRIME) {
+                    config.enterpriseConfig.setIdentity("");
+                    config.enterpriseConfig.setAnonymousIdentity("");
+                } else if (eapMethod == Eap.PWD) {
+                    config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString());
+                    config.enterpriseConfig.setAnonymousIdentity("");
+                } else {
+                    config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString());
+                    config.enterpriseConfig.setAnonymousIdentity(
+                            mEapAnonymousView.getText().toString());
+                }
 
                 if (mPasswordView.isShown()) {
                     // For security reasons, a previous password is not displayed to user.
@@ -768,6 +774,7 @@ public class WifiConfigController implements TextWatcher,
             case WIFI_EAP_METHOD_AKA_PRIME:
                 setPhase2Invisible();
                 setAnonymousIdentInvisible();
+                setCaCertInvisible();
                 setUserCertInvisible();
                 setPasswordInvisible();
                 setIdentityInvisible();
@@ -952,6 +959,10 @@ public class WifiConfigController implements TextWatcher,
         return mEdit;
     }
 
+    public boolean isModify() {
+        return mModify;
+    }
+
     @Override
     public void afterTextChanged(Editable s) {
         mTextViewChangedHandler.post(new Runnable() {
index c18baf5..d052c95 100644 (file)
@@ -31,6 +31,7 @@ class WifiDialog extends AlertDialog implements WifiConfigUiBase {
     static final int BUTTON_FORGET = DialogInterface.BUTTON_NEUTRAL;
 
     private final boolean mEdit;
+    private final boolean mModify;
     private final DialogInterface.OnClickListener mListener;
     private final AccessPoint mAccessPoint;
 
@@ -40,17 +41,18 @@ class WifiDialog extends AlertDialog implements WifiConfigUiBase {
     private boolean mHideForgetButton;
 
     public WifiDialog(Context context, DialogInterface.OnClickListener listener,
-            AccessPoint accessPoint, boolean edit, boolean hideSubmitButton,
-            boolean hideForgetButton) {
-        this(context, listener, accessPoint, edit);
+            AccessPoint accessPoint, boolean edit, boolean modify,
+            boolean hideSubmitButton, boolean hideForgetButton) {
+        this(context, listener, accessPoint, edit, modify);
         mHideSubmitButton = hideSubmitButton;
         mHideForgetButton = hideForgetButton;
     }
 
     public WifiDialog(Context context, DialogInterface.OnClickListener listener,
-            AccessPoint accessPoint, boolean edit) {
+            AccessPoint accessPoint, boolean edit, boolean modify) {
         super(context);
         mEdit = edit;
+        mModify = modify;
         mListener = listener;
         mAccessPoint = accessPoint;
         mHideSubmitButton = false;
@@ -67,7 +69,7 @@ class WifiDialog extends AlertDialog implements WifiConfigUiBase {
         mView = getLayoutInflater().inflate(R.layout.wifi_dialog, null);
         setView(mView);
         setInverseBackgroundForced(true);
-        mController = new WifiConfigController(this, mView, mAccessPoint, mEdit);
+        mController = new WifiConfigController(this, mView, mAccessPoint, mEdit, mModify);
         super.onCreate(savedInstanceState);
 
         if (mHideSubmitButton) {
index c32df9d..7eded24 100644 (file)
@@ -119,6 +119,7 @@ public class WifiSettings extends RestrictedSettingsFragment
 
     // Instance state keys
     private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode";
+    private static final String SAVE_DIALOG_MODIFY_MODE = "modify_mode";
     private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
     private static final String SAVED_WIFI_NFC_DIALOG_STATE = "wifi_nfc_dlg_state";
 
@@ -150,6 +151,7 @@ public class WifiSettings extends RestrictedSettingsFragment
     private boolean mEnableNextOnConnection;
 
     // Save the dialog details
+    private boolean mDlgModify;
     private boolean mDlgEdit;
     private AccessPoint mDlgAccessPoint;
     private Bundle mAccessPointSavedState;
@@ -248,6 +250,7 @@ public class WifiSettings extends RestrictedSettingsFragment
 
         if (savedInstanceState != null) {
             mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
+            mDlgModify = savedInstanceState.getBoolean(SAVE_DIALOG_MODIFY_MODE);
             if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
                 mAccessPointSavedState =
                     savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
@@ -379,6 +382,7 @@ public class WifiSettings extends RestrictedSettingsFragment
         // If the dialog is showing, save its state.
         if (mDialog != null && mDialog.isShowing()) {
             outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit);
+            outState.putBoolean(SAVE_DIALOG_MODIFY_MODE, mDlgModify);
             if (mDlgAccessPoint != null) {
                 mAccessPointSavedState = new Bundle();
                 mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
@@ -506,6 +510,7 @@ public class WifiSettings extends RestrictedSettingsFragment
                     mSelectedAccessPoint.generateOpenNetworkConfig();
                     connect(mSelectedAccessPoint.getConfig());
                 } else {
+                    mDlgModify = false;
                     showDialog(mSelectedAccessPoint, true);
                 }
                 return true;
@@ -515,6 +520,7 @@ public class WifiSettings extends RestrictedSettingsFragment
                 return true;
             }
             case MENU_ID_MODIFY: {
+                mDlgModify = true;
                 showDialog(mSelectedAccessPoint, true);
                 return true;
             }
@@ -539,8 +545,12 @@ public class WifiSettings extends RestrictedSettingsFragment
                     getActivity().invalidateOptionsMenu();
                 }
                 connect(mSelectedAccessPoint.getConfig());
-            } else {
+            } else if (mSelectedAccessPoint.isSaved()){
+                mDlgModify = false;
                 showDialog(mSelectedAccessPoint, false);
+            } else {
+                mDlgModify = false;
+                showDialog(mSelectedAccessPoint, true);
             }
         } else {
             return super.onPreferenceTreeClick(screen, preference);
@@ -607,7 +617,7 @@ public class WifiSettings extends RestrictedSettingsFragment
                 final boolean hideForget = (ap == null || isEditabilityLockedDown(getActivity(),
                         ap.getConfig()));
                 mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit,
-                        /* no hide submit/connect */ false,
+                        mDlgModify, /* no hide submit/connect */ false,
                         /* hide forget if config locked down */ hideForget);
                 return mDialog;
             case WPS_PBC_DIALOG_ID:
@@ -653,16 +663,20 @@ public class WifiSettings extends RestrictedSettingsFragment
                 getPreferenceScreen().removeAll();
 
                 boolean hasAvailableAccessPoints = false;
+                int index = 0;
                 for (AccessPoint accessPoint : accessPoints) {
                     // Ignore access points that are out of range.
                     if (accessPoint.getLevel() != -1) {
                         hasAvailableAccessPoints = true;
                         if (accessPoint.getTag() != null) {
-                            getPreferenceScreen().addPreference((Preference) accessPoint.getTag());
+                            final Preference pref = (Preference) accessPoint.getTag();
+                            pref.setOrder(index++);
+                            getPreferenceScreen().addPreference(pref);
                             continue;
                         }
                         AccessPointPreference preference = new AccessPointPreference(accessPoint,
                                 getActivity(), mUserBadgeCache, false);
+                        preference.setOrder(index++);
 
                         if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr())
                                 && !accessPoint.isSaved()
@@ -821,14 +835,11 @@ public class WifiSettings extends RestrictedSettingsFragment
                     && mSelectedAccessPoint.isSaved()) {
                 connect(mSelectedAccessPoint.getConfig());
             }
-        } else if (config.networkId != INVALID_NETWORK_ID) {
-            if (mSelectedAccessPoint != null) {
-                mWifiManager.save(config, mSaveListener);
-            }
+        } else if (configController.isModify()) {
+            mWifiManager.save(config, mSaveListener);
         } else {
-            if (configController.isEdit()) {
-                mWifiManager.save(config, mSaveListener);
-            } else {
+            mWifiManager.save(config, mSaveListener);
+            if (mSelectedAccessPoint != null) { // Not an "Add network"
                 connect(config);
             }
         }
index 0ed7812..ecbd947 100644 (file)
@@ -22,27 +22,8 @@ import android.text.SpannableStringBuilder;
 
 import com.android.settings.bluetooth.Utf8ByteLengthFilter;
 
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargetNew;
-import dalvik.annotation.TestTargets;
-
-@TestTargetClass(Utf8ByteLengthFilter.class)
 public class Utf8ByteLengthFilterTest extends AndroidTestCase {
 
-    @TestTargets({
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "filter",
-            args = {java.lang.CharSequence.class, int.class, int.class, android.text.Spanned.class,
-                    int.class, int.class}
-        ),
-        @TestTargetNew(
-            level = TestLevel.COMPLETE,
-            method = "Utf8ByteLengthFilter",
-            args = {int.class}
-        )
-    })
     public void testFilter() {
         // Define the variables
         CharSequence source;
index 26cfc9b..11e8265 100644 (file)
@@ -129,18 +129,20 @@ public class VpnTests extends InstrumentationTestCase {
             }
         }
         // disconnect existing vpn if there is any
-        LegacyVpnInfo oldVpn = mService.getLegacyVpnInfo();
+        LegacyVpnInfo oldVpn = mService.getLegacyVpnInfo(UserHandle.myUserId());
         if (oldVpn != null) {
             Log.v(TAG, "disconnect legacy VPN");
             disconnect();
             // wait till the legacy VPN is disconnected.
             int tries = 0;
-            while (tries < MAX_DISCONNECTION_TRIES && mService.getLegacyVpnInfo() != null) {
+            while (tries < MAX_DISCONNECTION_TRIES &&
+                    mService.getLegacyVpnInfo(UserHandle.myUserId()) != null) {
                 tries++;
                 Thread.sleep(10 * 1000);
                 Log.v(TAG, "Wait for legacy VPN to be disconnected.");
             }
-            Assert.assertNull("Failed to disconect VPN", mService.getLegacyVpnInfo());
+            Assert.assertNull("Failed to disconect VPN",
+                    mService.getLegacyVpnInfo(UserHandle.myUserId()));
             // wait for 30 seconds after the previous VPN is disconnected.
             sleep(30 * 1000);
         }
@@ -276,7 +278,7 @@ public class VpnTests extends InstrumentationTestCase {
      * Verify the vpn connection by checking the VPN state, external IP or ping test
      */
     private void validateVpnConnection(VpnProfile profile, boolean pingTestFlag) throws Exception {
-        LegacyVpnInfo legacyVpnInfo = mService.getLegacyVpnInfo();
+        LegacyVpnInfo legacyVpnInfo = mService.getLegacyVpnInfo(UserHandle.myUserId());
         Assert.assertTrue(legacyVpnInfo != null);
 
         long start = System.currentTimeMillis();
@@ -284,7 +286,7 @@ public class VpnTests extends InstrumentationTestCase {
                 (legacyVpnInfo.state != LegacyVpnInfo.STATE_CONNECTED)) {
             Log.v(TAG, "vpn state: " + legacyVpnInfo.state);
             sleep(10 * 1000);
-            legacyVpnInfo = mService.getLegacyVpnInfo();
+            legacyVpnInfo = mService.getLegacyVpnInfo(UserHandle.myUserId());
         }
 
         // the vpn state should be CONNECTED