Merge "Check to see if the power source is a valid charger."
diff --git a/Android.mk b/Android.mk
index b171dbd..c08be7f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,6 +1,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_JAVA_LIBRARIES := bouncycastle
LOCAL_STATIC_JAVA_LIBRARIES := guava
LOCAL_MODULE_TAGS := optional
diff --git a/res/drawable-hdpi/ic_emergency.png b/res/drawable-hdpi/ic_emergency.png
new file mode 100644
index 0000000..89c05e3
--- /dev/null
+++ b/res/drawable-hdpi/ic_emergency.png
Binary files differ
diff --git a/res/drawable-hdpi/stat_sys_phone_call.png b/res/drawable-hdpi/stat_sys_phone_call.png
new file mode 100755
index 0000000..9b5f075
--- /dev/null
+++ b/res/drawable-hdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_emergency.png b/res/drawable-mdpi/ic_emergency.png
new file mode 100755
index 0000000..c6faf1e
--- /dev/null
+++ b/res/drawable-mdpi/ic_emergency.png
Binary files differ
diff --git a/res/drawable-mdpi/stat_sys_phone_call.png b/res/drawable-mdpi/stat_sys_phone_call.png
new file mode 100644
index 0000000..c44d062
--- /dev/null
+++ b/res/drawable-mdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/res/drawable/data_sweep_left.xml b/res/drawable/data_sweep_left.xml
index cb801a0..3532cbc 100644
--- a/res/drawable/data_sweep_left.xml
+++ b/res/drawable/data_sweep_left.xml
@@ -15,6 +15,7 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:enterFadeDuration="@android:integer/config_mediumAnimTime"
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
<item android:state_activated="true" android:state_enabled="true" android:drawable="@drawable/data_sweep_left_activated" />
diff --git a/res/drawable/data_sweep_limit.xml b/res/drawable/data_sweep_limit.xml
index 378b0aa..cfdbfbb 100644
--- a/res/drawable/data_sweep_limit.xml
+++ b/res/drawable/data_sweep_limit.xml
@@ -15,6 +15,7 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:enterFadeDuration="@android:integer/config_mediumAnimTime"
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
<item android:state_activated="true" android:state_enabled="true" android:drawable="@drawable/data_sweep_limit_activated" />
diff --git a/res/drawable/data_sweep_right.xml b/res/drawable/data_sweep_right.xml
index a75a1b2..cbe2a85 100644
--- a/res/drawable/data_sweep_right.xml
+++ b/res/drawable/data_sweep_right.xml
@@ -15,6 +15,7 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:enterFadeDuration="@android:integer/config_mediumAnimTime"
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
<item android:state_activated="true" android:state_enabled="true" android:drawable="@drawable/data_sweep_right_activated" />
diff --git a/res/drawable/data_sweep_warning.xml b/res/drawable/data_sweep_warning.xml
index 001d0c5..8fbe8e7 100644
--- a/res/drawable/data_sweep_warning.xml
+++ b/res/drawable/data_sweep_warning.xml
@@ -15,6 +15,7 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:enterFadeDuration="@android:integer/config_mediumAnimTime"
android:exitFadeDuration="@android:integer/config_mediumAnimTime">
<item android:state_activated="true" android:state_enabled="true" android:drawable="@drawable/data_sweep_warning_activated" />
diff --git a/res/layout/crypt_keeper_password_entry.xml b/res/layout/crypt_keeper_password_entry.xml
index a5193ce..60dcf6a 100644
--- a/res/layout/crypt_keeper_password_entry.xml
+++ b/res/layout/crypt_keeper_password_entry.xml
@@ -42,4 +42,16 @@
android:textColor="#ffffffff"
/>
-</LinearLayout>
\ No newline at end of file
+ <!-- Emergency call button.
+ Text and icon are set by CryptKeeper.updateEmergencyCallButtonState() -->
+ <Button android:id="@+id/emergencyCallButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:layout_marginTop="10dip"
+ style="@*android:style/Widget.Button.Transparent"
+ android:textSize="14sp"
+ android:drawablePadding="6dip"
+ />
+
+</LinearLayout>
diff --git a/res/layout/data_usage_header.xml b/res/layout/data_usage_header.xml
index 3f4ca5b..547d85d 100644
--- a/res/layout/data_usage_header.xml
+++ b/res/layout/data_usage_header.xml
@@ -19,13 +19,18 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <LinearLayout
- android:id="@+id/network_switches"
+ <FrameLayout
+ android:id="@+id/network_switches_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:showDividers="middle|end"
- android:divider="?android:attr/listDivider" />
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:id="@+id/network_switches"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:showDividers="middle|end"
+ android:divider="?android:attr/listDivider" />
+ </FrameLayout>
<LinearLayout
android:layout_width="match_parent"
diff --git a/res/layout/data_usage_summary.xml b/res/layout/data_usage_summary.xml
index 41b8eed..7e68143 100644
--- a/res/layout/data_usage_summary.xml
+++ b/res/layout/data_usage_summary.xml
@@ -20,6 +20,7 @@
android:layout_height="match_parent">
<LinearLayout
+ android:id="@+id/tabs_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
diff --git a/res/layout/trusted_credential.xml b/res/layout/trusted_credential.xml
new file mode 100644
index 0000000..9955a79
--- /dev/null
+++ b/res/layout/trusted_credential.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:paddingRight="?android:attr/scrollbarSize"
+ android:background="?android:attr/selectableItemBackground"
+ android:padding="15dip"
+ >
+ <TextView
+ android:id="@+id/trusted_credential_subject"
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ />
+ <!-- checkbox is invisible and not gone so that the height is consistent between tabs -->
+ <CheckBox
+ android:id="@+id/trusted_credential_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ android:clickable="false"
+ android:focusable="false"
+ android:layout_weight="0"
+ />
+</LinearLayout>
diff --git a/res/layout/trusted_credential_details.xml b/res/layout/trusted_credential_details.xml
new file mode 100644
index 0000000..c18d933
--- /dev/null
+++ b/res/layout/trusted_credential_details.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <FrameLayout
+ android:id="@+id/cert_details"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ />
+ <Button
+ android:id="@+id/cert_remove_button"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="right"
+ android:layout_margin="6dip"
+ />
+</LinearLayout>
diff --git a/res/layout/trusted_credentials.xml b/res/layout/trusted_credentials.xml
new file mode 100644
index 0000000..06ce44b
--- /dev/null
+++ b/res/layout/trusted_credentials.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<TabHost
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="5dp"
+ >
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="5dp"
+ >
+ <FrameLayout
+ android:id="@+id/system_tab"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <ProgressBar
+ android:id="@+id/system_progress"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ />
+ <ListView
+ android:id="@+id/system_list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:visibility="gone"
+ >
+ </ListView>
+ </FrameLayout>
+ <FrameLayout
+ android:id="@+id/user_tab"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <ProgressBar
+ android:id="@+id/user_progress"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ />
+ <ListView
+ android:id="@+id/user_list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:visibility="gone"
+ >
+ </ListView>
+ </FrameLayout>
+ </FrameLayout>
+ </LinearLayout>
+</TabHost>
diff --git a/res/layout/vpn_dialog.xml b/res/layout/vpn_dialog.xml
new file mode 100644
index 0000000..3e4b441
--- /dev/null
+++ b/res/layout/vpn_dialog.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="5mm">
+
+ <LinearLayout android:id="@+id/editor"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+ <TextView style="@style/vpn_label" android:text="@string/vpn_name"/>
+ <EditText style="@style/vpn_value" android:id="@+id/name"
+ android:singleLine="true"/>
+
+ <TextView style="@style/vpn_label" android:text="@string/vpn_type"/>
+ <Spinner style="@style/vpn_value" android:id="@+id/type"
+ android:prompt="@string/vpn_type"
+ android:entries="@array/vpn_types"/>
+
+ <TextView style="@style/vpn_label" android:text="@string/vpn_server"/>
+ <EditText style="@style/vpn_value" android:id="@+id/server"
+ android:singleLine="true"/>
+
+ <CheckBox style="@style/vpn_value" android:id="@+id/mppe"
+ android:text="@string/vpn_mppe"
+ android:visibility="gone"/>
+
+ <LinearLayout android:id="@+id/l2tp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView style="@style/vpn_label" android:text="@string/vpn_l2tp_secret"/>
+ <EditText style="@style/vpn_value" android:id="@+id/l2tp_secret"
+ android:singleLine="true"
+ android:password="true"
+ android:hint="@string/vpn_not_used"/>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/ipsec_psk"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView style="@style/vpn_label" android:text="@string/vpn_ipsec_identifier"/>
+ <EditText style="@style/vpn_value" android:id="@+id/ipsec_identifier"
+ android:singleLine="true"
+ android:hint="@string/vpn_not_used"/>
+
+ <TextView style="@style/vpn_label" android:text="@string/vpn_ipsec_secret"/>
+ <EditText style="@style/vpn_value" android:id="@+id/ipsec_secret"
+ android:singleLine="true"
+ android:password="true"/>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/ipsec_user"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView style="@style/vpn_label" android:text="@string/vpn_ipsec_user_cert"/>
+ <Spinner style="@style/vpn_value" android:id="@+id/ipsec_user_cert"
+ android:prompt="@string/vpn_ipsec_user_cert" />
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/ipsec_ca"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView style="@style/vpn_label" android:text="@string/vpn_ipsec_ca_cert"/>
+ <Spinner style="@style/vpn_value" android:id="@+id/ipsec_ca_cert"
+ android:prompt="@string/vpn_ipsec_ca_cert" />
+ </LinearLayout>
+
+ <TextView style="@style/vpn_label" android:text="@string/vpn_domains"/>
+ <EditText style="@style/vpn_value" android:id="@+id/domains"
+ android:hint="@string/vpn_not_used"/>
+
+ <!-- Not sure if we have time to make it. -->
+ <TextView style="@style/vpn_label" android:text="@string/vpn_routes"
+ android:visibility="gone"/>
+ <EditText style="@style/vpn_value" android:id="@+id/routes"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/login"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+ <TextView style="@style/vpn_label" android:text="@string/vpn_username"/>
+ <EditText style="@style/vpn_value" android:id="@+id/username"
+ android:singleLine="true"/>
+
+ <TextView style="@style/vpn_label" android:text="@string/vpn_password"/>
+ <EditText style="@style/vpn_value" android:id="@+id/password"
+ android:singleLine="true"
+ android:password="true"/>
+
+ <CheckBox style="@style/vpn_value" android:id="@+id/save_login"
+ android:text="@string/vpn_save_login"/>
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 3f2fc23..c0578fa 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -612,5 +612,39 @@
<item>Use HDCP checking for DRM content only</item>
<item>Always use HDCP checking</item>
</string-array>
-</resources>
+ <!-- Match this with the constants in VpnProfile. --> <skip />
+ <!-- Short names for each VPN type, not really translatable. [CHAR LIMIT=20] -->
+ <string-array name="vpn_types" translatable="false">
+ <item>PPTP</item>
+ <item>L2TP/IPSec PSK</item>
+ <item>L2TP/IPSec RSA</item>
+ <item>IPSec Xauth PSK</item>
+ <item>IPSec Xauth RSA</item>
+ <item>IPSec Hybrid RSA</item>
+ </string-array>
+
+ <!-- Match this with the constants in VpnProfile. --> <skip />
+ <!-- Longer descriptions for each VPN type. [CHAR LIMIT=100] -->
+ <string-array name="vpn_types_long">
+ <item>PPTP VPN</item>
+ <item>L2TP/IPSec VPN with pre-shared keys</item>
+ <item>L2TP/IPSec VPN with certificates</item>
+ <item>IPSec VPN with pre-shared keys and Xauth authentication</item>
+ <item>IPSec VPN with certificates and Xauth authentication</item>
+ <item>IPSec VPN with certificates and hybrid authentication</item>
+ </string-array>
+
+ <!-- Match this with the constants in VpnProfile. --> <skip />
+ <!-- Status for a VPN network. [CHAR LIMIT=100] -->
+ <string-array name="vpn_states">
+ <!-- Status message when VPN is connecting. -->
+ <item>Connecting\u2026</item>
+ <!-- Status message when VPN is connected. -->
+ <item>Connected</item>
+ <!-- Status message when VPN is disconnected. -->
+ <item>Disconnected</item>
+ <!-- Status message when VPN failed to connect. -->
+ <item>Failed</item>
+ </string-array>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b0b0a2e..cc677ad 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1082,6 +1082,10 @@
<string name="wifi_notify_open_networks">Network notification</string>
<!-- Checkbox summary for option to notify user when open networks are nearby -->
<string name="wifi_notify_open_networks_summary">Notify me when an open network is available</string>
+ <!-- Checkbox title for option to toggle wifi watchdog service -->
+ <string name="wifi_enable_watchdog_service">Wi-Fi Connectivity Checks</string>
+ <!-- Checkbox summary for option to toggle wifi watchdog service -->
+ <string name="wifi_enable_watchdog_service_summary">Detect and manage potential network connectivity problems</string>
<!-- Setting title for setting the wifi sleep policy -->
<string name="wifi_setting_sleep_policy_title">Wi-Fi disconnect policy</string>
<!-- Setting summary for setting the wifi sleep policy -->
@@ -2611,12 +2615,14 @@
<string name="accessibility_settings">Accessibility</string>
<!-- Settings title for accessibility settings screen -->
<string name="accessibility_settings_title">Accessibility settings</string>
- <!-- Settings summary for accessibility settings -->
+ <!-- Settings summary for accessibility settings [CHAR LIMIT=40] -->
<string name="accessibility_settings_summary">Manage accessibility options</string>
- <!-- Setting Checkbox title for enabling accessibility -->
- <string name="toggle_accessibility_title">Accessibility</string>
- <!-- Setting accessibility services category -->
+ <!-- Setting Checkbox title for enabling accessibility large text [CHAR LIMIT=25] -->
+ <string name="toggle_large_text_title">Large text</string>
+ <!-- Setting accessibility services category [CHAR LIMIT=25] -->
<string name="accessibility_services_category">Accessibility services</string>
+ <!-- Setting Checkbox title for enabling accessibility services [CHAR LIMIT=40] -->
+ <string name="toggle_accessibility_title">Allow accessibility services</string>
<!-- Message for announcing the lack of installed accessibility services. -->
<string name="no_accessibility_services_summary">No installed accessibility services.</string>
<!-- Warning message about security implications of enabling an accessibility service,
@@ -2638,8 +2644,6 @@
applications installed.\n\nYou can download a screen reader for your device from Android
Market.\n\nClick "OK" to install the screen reader.</string>
- <!-- Accessibility settings: Webpage accessibility scripts category [CHAR LIMIT=25] -->
- <string name="accessibility_script_injection_category">Accessibility scripts</string>
<!-- Accessibility settings: Checkbox title for enabling download of accessibility scripts [CHAR LIMIT=40] -->
<string name="accessibility_script_injection_enabled">Download accessibility scripts</string>
<!-- Accessibility settings: Checkbox summary for enabling download of accessibility scripts [CHAR LIMIT=65] -->
@@ -2947,8 +2951,6 @@
<string name="vpn_settings_activity_title">VPN settings</string>
- <!-- Title of VPN connect dialog -->
- <string name="vpn_connect_to">Connect to <xliff:g id="name" example="Work Network">%s</xliff:g></string>
<!-- In VPN connect dialog, for inputing username and password -->
<string name="vpn_username_colon">Username:</string>
<string name="vpn_password_colon">Password:</string>
@@ -2969,8 +2971,6 @@
<string name="vpn_menu_revert">Revert</string>
<string name="vpn_menu_connect">Connect to network</string>
<string name="vpn_menu_disconnect">Disconnect from network</string>
- <string name="vpn_menu_edit">Edit network</string>
- <string name="vpn_menu_delete">Delete network</string>
<!-- VPN error dialog messages -->
<string name="vpn_error_miss_entering">You must enter <xliff:g id="code">%s</xliff:g>.</string>
@@ -3011,7 +3011,6 @@
<string name="vpn_connect_hint">Connect to network</string>
<!-- Name of a VPN profile -->
- <string name="vpn_name">VPN name</string>
<string name="vpn_a_name">a VPN name</string>
<!-- Toast message shown when a profile is added -->
@@ -3033,7 +3032,6 @@
<!-- Preference title -->
<string name="vpn_l2tp_secret_string_title">Set L2TP secret</string>
<!-- Complete term -->
- <string name="vpn_l2tp_secret">L2TP secret</string>
<string name="vpn_a_l2tp_secret">an L2TP secret</string>
<string name="vpn_pptp_encryption_title">encryption</string>
<string name="vpn_pptp_encryption">PPTP encryption</string>
@@ -3097,6 +3095,10 @@
<string name="credentials_reset">Clear credentials</string>
<!-- Summary of preference to reset credential storage [CHAR LIMIT=NONE] -->
<string name="credentials_reset_summary">Remove all certificates</string>
+ <!-- Title of preference to display trusted credentials (aka CA certificates) [CHAR LIMIT=30] -->
+ <string name="trusted_credentials">Trusted credentials</string>
+ <!-- Summary of preference to display trusted credentials (aka CA certificates) [CHAR LIMIT=NONE] -->
+ <string name="trusted_credentials_summary">Display trusted CA certificates</string>
<!-- Title of dialog to enable credential storage [CHAR LIMIT=30] -->
<string name="credentials_unlock">Enter password</string>
@@ -3481,4 +3483,80 @@
<!-- Label displaying current network data usage warning threshold. [CHAR LIMIT=18] -->
<string name="data_usage_sweep_warning"><font size="32"><xliff:g id="number" example="128">%1$s</xliff:g></font> <font size="12"><xliff:g id="unit" example="KB">%2$s</xliff:g></font>\n<font size="12">warning</font></string>
+ <!-- Button at the bottom of the CryptKeeper screen to make an emergency call. -->
+ <string name="cryptkeeper_emergency_call">Emergency call</string>
+ <!-- Button at the bottom of the CryptKeeper screen that lets the user return to a call -->
+ <string name="cryptkeeper_return_to_call">Return to call</string>
+
+ <!-- Input label for the name of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_name">Name</string>
+ <!-- Input label for the type of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_type">Type</string>
+ <!-- Input label for the server address of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_server">Server address</string>
+ <!-- Checkbox label to enable PPP encryption for a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_mppe">PPP encryption (MPPE)</string>
+ <!-- Input label for the L2TP secret of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_l2tp_secret">L2TP secret</string>
+ <!-- Input label for the IPSec identifier of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_ipsec_identifier">IPSec identifier</string>
+ <!-- Input label for the IPSec pre-shared key of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_ipsec_secret">IPSec pre-shared key</string>
+ <!-- Selection label for the IPSec user certificate of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_ipsec_user_cert">IPSec user certificate</string>
+ <!-- Selection label for the IPSec CA certificate of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_ipsec_ca_cert">IPSec CA certificate</string>
+ <!-- Input label for the DNS search domains of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_domains">DNS search domains</string>
+ <!-- Input label for the forwarding routes of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_routes">Forwarding routes</string>
+ <!-- Input label for the username of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_username">Username</string>
+ <!-- Input label for the password of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_password">Password</string>
+ <!-- Checkbox label to save the username and the password for a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_save_login">Save this information</string>
+
+ <!-- Hint for an optional input of a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_not_used">(not used)</string>
+ <!-- Option to not use a CA certificate to verify the VPN server. [CHAR LIMIT=40] -->
+ <string name="vpn_no_ca_cert">(do not verify server)</string>
+
+ <!-- Button label to cancel chaning a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_cancel">Cancel</string>
+ <!-- Button label to save a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_save">Save</string>
+ <!-- Button label to connect to a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_connect">Connect</string>
+ <!-- Dialog title to edit a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_edit">Edit VPN network</string>
+ <!-- Dialog title to connect to a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_connect_to">Connect to <xliff:g id="network" example="School">%s</xliff:g></string>
+
+ <!-- Preference title for VPN settings. [CHAR LIMIT=40] -->
+ <string name="vpn_title">VPN settings</string>
+ <!-- Preference title to create a new VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_create">Add VPN network</string>
+ <!-- Menu item to edit a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_menu_edit">Edit network</string>
+ <!-- Menu item to delete a VPN network. [CHAR LIMIT=40] -->
+ <string name="vpn_menu_delete">Delete network</string>
+
+ <!-- Tab label for built-in system CA certificates. -->
+ <string name="trusted_credentials_system_tab">System</string>
+ <!-- Tab label for user added CA certificates. -->
+ <string name="trusted_credentials_user_tab">User</string>
+ <!-- Button label for disabling a system CA certificate. -->
+ <string name="trusted_credentials_disable_label">Disable</string>
+ <!-- Button label for enabling a system CA certificate. -->
+ <string name="trusted_credentials_enable_label">Enable</string>
+ <!-- Button label for removing a user CA certificate. -->
+ <string name="trusted_credentials_remove_label">Remove</string>
+ <!-- Alert dialog confirmation when enabling a system CA certificate. -->
+ <string name="trusted_credentials_enable_confirmation">Enable the system CA certificate?</string>
+ <!-- Alert dialog confirmation when disabling a system CA certificate. -->
+ <string name="trusted_credentials_disable_confirmation">Disable the system CA certificate?</string>
+ <!-- Alert dialog confirmation when removing a user CA certificate. -->
+ <string name="trusted_credentials_remove_confirmation">Permanently remove the user CA certificate?</string>
+
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index e2d7fe2..2e3c1d9 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -138,4 +138,14 @@
<item name="android:singleLine">true</item>
</style>
+ <style name="vpn_label">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="vpn_value">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
</resources>
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
index 771b69e..90aef84 100644
--- a/res/xml/accessibility_settings.xml
+++ b/res/xml/accessibility_settings.xml
@@ -19,20 +19,23 @@
android:title="@string/accessibility_settings_title">
<CheckBoxPreference
- android:key="toggle_accessibility_service_checkbox"
- android:title="@string/toggle_accessibility_title"
+ android:key="toggle_large_text_checkbox"
+ android:title="@string/toggle_large_text_title"
android:persistent="false"/>
<PreferenceCategory android:key="accessibility_services_category"
- android:title="@string/accessibility_services_category" />
-
- <PreferenceCategory android:key="accessibility_script_injection_category"
- android:title="@string/accessibility_script_injection_category">
+ android:title="@string/accessibility_services_category">
+ <CheckBoxPreference
+ android:key="toggle_accessibility_service_checkbox"
+ android:title="@string/toggle_accessibility_title"
+ android:persistent="false"
+ android:order="-10000"/>
<CheckBoxPreference
android:key="toggle_accessibility_script_injection_checkbox"
android:title="@string/accessibility_script_injection_enabled"
android:summary="@string/accessibility_script_injection_enabled_summary"
- android:persistent="false" />
+ android:persistent="false"
+ android:order="10000" />
</PreferenceCategory>
<PreferenceCategory android:key="power_button_category"
diff --git a/res/xml/security_settings_misc.xml b/res/xml/security_settings_misc.xml
index e996f70..3a88d0b 100644
--- a/res/xml/security_settings_misc.xml
+++ b/res/xml/security_settings_misc.xml
@@ -56,6 +56,11 @@
<PreferenceCategory android:title="@string/credentials_title"
android:persistent="false">
+ <Preference android:title="@string/trusted_credentials"
+ android:summary="@string/trusted_credentials_summary"
+ android:persistent="false"
+ android:fragment="com.android.settings.TrustedCredentials"/>
+
<Preference android:title="@string/credentials_install"
android:summary="@string/credentials_install_summary"
android:persistent="false">
diff --git a/res/xml/vpn_settings2.xml b/res/xml/vpn_settings2.xml
new file mode 100644
index 0000000..189e7ed
--- /dev/null
+++ b/res/xml/vpn_settings2.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/vpn_title">
+ <Preference android:key="add_network"
+ android:title="@string/vpn_create"
+ android:persistent="false"/>
+</PreferenceScreen>
diff --git a/res/xml/wifi_advanced_settings.xml b/res/xml/wifi_advanced_settings.xml
index 1444de1..d7344b1 100644
--- a/res/xml/wifi_advanced_settings.xml
+++ b/res/xml/wifi_advanced_settings.xml
@@ -24,6 +24,13 @@
android:summary="@string/wifi_notify_open_networks_summary"
android:persistent="false" />
+ <!-- android:dependency="enable_wifi" -->
+ <CheckBoxPreference
+ android:key="wifi_enable_watchdog_service"
+ android:title="@string/wifi_enable_watchdog_service"
+ android:summary="@string/wifi_enable_watchdog_service_summary"
+ android:persistent="false" />
+
<ListPreference
android:key="sleep_policy"
android:title="@string/wifi_setting_sleep_policy_title"
diff --git a/src/com/android/settings/AccessibilitySettings.java b/src/com/android/settings/AccessibilitySettings.java
index 9a0db5d..bbb3678 100644
--- a/src/com/android/settings/AccessibilitySettings.java
+++ b/src/com/android/settings/AccessibilitySettings.java
@@ -17,14 +17,17 @@
package com.android.settings;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Service;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
@@ -51,7 +54,12 @@
private static final String DEFAULT_SCREENREADER_MARKET_LINK =
"market://search?q=pname:com.google.android.marvin.talkback";
- private final String TOGGLE_ACCESSIBILITY_CHECKBOX =
+ private static final float LARGE_FONT_SCALE = 1.3f;
+
+ private static final String TOGGLE_LARGE_TEXT_CHECKBOX =
+ "toggle_large_text_checkbox";
+
+ private static final String TOGGLE_ACCESSIBILITY_CHECKBOX =
"toggle_accessibility_service_checkbox";
private static final String ACCESSIBILITY_SERVICES_CATEGORY =
@@ -66,10 +74,10 @@
private static final String POWER_BUTTON_ENDS_CALL_CHECKBOX =
"power_button_ends_call";
- private final String KEY_TOGGLE_ACCESSIBILITY_SERVICE_CHECKBOX =
+ private static final String KEY_TOGGLE_ACCESSIBILITY_SERVICE_CHECKBOX =
"key_toggle_accessibility_service_checkbox";
- private final String KEY_LONG_PRESS_TIMEOUT_LIST_PREFERENCE =
+ private static final String KEY_LONG_PRESS_TIMEOUT_LIST_PREFERENCE =
"long_press_timeout_list_preference";
private static final int DIALOG_ID_DISABLE_ACCESSIBILITY = 1;
@@ -77,6 +85,7 @@
private static final int DIALOG_ID_ENABLE_ACCESSIBILITY_SERVICE = 3;
private static final int DIALOG_ID_NO_ACCESSIBILITY_SERVICES = 4;
+ private CheckBoxPreference mToggleLargeTextCheckBox;
private CheckBoxPreference mToggleAccessibilityCheckBox;
private CheckBoxPreference mToggleScriptInjectionCheckBox;
private SettingsCheckBoxPreference mToggleAccessibilityServiceCheckBox;
@@ -88,6 +97,8 @@
private ListPreference mLongPressTimeoutListPreference;
+ private final Configuration mCurConfig = new Configuration();
+
private Map<String, AccessibilityServiceInfo> mAccessibilityServices =
new LinkedHashMap<String, AccessibilityServiceInfo>();
@@ -100,6 +111,9 @@
addPreferencesFromResource(R.xml.accessibility_settings);
+ mToggleLargeTextCheckBox = (CheckBoxPreference) findPreference(
+ TOGGLE_LARGE_TEXT_CHECKBOX);
+
mAccessibilityServicesCategory =
(PreferenceGroup) findPreference(ACCESSIBILITY_SERVICES_CATEGORY);
@@ -145,7 +159,7 @@
@Override
public void onActivityCreated(Bundle savedInstanceState) {
- addAccessibilitServicePreferences();
+ addAccessibilityServicePreferences();
final HashSet<String> enabled = new HashSet<String>();
String settingValue = Settings.Secure.getString(getContentResolver(),
@@ -192,6 +206,8 @@
showDialog(DIALOG_ID_NO_ACCESSIBILITY_SERVICES);
}
+ readFontSizePreference();
+
super.onActivityCreated(savedInstanceState);
}
@@ -251,10 +267,10 @@
int count = mAccessibilityServicesCategory.getPreferenceCount();
for (int i = 0; i < count; i++) {
Preference pref = mAccessibilityServicesCategory.getPreference(i);
- pref.setEnabled(isEnabled);
+ if (pref != mToggleAccessibilityCheckBox) {
+ pref.setEnabled(isEnabled);
+ }
}
-
- mToggleScriptInjectionCheckBox.setEnabled(isEnabled);
}
@Override
@@ -263,6 +279,13 @@
if (TOGGLE_ACCESSIBILITY_CHECKBOX.equals(key)) {
handleEnableAccessibilityStateChange((CheckBoxPreference) preference);
+ } else if (TOGGLE_LARGE_TEXT_CHECKBOX.equals(key)) {
+ try {
+ mCurConfig.fontScale = mToggleLargeTextCheckBox.isChecked()
+ ? LARGE_FONT_SCALE : 1;
+ ActivityManagerNative.getDefault().updateConfiguration(mCurConfig);
+ } catch (RemoteException e) {
+ }
} else if (POWER_BUTTON_ENDS_CALL_CHECKBOX.equals(key)) {
boolean isChecked = ((CheckBoxPreference) preference).isChecked();
// The checkbox is labeled "Power button ends call"; thus the in-call
@@ -355,20 +378,22 @@
/**
* Adds {@link CheckBoxPreference} for enabling or disabling an accessibility services.
*/
- private void addAccessibilitServicePreferences() {
+ private void addAccessibilityServicePreferences() {
AccessibilityManager accessibilityManager =
(AccessibilityManager) getSystemService(Service.ACCESSIBILITY_SERVICE);
List<AccessibilityServiceInfo> installedServices =
accessibilityManager.getInstalledAccessibilityServiceList();
- if (installedServices.isEmpty()) {
- getPreferenceScreen().removePreference(mAccessibilityServicesCategory);
- return;
+ for (int i = 0; i < mAccessibilityServicesCategory.getPreferenceCount(); i++) {
+ Preference pref = mAccessibilityServicesCategory.getPreference(i);
+ if (pref != mToggleAccessibilityCheckBox
+ && pref != mToggleScriptInjectionCheckBox) {
+ mAccessibilityServicesCategory.removePreference(pref);
+ i--;
+ }
}
- getPreferenceScreen().addPreference(mAccessibilityServicesCategory);
-
for (int i = 0, count = installedServices.size(); i < count; ++i) {
AccessibilityServiceInfo accessibilityServiceInfo = installedServices.get(i);
String key = accessibilityServiceInfo.getId();
@@ -385,6 +410,7 @@
SettingsCheckBoxPreference preference = new SettingsCheckBoxPreference(
getActivity(), settingsIntent);
preference.setKey(key);
+ preference.setOrder(i);
ServiceInfo serviceInfo = accessibilityServiceInfo.getResolveInfo().serviceInfo;
preference.setTitle(serviceInfo.loadLabel(getActivity().getPackageManager()));
mAccessibilityServicesCategory.addPreference(preference);
@@ -392,6 +418,16 @@
}
}
+ public void readFontSizePreference() {
+ try {
+ mCurConfig.updateFrom(
+ ActivityManagerNative.getDefault().getConfiguration());
+ } catch (RemoteException e) {
+ }
+ mToggleLargeTextCheckBox.setChecked(Float.compare(mCurConfig.fontScale,
+ LARGE_FONT_SCALE) == 0);
+ }
+
@Override
public Dialog onCreateDialog(int dialogId) {
switch (dialogId) {
diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java
index 20bf7ce..612f4c0 100644
--- a/src/com/android/settings/CryptKeeper.java
+++ b/src/com/android/settings/CryptKeeper.java
@@ -16,6 +16,7 @@
package com.android.settings;
+import com.android.internal.telephony.ITelephony;
import com.android.internal.widget.PasswordEntryKeyboardHelper;
import com.android.internal.widget.PasswordEntryKeyboardView;
@@ -33,9 +34,11 @@
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.storage.IMountService;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@@ -62,6 +65,9 @@
private static final int COOL_DOWN_ATTEMPTS = 10;
private static final int COOL_DOWN_INTERVAL = 30; // 30 seconds
+ // Intent action for launching the Emergency Dialer activity.
+ static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+
private int mCooldown;
PowerManager.WakeLock mWakeLock;
private EditText mPasswordEntry;
@@ -347,6 +353,8 @@
keyboardView, mPasswordEntry, false);
keyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
}
+
+ updateEmergencyCallButtonState();
}
private IMountService getMountService() {
@@ -381,4 +389,73 @@
}
return false;
}
-}
\ No newline at end of file
+
+ //
+ // Code to update the state of, and handle clicks from, the "Emergency call" button.
+ //
+ // This code is mostly duplicated from the corresponding code in
+ // LockPatternUtils and LockPatternKeyguardView under frameworks/base.
+ //
+
+ private void updateEmergencyCallButtonState() {
+ Button button = (Button) findViewById(R.id.emergencyCallButton);
+ // The button isn't present at all in some configurations.
+ if (button == null) return;
+
+ if (isEmergencyCallCapable()) {
+ button.setVisibility(View.VISIBLE);
+ button.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ takeEmergencyCallAction();
+ }
+ });
+ } else {
+ button.setVisibility(View.GONE);
+ return;
+ }
+
+ int newState = TelephonyManager.getDefault().getCallState();
+ int textId;
+ if (newState == TelephonyManager.CALL_STATE_OFFHOOK) {
+ // show "return to call" text and show phone icon
+ textId = R.string.cryptkeeper_return_to_call;
+ int phoneCallIcon = R.drawable.stat_sys_phone_call;
+ button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0);
+ } else {
+ textId = R.string.cryptkeeper_emergency_call;
+ int emergencyIcon = R.drawable.ic_emergency;
+ button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0);
+ }
+ button.setText(textId);
+ }
+
+ private boolean isEmergencyCallCapable() {
+ return getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
+ }
+
+ private void takeEmergencyCallAction() {
+ if (TelephonyManager.getDefault().getCallState() == TelephonyManager.CALL_STATE_OFFHOOK) {
+ resumeCall();
+ } else {
+ launchEmergencyDialer();
+ }
+ }
+
+ private void resumeCall() {
+ ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+ if (phone != null) {
+ try {
+ phone.showCallScreen();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony service: " + e);
+ }
+ }
+ }
+
+ private void launchEmergencyDialer() {
+ Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ startActivity(intent);
+ }
+}
diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java
index 098f57a..bd79669 100644
--- a/src/com/android/settings/DataUsageSummary.java
+++ b/src/com/android/settings/DataUsageSummary.java
@@ -71,6 +71,7 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
@@ -136,12 +137,14 @@
private SharedPreferences mPrefs;
private TabHost mTabHost;
+ private ViewGroup mTabsContainer;
private TabWidget mTabWidget;
private ListView mListView;
private DataUsageAdapter mAdapter;
private ViewGroup mHeader;
+ private ViewGroup mNetworkSwitchesContainer;
private LinearLayout mNetworkSwitches;
private Switch mDataEnabled;
private View mDataEnabledView;
@@ -176,6 +179,7 @@
private NetworkStatsHistory mHistory;
private NetworkStatsHistory mDetailHistory;
+ private String mCurrentTab = null;
private String mIntentTab = null;
/** Flag used to ignore listeners during binding. */
@@ -209,6 +213,7 @@
final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
+ mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
mListView = (ListView) view.findViewById(android.R.id.list);
@@ -220,6 +225,8 @@
{
// bind network switches
+ mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
+ R.id.network_switches_container);
mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
mDataEnabled = new Switch(inflater.getContext());
@@ -263,9 +270,8 @@
mAppSwitches.addView(mAppRestrictView);
}
- // TODO: tweak these transitions
- final LayoutTransition transition = new LayoutTransition();
- mHeader.setLayoutTransition(transition);
+ // only assign layout transitions once first layout is finished
+ mHeader.getViewTreeObserver().addOnGlobalLayoutListener(mFirstLayoutListener);
mAdapter = new DataUsageAdapter();
mListView.setOnItemClickListener(mListListener);
@@ -345,6 +351,25 @@
}
/**
+ * Listener to setup {@link LayoutTransition} after first layout pass.
+ */
+ private OnGlobalLayoutListener mFirstLayoutListener = new OnGlobalLayoutListener() {
+ /** {@inheritDoc} */
+ public void onGlobalLayout() {
+ mHeader.getViewTreeObserver().removeGlobalOnLayoutListener(mFirstLayoutListener);
+
+ mTabsContainer.setLayoutTransition(new LayoutTransition());
+ mHeader.setLayoutTransition(new LayoutTransition());
+ mNetworkSwitchesContainer.setLayoutTransition(new LayoutTransition());
+
+ final LayoutTransition chartTransition = new LayoutTransition();
+ chartTransition.setStartDelay(LayoutTransition.APPEARING, 0);
+ chartTransition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
+ mChart.setLayoutTransition(chartTransition);
+ }
+ };
+
+ /**
* Rebuild all tabs based on {@link NetworkPolicyEditor} and
* {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
* first tab, and kicks off a full rebind of body contents.
@@ -434,6 +459,9 @@
throw new IllegalStateException("no mobile or wifi radios");
}
+ final boolean tabChanged = !currentTab.equals(mCurrentTab);
+ mCurrentTab = currentTab;
+
if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
if (TAB_WIFI.equals(currentTab)) {
@@ -480,7 +508,8 @@
// bind chart to historical stats
mChart.bindNetworkStats(mHistory);
- updatePolicy(true);
+ // only update policy when switching tabs
+ updatePolicy(tabChanged);
updateAppDetail();
// force scroll to top of body
@@ -607,6 +636,12 @@
// we fall through to update cycle list for detail mode
} else {
mNetworkSwitches.setVisibility(View.VISIBLE);
+
+ // when heading back to summary without cycle refresh, kick details
+ // update to repopulate list.
+ if (!refreshCycle) {
+ updateDetailData();
+ }
}
final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
diff --git a/src/com/android/settings/TextToSpeechSettings.java b/src/com/android/settings/TextToSpeechSettings.java
index f7945d8..d76f08f 100644
--- a/src/com/android/settings/TextToSpeechSettings.java
+++ b/src/com/android/settings/TextToSpeechSettings.java
@@ -26,15 +26,12 @@
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
-import android.preference.PreferenceGroup;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.speech.tts.TextToSpeech;
@@ -695,13 +692,16 @@
mTts = new TextToSpeech(getActivity().getApplicationContext(), this, engine);
mEnableDemo = false;
mVoicesMissing = false;
- updateWidgetState();
- checkVoiceData();
- // Finally, persist this value to settings.
+ // Persist this value to settings and update the UI before we check
+ // voice data because if the TTS class connected without any exception, "engine"
+ // will be the default engine irrespective of whether the voice check
+ // passes or not.
Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, engine);
- // .. and update the UI.
mDefaultSynthPref.setValue(engine);
+ updateWidgetState();
+
+ checkVoiceData();
Log.v(TAG, "The default synth is now: " + engine);
}
diff --git a/src/com/android/settings/TrustedCredentials.java b/src/com/android/settings/TrustedCredentials.java
new file mode 100644
index 0000000..987d2dc
--- /dev/null
+++ b/src/com/android/settings/TrustedCredentials.java
@@ -0,0 +1,390 @@
+/*
+ * 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;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.content.DialogInterface;
+import android.net.http.SslCertificate;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.security.KeyChain.KeyChainConnection;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.FrameLayout;
+import android.widget.ListView;
+import android.widget.TabHost;
+import android.widget.TextView;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import org.apache.harmony.xnet.provider.jsse.TrustedCertificateStore;
+
+public class TrustedCredentials extends Fragment {
+
+ private static final String TAG = "TrustedCredentials";
+
+ private enum Tab {
+ SYSTEM("system",
+ R.string.trusted_credentials_system_tab,
+ R.id.system_tab,
+ R.id.system_progress,
+ R.id.system_list,
+ true),
+ USER("user",
+ R.string.trusted_credentials_user_tab,
+ R.id.user_tab,
+ R.id.user_progress,
+ R.id.user_list,
+ false);
+
+ private final String mTag;
+ private final int mLabel;
+ private final int mView;
+ private final int mProgress;
+ private final int mList;
+ private final boolean mCheckbox;
+ private Tab(String tag, int label, int view, int progress, int list, boolean checkbox) {
+ mTag = tag;
+ mLabel = label;
+ mView = view;
+ mProgress = progress;
+ mList = list;
+ mCheckbox = checkbox;
+ }
+ private Set<String> getAliases(TrustedCertificateStore store) {
+ switch (this) {
+ case SYSTEM:
+ return store.allSystemAliases();
+ case USER:
+ return store.userAliases();
+ }
+ throw new AssertionError();
+ }
+ private boolean deleted(TrustedCertificateStore store, String alias) {
+ switch (this) {
+ case SYSTEM:
+ return !store.containsAlias(alias);
+ case USER:
+ return false;
+ }
+ throw new AssertionError();
+ }
+ private int getButtonLabel(CertHolder certHolder) {
+ switch (this) {
+ case SYSTEM:
+ if (certHolder.mDeleted) {
+ return R.string.trusted_credentials_enable_label;
+ }
+ return R.string.trusted_credentials_disable_label;
+ case USER:
+ return R.string.trusted_credentials_remove_label;
+ }
+ throw new AssertionError();
+ }
+ private int getButtonConfirmation(CertHolder certHolder) {
+ switch (this) {
+ case SYSTEM:
+ if (certHolder.mDeleted) {
+ return R.string.trusted_credentials_enable_confirmation;
+ }
+ return R.string.trusted_credentials_disable_confirmation;
+ case USER:
+ return R.string.trusted_credentials_remove_confirmation;
+ }
+ throw new AssertionError();
+ }
+ private void postOperationUpdate(boolean ok, CertHolder certHolder) {
+ if (ok) {
+ if (certHolder.mTab.mCheckbox) {
+ certHolder.mDeleted = !certHolder.mDeleted;
+ } else {
+ certHolder.mAdapter.mCertHolders.remove(certHolder);
+ }
+ certHolder.mAdapter.notifyDataSetChanged();
+ } else {
+ // bail, reload to reset to known state
+ certHolder.mAdapter.load();
+ }
+ }
+ }
+
+ // be careful not to use this on the UI thread since it is does file operations
+ private final TrustedCertificateStore mStore = new TrustedCertificateStore();
+
+ private TabHost mTabHost;
+
+ @Override public View onCreateView(
+ LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
+ mTabHost = (TabHost) inflater.inflate(R.layout.trusted_credentials, parent, false);
+ mTabHost.setup();
+ addTab(Tab.SYSTEM);
+ // TODO add Install button on Tab.USER to go to CertInstaller like KeyChainActivity
+ addTab(Tab.USER);
+ return mTabHost;
+ }
+
+ private void addTab(Tab tab) {
+ TabHost.TabSpec systemSpec = mTabHost.newTabSpec(tab.mTag)
+ .setIndicator(getActivity().getString(tab.mLabel))
+ .setContent(tab.mView);
+ mTabHost.addTab(systemSpec);
+
+ ListView lv = (ListView) mTabHost.findViewById(tab.mList);
+ final TrustedCertificateAdapter adapter = new TrustedCertificateAdapter(tab);
+ lv.setAdapter(adapter);
+ lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
+ showCertDialog(adapter.getItem(pos));
+ }
+ });
+ }
+
+ private class TrustedCertificateAdapter extends BaseAdapter {
+ private final List<CertHolder> mCertHolders = new ArrayList<CertHolder>();
+ private final Tab mTab;
+ private TrustedCertificateAdapter(Tab tab) {
+ mTab = tab;
+ load();
+ }
+ private void load() {
+ new AliasLoader().execute();
+ }
+ @Override public int getCount() {
+ return mCertHolders.size();
+ }
+ @Override public CertHolder getItem(int position) {
+ return mCertHolders.get(position);
+ }
+ @Override public long getItemId(int position) {
+ return position;
+ }
+ @Override public View getView(int position, View view, ViewGroup parent) {
+ ViewHolder holder;
+ if (view == null) {
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ view = inflater.inflate(R.layout.trusted_credential, parent, false);
+ holder = new ViewHolder();
+ holder.mSubjectView = (TextView)view.findViewById(R.id.trusted_credential_subject);
+ holder.mCheckBox = (CheckBox) view.findViewById(R.id.trusted_credential_status);
+ view.setTag(holder);
+ } else {
+ holder = (ViewHolder) view.getTag();
+ }
+ CertHolder certHolder = mCertHolders.get(position);
+ holder.mSubjectView.setText(certHolder.mSubject);
+ if (mTab.mCheckbox) {
+ holder.mCheckBox.setChecked(!certHolder.mDeleted);
+ holder.mCheckBox.setVisibility(View.VISIBLE);
+ }
+ return view;
+ };
+
+ private class AliasLoader extends AsyncTask<Void, Void, List<CertHolder>> {
+ @Override protected void onPreExecute() {
+ View content = mTabHost.getTabContentView();
+ content.findViewById(mTab.mProgress).setVisibility(View.VISIBLE);
+ content.findViewById(mTab.mList).setVisibility(View.GONE);
+ }
+ @Override protected List<CertHolder> doInBackground(Void... params) {
+ Set<String> aliases = mTab.getAliases(mStore);
+ List<CertHolder> certHolders = new ArrayList<CertHolder>(aliases.size());
+ for (String alias : aliases) {
+ X509Certificate cert = (X509Certificate) mStore.getCertificate(alias, true);
+ certHolders.add(new CertHolder(mStore,
+ TrustedCertificateAdapter.this,
+ mTab,
+ alias,
+ cert));
+ }
+ Collections.sort(certHolders);
+ return certHolders;
+ }
+ @Override protected void onPostExecute(List<CertHolder> certHolders) {
+ mCertHolders.clear();
+ mCertHolders.addAll(certHolders);
+ notifyDataSetChanged();
+ View content = mTabHost.getTabContentView();
+ content.findViewById(mTab.mProgress).setVisibility(View.GONE);
+ content.findViewById(mTab.mList).setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ private static class CertHolder implements Comparable<CertHolder> {
+ private final TrustedCertificateStore mStore;
+ private final TrustedCertificateAdapter mAdapter;
+ private final Tab mTab;
+ private final String mAlias;
+ private final X509Certificate mX509Cert;
+
+ private final SslCertificate mSslCert;
+ private final String mSubject;
+ private boolean mDeleted;
+
+ private CertHolder(TrustedCertificateStore store,
+ TrustedCertificateAdapter adapter,
+ Tab tab,
+ String alias,
+ X509Certificate x509Cert) {
+ mStore = store;
+ mAdapter = adapter;
+ mTab = tab;
+ mAlias = alias;
+ mX509Cert = x509Cert;
+
+ mSslCert = new SslCertificate(x509Cert);
+
+ String cn = mSslCert.getIssuedTo().getCName();
+ String o = mSslCert.getIssuedTo().getOName();
+ String ou = mSslCert.getIssuedTo().getUName();
+ StringBuilder sb = new StringBuilder();
+ if (!cn.isEmpty()) {
+ sb.append("CN=" + cn);
+ }
+ if (!o.isEmpty()) {
+ if (sb.length() != 0) {
+ sb.append(", ");
+ }
+ sb.append("O=" + o);
+ }
+ if (!ou.isEmpty()) {
+ if (sb.length() != 0) {
+ sb.append(", ");
+ }
+ sb.append("OU=" + ou);
+ }
+ if (sb.length() != 0) {
+ mSubject = sb.toString();
+ } else {
+ mSubject = mSslCert.getIssuedTo().getDName();
+ }
+
+ mDeleted = mTab.deleted(mStore, mAlias);
+ }
+ @Override public int compareTo(CertHolder o) {
+ return this.mSubject.compareTo(o.mSubject);
+ }
+ @Override public boolean equals(Object o) {
+ if (!(o instanceof CertHolder)) {
+ return false;
+ }
+ CertHolder other = (CertHolder) o;
+ return mAlias.equals(other.mAlias);
+ }
+ @Override public int hashCode() {
+ return mAlias.hashCode();
+ }
+ }
+
+ private static class ViewHolder {
+ private TextView mSubjectView;
+ private CheckBox mCheckBox;
+ }
+
+ private void showCertDialog(final CertHolder certHolder) {
+ View view = View.inflate(getActivity(), R.layout.trusted_credential_details, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(com.android.internal.R.string.ssl_certificate);
+ builder.setView(view);
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override public void onClick(DialogInterface dialog, int id) {
+ dialog.dismiss();
+ }
+ });
+ final Dialog certDialog = builder.create();
+
+ FrameLayout details = (FrameLayout) view.findViewById(R.id.cert_details);
+ details.addView(certHolder.mSslCert.inflateCertificateView(getActivity()));
+
+ Button removeButton = (Button) view.findViewById(R.id.cert_remove_button);
+ removeButton.setText(certHolder.mTab.getButtonLabel(certHolder));
+ removeButton.setOnClickListener(new View.OnClickListener() {
+ @Override public void onClick(View v) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(certHolder.mTab.getButtonConfirmation(certHolder));
+ builder.setPositiveButton(
+ android.R.string.yes, new DialogInterface.OnClickListener() {
+ @Override public void onClick(DialogInterface dialog, int id) {
+ new AliasOperation(certHolder).execute();
+ dialog.dismiss();
+ certDialog.dismiss();
+ }
+ });
+ builder.setNegativeButton(
+ android.R.string.no, new DialogInterface.OnClickListener() {
+ @Override public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+ }
+ });
+
+ certDialog.show();
+ }
+
+ private class AliasOperation extends AsyncTask<Void, Void, Boolean> {
+ private final CertHolder mCertHolder;
+ private AliasOperation(CertHolder certHolder) {
+ mCertHolder = certHolder;
+ }
+ @Override protected Boolean doInBackground(Void... params) {
+ try {
+ KeyChainConnection keyChainConnection = KeyChain.bind(getActivity());
+ IKeyChainService service = keyChainConnection.getService();
+ try {
+ if (mCertHolder.mDeleted) {
+ byte[] bytes = mCertHolder.mX509Cert.getEncoded();
+ service.installCaCertificate(bytes);
+ return true;
+ } else {
+ return service.deleteCaCertificate(mCertHolder.mAlias);
+ }
+ } finally {
+ keyChainConnection.close();
+ }
+ } catch (CertificateEncodingException e) {
+ return false;
+ } catch (IllegalStateException e) {
+ // used by installCaCertificate to report errors
+ return false;
+ } catch (RemoteException e) {
+ return false;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return false;
+ }
+ }
+ @Override protected void onPostExecute(Boolean ok) {
+ mCertHolder.mTab.postOperationUpdate(ok, mCertHolder);
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index caf7aef..1c02f74 100644
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -282,7 +282,7 @@
intent.setPackage(mAppEntry.info.packageName);
List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 0);
if ((homes != null && homes.size() > 0) ||
- (mPackageInfo != null &&
+ (mPackageInfo != null && mPackageInfo.signatures != null &&
sys.signatures[0].equals(mPackageInfo.signatures[0]))) {
// Disable button for core system applications.
mUninstallButton.setText(R.string.disable_text);
@@ -482,8 +482,10 @@
ActivityManager am = (ActivityManager)
getActivity().getSystemService(Context.ACTIVITY_SERVICE);
int compatMode = am.getPackageScreenCompatMode(packageName);
- if (compatMode == ActivityManager.COMPAT_MODE_DISABLED
- || compatMode == ActivityManager.COMPAT_MODE_ENABLED) {
+ // For now these are always off; this is the old UI model which we
+ // are no longer using.
+ if (false && (compatMode == ActivityManager.COMPAT_MODE_DISABLED
+ || compatMode == ActivityManager.COMPAT_MODE_ENABLED)) {
mScreenCompatSection.setVisibility(View.VISIBLE);
mAskCompatibilityCB.setChecked(am.getPackageAskScreenCompat(packageName));
mAskCompatibilityCB.setOnCheckedChangeListener(this);
diff --git a/src/com/android/settings/vpn2/VpnDialog.java b/src/com/android/settings/vpn2/VpnDialog.java
new file mode 100644
index 0000000..b3e417b
--- /dev/null
+++ b/src/com/android/settings/vpn2/VpnDialog.java
@@ -0,0 +1,325 @@
+/*
+ * 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.vpn2;
+
+import com.android.settings.R;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+class VpnDialog extends AlertDialog implements TextWatcher, OnItemSelectedListener {
+ private static final String DUMMY = "\r\r\r\r";
+
+ private static String getDummy(String secret) {
+ return secret.isEmpty() ? "" : DUMMY;
+ }
+
+ private static String getSecret(TextView dummy) {
+ String secret = dummy.getText().toString();
+ return DUMMY.equals(secret) ? "" : secret;
+ }
+
+ private final KeyStore mKeyStore = KeyStore.getInstance();
+ private final DialogInterface.OnClickListener mListener;
+ private final VpnProfile mProfile;
+
+ private boolean mEditing;
+
+ private View mView;
+
+ private TextView mName;
+ private Spinner mType;
+ private TextView mServer;
+ private TextView mUsername;
+ private TextView mPassword;
+ private TextView mDomains;
+ private TextView mRoutes;
+ private CheckBox mMppe;
+ private TextView mL2tpSecret;
+ private TextView mIpsecIdentifier;
+ private TextView mIpsecSecret;
+ private Spinner mIpsecUserCert;
+ private Spinner mIpsecCaCert;
+ private CheckBox mSaveLogin;
+
+ VpnDialog(Context context, DialogInterface.OnClickListener listener,
+ VpnProfile profile, boolean editing) {
+ super(context);
+ mListener = listener;
+ mProfile = profile;
+ mEditing = editing;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ mView = getLayoutInflater().inflate(R.layout.vpn_dialog, null);
+ setView(mView);
+ setInverseBackgroundForced(true);
+
+ Context context = getContext();
+
+ // First, find out all the fields.
+ mName = (TextView) mView.findViewById(R.id.name);
+ mType = (Spinner) mView.findViewById(R.id.type);
+ mServer = (TextView) mView.findViewById(R.id.server);
+ mUsername = (TextView) mView.findViewById(R.id.username);
+ mPassword = (TextView) mView.findViewById(R.id.password);
+ mDomains = (TextView) mView.findViewById(R.id.domains);
+ mRoutes = (TextView) mView.findViewById(R.id.routes);
+ mMppe = (CheckBox) mView.findViewById(R.id.mppe);
+ mL2tpSecret = (TextView) mView.findViewById(R.id.l2tp_secret);
+ mIpsecIdentifier = (TextView) mView.findViewById(R.id.ipsec_identifier);
+ mIpsecSecret = (TextView) mView.findViewById(R.id.ipsec_secret);
+ mIpsecUserCert = (Spinner) mView.findViewById(R.id.ipsec_user_cert);
+ mIpsecCaCert = (Spinner) mView.findViewById(R.id.ipsec_ca_cert);
+ mSaveLogin = (CheckBox) mView.findViewById(R.id.save_login);
+
+ // Second, copy values from the profile.
+ mName.setText(mProfile.name);
+ mType.setSelection(mProfile.type);
+ mServer.setText(mProfile.server);
+ mUsername.setText(mProfile.username);
+ mPassword.setText(getDummy(mProfile.password));
+ mDomains.setText(mProfile.domains);
+ mRoutes.setText(mProfile.routes);
+ mMppe.setChecked(mProfile.mppe);
+ mL2tpSecret.setText(getDummy(mProfile.l2tpSecret));
+ mIpsecIdentifier.setText(mProfile.ipsecIdentifier);
+ mIpsecSecret.setText(getDummy(mProfile.ipsecSecret));
+ loadCertificates(mIpsecUserCert, Credentials.USER_CERTIFICATE,
+ 0, mProfile.ipsecUserCert);
+ loadCertificates(mIpsecUserCert, Credentials.CA_CERTIFICATE,
+ R.string.vpn_no_ca_cert, mProfile.ipsecCaCert);
+ mSaveLogin.setChecked(mProfile.saveLogin);
+
+ // Third, add listeners to required fields.
+ mName.addTextChangedListener(this);
+ mType.setOnItemSelectedListener(this);
+ mServer.addTextChangedListener(this);
+ mUsername.addTextChangedListener(this);
+ mPassword.addTextChangedListener(this);
+ mIpsecSecret.addTextChangedListener(this);
+ mIpsecUserCert.setOnItemSelectedListener(this);
+
+ // Forth, determine to do editing or connecting.
+ boolean valid = validate(true);
+ mEditing = mEditing || !valid;
+
+ if (mEditing) {
+ setTitle(R.string.vpn_edit);
+
+ // Show common fields.
+ mView.findViewById(R.id.editor).setVisibility(View.VISIBLE);
+
+ // Show type-specific fields.
+ changeType(mProfile.type);
+
+ // Create a button to save the profile.
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(R.string.vpn_save), mListener);
+ } else {
+ setTitle(context.getString(R.string.vpn_connect_to, mProfile.name));
+
+ // Not editing, just show username and password.
+ mView.findViewById(R.id.login).setVisibility(View.VISIBLE);
+
+ // Create a button to connect the network.
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(R.string.vpn_connect), mListener);
+ }
+
+ // Always provide a cancel button.
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ context.getString(R.string.vpn_cancel), mListener);
+
+ // Let AlertDialog create everything.
+ super.onCreate(null);
+
+ // Disable the action button if necessary.
+ getButton(DialogInterface.BUTTON_POSITIVE)
+ .setEnabled(mEditing ? valid : validate(false));
+ }
+
+ @Override
+ public void afterTextChanged(Editable field) {
+ getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validate(mEditing));
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ if (parent == mType) {
+ changeType(position);
+ }
+ getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validate(false));
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+
+ private void changeType(int type) {
+ // First, hide everything.
+ mMppe.setVisibility(View.GONE);
+ mView.findViewById(R.id.l2tp).setVisibility(View.GONE);
+ mView.findViewById(R.id.ipsec_psk).setVisibility(View.GONE);
+ mView.findViewById(R.id.ipsec_user).setVisibility(View.GONE);
+ mView.findViewById(R.id.ipsec_ca).setVisibility(View.GONE);
+
+ // Then, unhide type-specific fields.
+ switch (type) {
+ case VpnProfile.TYPE_PPTP:
+ mMppe.setVisibility(View.VISIBLE);
+ break;
+
+ case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+ mView.findViewById(R.id.l2tp).setVisibility(View.VISIBLE);
+ // fall through
+ case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
+ mView.findViewById(R.id.ipsec_psk).setVisibility(View.VISIBLE);
+ break;
+
+ case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+ mView.findViewById(R.id.l2tp).setVisibility(View.VISIBLE);
+ // fall through
+ case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
+ mView.findViewById(R.id.ipsec_ca).setVisibility(View.VISIBLE);
+ // fall through
+ case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
+ mView.findViewById(R.id.ipsec_user).setVisibility(View.VISIBLE);
+ break;
+ }
+ }
+
+ private boolean validate(boolean editing) {
+ if (!editing) {
+ return mUsername.getText().length() != 0 && mPassword.getText().length() != 0;
+ }
+ if (mName.getText().length() == 0 || mServer.getText().length() == 0) {
+ return false;
+ }
+ switch (mType.getSelectedItemPosition()) {
+ case VpnProfile.TYPE_PPTP:
+ return true;
+
+ case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+ case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
+ return mIpsecSecret.getText().length() != 0;
+
+ case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+ case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
+ case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
+ return mIpsecUserCert.getSelectedItemPosition() != 0;
+ }
+ return false;
+ }
+
+ private void loadCertificates(Spinner spinner, String prefix, int firstId, String selected) {
+ Context context = getContext();
+ String first = (firstId == 0) ? "" : context.getString(firstId);
+ String[] certs = mKeyStore.saw(prefix);
+
+ if (certs == null || certs.length == 0) {
+ certs = new String[] {first};
+ } else {
+ String[] array = new String[certs.length + 1];
+ array[0] = first;
+ System.arraycopy(certs, 0, array, 1, certs.length);
+ certs = array;
+ }
+
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(
+ context, android.R.layout.simple_spinner_item, certs);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinner.setAdapter(adapter);
+
+ for (int i = 1; i < certs.length; ++i) {
+ if (certs[i].equals(selected)) {
+ spinner.setSelection(i);
+ break;
+ }
+ }
+ }
+
+ boolean isEditing() {
+ return mEditing;
+ }
+
+ VpnProfile getProfile() {
+ // First, save common fields.
+ VpnProfile profile = new VpnProfile(mProfile.key);
+ profile.name = mName.getText().toString();
+ profile.type = mType.getSelectedItemPosition();
+ profile.server = mServer.getText().toString().trim();
+ profile.username = mUsername.getText().toString();
+ profile.password = getSecret(mPassword);
+ profile.domains = mDomains.getText().toString().trim();
+ profile.routes = mRoutes.getText().toString().trim();
+
+ // Then, save type-specific fields.
+ switch (profile.type) {
+ case VpnProfile.TYPE_PPTP:
+ profile.mppe = mMppe.isChecked();
+ break;
+
+ case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+ profile.l2tpSecret = getSecret(mL2tpSecret);
+ // fall through
+ case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
+ profile.ipsecSecret = getSecret(mIpsecSecret);
+ break;
+
+ case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+ profile.l2tpSecret = getSecret(mL2tpSecret);
+ // fall through
+ case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
+ if (mIpsecCaCert.getSelectedItemPosition() != 0) {
+ profile.ipsecCaCert = (String) mIpsecCaCert.getSelectedItem();
+ }
+ // fall through
+ case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
+ if (mIpsecUserCert.getSelectedItemPosition() != 0) {
+ profile.ipsecUserCert = (String) mIpsecUserCert.getSelectedItem();
+ }
+ break;
+ }
+
+ profile.saveLogin = mSaveLogin.isChecked();
+ return profile;
+ }
+}
diff --git a/src/com/android/settings/vpn2/VpnProfile.java b/src/com/android/settings/vpn2/VpnProfile.java
new file mode 100644
index 0000000..9e4c528
--- /dev/null
+++ b/src/com/android/settings/vpn2/VpnProfile.java
@@ -0,0 +1,115 @@
+/*
+ * 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.vpn2;
+
+import java.nio.charset.Charsets;
+
+/**
+ * Parcel-like entity class for VPN profiles. To keep things simple, all
+ * fields are package private. Methods are provided for serialization, so
+ * storage can be implemented easily. Two rules are set for this class.
+ * First, all fields must be kept non-null. Second, always make a copy
+ * using clone() before modifying.
+ */
+class VpnProfile implements Cloneable {
+ // Match these constants with R.array.vpn_types.
+ static final int TYPE_PPTP = 0;
+ static final int TYPE_L2TP_IPSEC_PSK = 1;
+ static final int TYPE_L2TP_IPSEC_RSA = 2;
+ static final int TYPE_IPSEC_XAUTH_PSK = 3;
+ static final int TYPE_IPSEC_XAUTH_RSA = 4;
+ static final int TYPE_IPSEC_HYBRID_RSA = 5;
+ static final int TYPE_MAX = 5;
+
+ // Entity fields.
+ final String key; // -1
+ String name = ""; // 0
+ int type = TYPE_PPTP; // 1
+ String server = ""; // 2
+ String username = ""; // 3
+ String password = ""; // 4
+ String domains = ""; // 5
+ String routes = ""; // 6
+ boolean mppe = false; // 7
+ String l2tpSecret = ""; // 8
+ String ipsecIdentifier = "";// 9
+ String ipsecSecret = ""; // 10
+ String ipsecUserCert = ""; // 11
+ String ipsecCaCert = ""; // 12
+
+ // Helper fields.
+ boolean saveLogin = false;
+
+ VpnProfile(String key) {
+ this.key = key;
+ }
+
+ static VpnProfile decode(String key, byte[] value) {
+ try {
+ if (key == null) {
+ return null;
+ }
+
+ String[] values = new String(value, Charsets.UTF_8).split("\0", -1);
+ // Currently it always has 13 fields.
+ if (values.length < 13) {
+ return null;
+ }
+
+ VpnProfile profile = new VpnProfile(key);
+ profile.name = values[0];
+ profile.type = Integer.valueOf(values[1]);
+ if (profile.type < 0 || profile.type > TYPE_MAX) {
+ return null;
+ }
+ profile.server = values[2];
+ profile.username = values[3];
+ profile.password = values[4];
+ profile.domains = values[5];
+ profile.routes = values[6];
+ profile.mppe = Boolean.valueOf(values[7]);
+ profile.l2tpSecret = values[8];
+ profile.ipsecIdentifier = values[9];
+ profile.ipsecSecret = values[10];
+ profile.ipsecUserCert = values[11];
+ profile.ipsecCaCert = values[12];
+
+ profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
+ return profile;
+ } catch (Exception e) {
+ // ignore
+ }
+ return null;
+ }
+
+ byte[] encode() {
+ StringBuilder builder = new StringBuilder(name);
+ builder.append('\0').append(type);
+ builder.append('\0').append(server);
+ builder.append('\0').append(saveLogin ? username : "");
+ builder.append('\0').append(saveLogin ? password : "");
+ builder.append('\0').append(domains);
+ builder.append('\0').append(routes);
+ builder.append('\0').append(mppe);
+ builder.append('\0').append(l2tpSecret);
+ builder.append('\0').append(ipsecIdentifier);
+ builder.append('\0').append(ipsecSecret);
+ builder.append('\0').append(ipsecUserCert);
+ builder.append('\0').append(ipsecCaCert);
+ return builder.toString().getBytes(Charsets.UTF_8);
+ }
+}
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
new file mode 100644
index 0000000..6662dd9
--- /dev/null
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -0,0 +1,347 @@
+/*
+ * 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.vpn2;
+
+import com.android.settings.R;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.preference.PreferenceGroup;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+import com.android.settings.SettingsPreferenceFragment;
+
+import java.util.HashMap;
+
+public class VpnSettings extends SettingsPreferenceFragment implements
+ Handler.Callback, Preference.OnPreferenceClickListener,
+ DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+
+ private static final String TAG = "VpnSettings";
+
+ // Match these constants with R.array.vpn_states.
+ private static final int STATE_NONE = -1;
+ private static final int STATE_CONNECTING = 0;
+ private static final int STATE_CONNECTED = 1;
+ private static final int STATE_DISCONNECTED = 2;
+ private static final int STATE_FAILED = 3;
+
+ private final KeyStore mKeyStore = KeyStore.getInstance();
+ private boolean mUnlocking = false;
+
+ private HashMap<String, VpnPreference> mPreferences;
+ private VpnDialog mDialog;
+ private String mSelectedKey;
+ private Handler mHandler;
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ addPreferencesFromResource(R.xml.vpn_settings2);
+ PreferenceGroup group = getPreferenceScreen();
+ group.setOrderingAsAdded(false);
+ group.findPreference("add_network").setOnPreferenceClickListener(this);
+
+ if (savedState != null) {
+ VpnProfile profile = VpnProfile.decode(savedState.getString("VpnKey"),
+ savedState.getByteArray("VpnProfile"));
+ if (profile != null) {
+ mDialog = new VpnDialog(getActivity(), this, profile,
+ savedState.getBoolean("VpnEditing"));
+ }
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle savedState) {
+ // We do not save view hierarchy, as they are just profiles.
+ if (mDialog != null) {
+ VpnProfile profile = mDialog.getProfile();
+ savedState.putString("VpnKey", profile.key);
+ savedState.putByteArray("VpnProfile", profile.encode());
+ savedState.putBoolean("VpnEditing", mDialog.isEditing());
+ }
+ // else?
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // Check KeyStore here, so others do not need to deal with it.
+ if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
+ if (!mUnlocking) {
+ // Let us unlock KeyStore. See you later!
+ Credentials.getInstance().unlock(getActivity());
+ } else {
+ // We already tried, but it is still not working!
+ getActivity().getFragmentManager().popBackStack();
+ }
+ mUnlocking = !mUnlocking;
+ return;
+ }
+
+ // Now KeyStore is always unlocked. Reset the flag.
+ mUnlocking = false;
+
+ // Currently we are the only user of profiles in KeyStore.
+ // Assuming KeyStore and KeyGuard do the right thing, we can
+ // safely cache profiles in the memory.
+ if (mPreferences == null) {
+ mPreferences = new HashMap<String, VpnPreference>();
+
+ String[] keys = mKeyStore.saw(Credentials.VPN);
+ if (keys != null && keys.length > 0) {
+ Context context = getActivity();
+
+ for (String key : keys) {
+ VpnProfile profile = VpnProfile.decode(key,
+ mKeyStore.get(Credentials.VPN + key));
+ if (profile == null) {
+ Log.w(TAG, "bad profile: key = " + key);
+ mKeyStore.delete(Credentials.VPN + key);
+ } else {
+ VpnPreference preference = new VpnPreference(context, profile);
+ mPreferences.put(key, preference);
+ }
+ }
+ }
+ }
+ PreferenceGroup group = getPreferenceScreen();
+ for (VpnPreference preference : mPreferences.values()) {
+ group.addPreference(preference);
+ }
+
+ // Show the dialog if there is one.
+ if (mDialog != null) {
+ mDialog.setOnDismissListener(this);
+ mDialog.show();
+ }
+
+ // Start monitoring.
+ if (mHandler == null) {
+ mHandler = new Handler(this);
+ }
+ mHandler.sendEmptyMessage(0);
+
+ // Register for context menu. Hmmm, getListView() is hidden?
+ registerForContextMenu(getListView());
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ // Hide the dialog if there is one.
+ if (mDialog != null) {
+ mDialog.setOnDismissListener(null);
+ mDialog.dismiss();
+ }
+
+ // Unregister for context menu.
+ unregisterForContextMenu(getListView());
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ // Here is the exit of a dialog.
+ mDialog = null;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int button) {
+ if (button == DialogInterface.BUTTON_POSITIVE) {
+ // Always save the profile.
+ VpnProfile profile = mDialog.getProfile();
+ mKeyStore.put(Credentials.VPN + profile.key, profile.encode());
+
+ // Update the preference.
+ VpnPreference preference = mPreferences.get(profile.key);
+ if (preference != null) {
+ disconnect(profile.key);
+ preference.update(profile);
+ } else {
+ preference = new VpnPreference(getActivity(), profile);
+ mPreferences.put(profile.key, preference);
+ getPreferenceScreen().addPreference(preference);
+ }
+
+ // If we are not editing, connect!
+ if (!mDialog.isEditing()) {
+ connect(profile.key);
+ }
+ }
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
+ if (mDialog != null) {
+ Log.v(TAG, "onCreateContextMenu() is called when mDialog != null");
+ return;
+ }
+
+ if (info instanceof AdapterContextMenuInfo) {
+ Preference preference = (Preference) getListView().getItemAtPosition(
+ ((AdapterContextMenuInfo) info).position);
+ if (preference instanceof VpnPreference) {
+ VpnProfile profile = ((VpnPreference) preference).getProfile();
+ mSelectedKey = profile.key;
+ menu.setHeaderTitle(profile.name);
+ menu.add(Menu.NONE, R.string.vpn_menu_edit, 0, R.string.vpn_menu_edit);
+ menu.add(Menu.NONE, R.string.vpn_menu_delete, 0, R.string.vpn_menu_delete);
+ }
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ if (mDialog != null) {
+ Log.v(TAG, "onContextItemSelected() is called when mDialog != null");
+ return false;
+ }
+
+ VpnPreference preference = mPreferences.get(mSelectedKey);
+ if (preference == null) {
+ Log.v(TAG, "onContextItemSelected() is called but no preference is found");
+ return false;
+ }
+
+ switch (item.getItemId()) {
+ case R.string.vpn_menu_edit:
+ mDialog = new VpnDialog(getActivity(), this, preference.getProfile(), true);
+ mDialog.setOnDismissListener(this);
+ mDialog.show();
+ return true;
+ case R.string.vpn_menu_delete:
+ disconnect(mSelectedKey);
+ getPreferenceScreen().removePreference(preference);
+ mPreferences.remove(mSelectedKey);
+ mKeyStore.delete(Credentials.VPN + mSelectedKey);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (mDialog != null) {
+ Log.v(TAG, "onPreferenceClick() is called when mDialog != null");
+ return true;
+ }
+
+ if (preference instanceof VpnPreference) {
+ mDialog = new VpnDialog(getActivity(), this,
+ ((VpnPreference) preference).getProfile(), false);
+ } else {
+ // Generate a new key. Here we just use the current time.
+ long millis = System.currentTimeMillis();
+ while (mPreferences.containsKey(Long.toHexString(millis))) {
+ ++millis;
+ }
+ mDialog = new VpnDialog(getActivity(), this,
+ new VpnProfile(Long.toHexString(millis)), true);
+ }
+ mDialog.setOnDismissListener(this);
+ mDialog.show();
+ return true;
+ }
+
+ @Override
+ public boolean handleMessage(Message message) {
+ mHandler.removeMessages(0);
+
+ if (isResumed()) {
+
+
+
+
+ mHandler.sendEmptyMessageDelayed(0, 1000);
+ }
+ return true;
+ }
+
+ private void connect(String key) {
+ }
+
+ private void disconnect(String key) {
+ }
+
+
+ private class VpnPreference extends Preference {
+ private VpnProfile mProfile;
+ private int mState = STATE_NONE;
+
+ VpnPreference(Context context, VpnProfile profile) {
+ super(context);
+ setPersistent(false);
+ setOnPreferenceClickListener(VpnSettings.this);
+
+ mProfile = profile;
+ update();
+ }
+
+ VpnProfile getProfile() {
+ return mProfile;
+ }
+
+ void update(VpnProfile profile) {
+ mProfile = profile;
+ update();
+ }
+
+ void update() {
+ if (mState != STATE_NONE) {
+ String[] states = getContext().getResources()
+ .getStringArray(R.array.vpn_states);
+ setSummary(states[mState]);
+ } else {
+ String[] types = getContext().getResources()
+ .getStringArray(R.array.vpn_types_long);
+ setSummary(types[mProfile.type]);
+ }
+ setTitle(mProfile.name);
+ notifyChanged();
+ }
+
+ @Override
+ public int compareTo(Preference preference) {
+ int result = 1;
+ if (preference instanceof VpnPreference) {
+ VpnPreference another = (VpnPreference) preference;
+
+ if ((result = another.mState - mState) == 0 &&
+ (result = mProfile.name.compareTo(another.mProfile.name)) == 0 &&
+ (result = mProfile.type - another.mProfile.type) == 0) {
+ result = mProfile.key.compareTo(another.mProfile.key);
+ }
+ }
+ return result;
+ }
+ }
+}
diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java
index d8344d5..6c9ded4 100644
--- a/src/com/android/settings/widget/ChartSweepView.java
+++ b/src/com/android/settings/widget/ChartSweepView.java
@@ -188,7 +188,12 @@
}
public float getPoint() {
- return mAxis.convertToPoint(mValue);
+ if (isEnabled()) {
+ return mAxis.convertToPoint(mValue);
+ } else {
+ // when disabled, show along top edge
+ return 0;
+ }
}
@Override
diff --git a/src/com/android/settings/widget/ChartView.java b/src/com/android/settings/widget/ChartView.java
index bf5616d..9223ca6 100644
--- a/src/com/android/settings/widget/ChartView.java
+++ b/src/com/android/settings/widget/ChartView.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
@@ -93,7 +94,6 @@
// sweep is always placed along specific dimension
final ChartSweepView sweep = (ChartSweepView) child;
final Rect sweepMargins = sweep.getSweepMargins();
- final float point = sweep.getPoint();
if (sweep.getFollowAxis() == ChartSweepView.HORIZONTAL) {
parentRect.left = parentRect.right =
diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java
index 6fe4042..a8bdaa6 100644
--- a/src/com/android/settings/widget/DataUsageChartView.java
+++ b/src/com/android/settings/widget/DataUsageChartView.java
@@ -129,9 +129,7 @@
mSweepLimit.setValue(policy.limitBytes);
mSweepLimit.setEnabled(true);
} else {
- // TODO: set limit default based on axis maximum
mSweepLimit.setVisibility(View.VISIBLE);
- mSweepLimit.setValue(5 * GB_IN_BYTES);
mSweepLimit.setEnabled(false);
}
@@ -189,6 +187,15 @@
}
}
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (!isActivated()) {
+ return true;
+ } else {
+ return super.onInterceptTouchEvent(ev);
+ }
+ }
+
/**
* Return current inspection range (start and end time) based on internal
* {@link ChartSweepView} positions.
@@ -302,56 +309,45 @@
public static class DataAxis implements ChartAxis {
private long mMin;
private long mMax;
- private long mMinLog;
- private long mMaxLog;
private float mSize;
public DataAxis() {
// TODO: adapt ranges to show when history >5GB, and handle 4G
// interfaces with higher limits.
- setBounds(1 * MB_IN_BYTES, 5 * GB_IN_BYTES);
+ setBounds(0, 5 * GB_IN_BYTES);
}
/** {@inheritDoc} */
public void setBounds(long min, long max) {
mMin = min;
mMax = max;
- mMinLog = (long) Math.log(mMin);
- mMaxLog = (long) Math.log(mMax);
}
/** {@inheritDoc} */
public void setSize(float size) {
- this.mSize = size;
+ mSize = size;
}
/** {@inheritDoc} */
public float convertToPoint(long value) {
- return (mSize * (value - mMin)) / (mMax - mMin);
-
- // TODO: finish tweaking log scale
-// if (value > mMin) {
-// return (float) ((mSize * (Math.log(value) - mMinLog)) / (mMaxLog - mMinLog));
-// } else {
-// return 0;
-// }
+ // TODO: this assumes range of [0,5]GB
+ final double fraction = Math.pow(
+ 10, 0.36884343106175160321 * Math.log10(value) + -3.62828151137812282556);
+ return (float) fraction * mSize;
}
/** {@inheritDoc} */
public long convertToValue(float point) {
- return (long) (mMin + ((point * (mMax - mMin)) / mSize));
-
- // TODO: finish tweaking log scale
-// return (long) Math.pow(Math.E, (mMinLog + ((point * (mMaxLog - mMinLog)) / mSize)));
+ final double y = point / mSize;
+ // TODO: this assumes range of [0,5]GB
+ final double fraction = 6.869341163271789302 * Math.pow(10, 9)
+ * Math.pow(y, 2.71117746931646030774);
+ return (long) fraction;
}
/** {@inheritDoc} */
public CharSequence getLabel(long value) {
-
// TODO: use exploded string here
-
-
- // TODO: convert to string
return Long.toString(value);
}
@@ -365,13 +361,13 @@
public float[] getTickPoints() {
final float[] tickPoints = new float[16];
- long value = mMax;
- float mult = 0.8f;
+ final long jump = ((mMax - mMin) / tickPoints.length);
+ long value = mMin;
for (int i = 0; i < tickPoints.length; i++) {
tickPoints[i] = convertToPoint(value);
- value = (long) (value * mult);
- mult *= 0.9;
+ value += jump;
}
+
return tickPoints;
}
}
diff --git a/src/com/android/settings/wifi/AdvancedSettings.java b/src/com/android/settings/wifi/AdvancedSettings.java
index 4855c99..cd7b8a3 100644
--- a/src/com/android/settings/wifi/AdvancedSettings.java
+++ b/src/com/android/settings/wifi/AdvancedSettings.java
@@ -43,6 +43,7 @@
private static final String KEY_FREQUENCY_BAND = "frequency_band";
private static final String KEY_NOTIFY_OPEN_NETWORKS = "notify_open_networks";
private static final String KEY_SLEEP_POLICY = "sleep_policy";
+ private static final String KEY_ENABLE_WIFI_WATCHDOG = "wifi_enable_watchdog_service";
private WifiManager mWifiManager;
@@ -72,6 +73,13 @@
Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1);
notifyOpenNetworks.setEnabled(mWifiManager.isWifiEnabled());
+ CheckBoxPreference watchdogEnabled =
+ (CheckBoxPreference) findPreference(KEY_ENABLE_WIFI_WATCHDOG);
+ watchdogEnabled.setChecked(Secure.getInt(getContentResolver(),
+ Secure.WIFI_WATCHDOG_ON, 1) == 1);
+
+ watchdogEnabled.setEnabled(mWifiManager.isWifiEnabled());
+
ListPreference frequencyPref = (ListPreference) findPreference(KEY_FREQUENCY_BAND);
if (mWifiManager.isDualBandSupported()) {
@@ -111,12 +119,17 @@
Secure.putInt(getContentResolver(),
Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
((CheckBoxPreference) preference).isChecked() ? 1 : 0);
+ } else if (KEY_ENABLE_WIFI_WATCHDOG.equals(key)) {
+ Secure.putInt(getContentResolver(),
+ Secure.WIFI_WATCHDOG_ON,
+ ((CheckBoxPreference) preference).isChecked() ? 1 : 0);
} else {
return super.onPreferenceTreeClick(screen, preference);
}
return true;
}
+ @Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
diff --git a/src/com/android/settings/wifi/AdvancedWifiSettings.java b/src/com/android/settings/wifi/AdvancedWifiSettings.java
index 1da8b68..bc92b3a 100644
--- a/src/com/android/settings/wifi/AdvancedWifiSettings.java
+++ b/src/com/android/settings/wifi/AdvancedWifiSettings.java
@@ -76,6 +76,7 @@
}
}
+ @Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
if (key == null) return true;