Merge "New UX for app usage screen"
diff --git a/res/drawable-sw600dp/setup_illustration.xml b/res/drawable-sw600dp/setup_illustration.xml
deleted file mode 100644
index c0e54e2..0000000
--- a/res/drawable-sw600dp/setup_illustration.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:autoMirrored="true">
-
- <item>
- <bitmap android:src="@drawable/setup_illustration_horizontal_tile"
- android:gravity="top"
- android:tileModeX="repeat"
- android:tileModeY="disabled" />
- </item>
-
- <item android:id="@+id/illustration_image">
- <bitmap android:src="@drawable/setup_illustration_wifi"
- android:gravity="top|start" />
- </item>
-
-</layer-list>
diff --git a/res/drawable/setup_illustration.xml b/res/drawable/setup_illustration.xml
deleted file mode 100644
index f8d96d5..0000000
--- a/res/drawable/setup_illustration.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:autoMirrored="true">
-
- <item android:id="@+id/illustration_image">
- <bitmap android:src="@drawable/setup_illustration_wifi"
- android:gravity="fill" />
- </item>
-
-</layer-list>
diff --git a/res/drawable/setup_illustration_bg.xml b/res/drawable/setup_illustration_bg.xml
deleted file mode 100644
index 0a229c7..0000000
--- a/res/drawable/setup_illustration_bg.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/setup_illustration_tile"
- android:tileMode="repeat" />
diff --git a/res/drawable/setup_wizard_card_bg.xml b/res/drawable/setup_wizard_card_bg.xml
deleted file mode 100644
index 8621437..0000000
--- a/res/drawable/setup_wizard_card_bg.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <corners
- android:topLeftRadius="@dimen/setup_wizard_card_corner_radius"
- android:topRightRadius="@dimen/setup_wizard_card_corner_radius" />
-
- <solid android:color="?android:attr/colorBackground" />
-
-</shape>
-
diff --git a/res/layout-land/setup_preference.xml b/res/layout-land/setup_preference.xml
deleted file mode 100644
index bee46ed..0000000
--- a/res/layout-land/setup_preference.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (c) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <FrameLayout
- android:id="@+id/title_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/setup_illustration_bg"
- android:elevation="@dimen/setup_wizard_title_area_elevation">
-
- <TextView
- android:id="@+id/title"
- style="@style/SetupTitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/wifi_setup_wizard_title" />
-
- </FrameLayout>
-
- <ListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:cacheColorHint="@android:color/transparent"
- android:clipToPadding="false"
- android:drawSelectorOnTop="false"
- android:headerDividersEnabled="false"
- android:scrollbarAlwaysDrawVerticalTrack="true" />
-
- <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
- android:id="@+id/navigation_bar"
- style="@style/setup_wizard_navbar_style" />
-
-</LinearLayout>
-
diff --git a/res/layout-land/setup_template.xml b/res/layout-land/setup_template.xml
deleted file mode 100644
index cc3402f..0000000
--- a/res/layout-land/setup_template.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <RelativeLayout
- android:layout_weight="1"
- android:layout_width="match_parent"
- android:layout_height="0dp">
-
- <FrameLayout
- android:id="@+id/title_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:background="@drawable/setup_illustration_bg"
- android:elevation="@dimen/setup_wizard_title_area_elevation">
-
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/wifi_setup_title"
- style="@style/SetupTitle"/>
- </FrameLayout>
-
- <ScrollView
- android:id="@+id/bottom_scroll_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/title_area"
- android:layout_alignParentBottom="true"
- android:fillViewport="true">
-
- <FrameLayout android:id="@+id/setup_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"/>
-
- </ScrollView>
-
- </RelativeLayout>
-
- <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
- android:id="@+id/navigation_bar"
- android:layout_width="match_parent"
- android:layout_height="56dip"
- style="@style/setup_wizard_navbar_style"/>
-</LinearLayout>
-
diff --git a/res/layout-sw600dp-land/setup_preference.xml b/res/layout-sw600dp-land/setup_preference.xml
deleted file mode 100644
index 883d1de..0000000
--- a/res/layout-sw600dp-land/setup_preference.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (c) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <com.android.settings.widget.SetupWizardIllustration
- android:id="@+id/setup_illustration"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:foreground="@drawable/setup_illustration"
- android:background="@drawable/setup_illustration_bg">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="start|top"
- android:weightSum="16">
-
- <TextView
- android:id="@+id/title"
- style="@style/SetupCardTitle"
- android:layout_width="1dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/setup_wizard_tablet_illustration_height"
- android:layout_weight="6"
- android:text="@string/wifi_setup_wizard_title"/>
-
- <LinearLayout
- android:layout_width="1dp"
- android:layout_height="match_parent"
- android:layout_marginTop="@dimen/setup_wizard_card_land_margin_top"
- android:layout_weight="8"
- android:background="@drawable/setup_wizard_card_bg"
- android:elevation="@dimen/setup_wizard_card_elevation"
- android:orientation="vertical">
-
- <ListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:cacheColorHint="@android:color/transparent"
- android:clipToPadding="false"
- android:drawSelectorOnTop="false"
- android:headerDividersEnabled="false"
- android:scrollbarAlwaysDrawVerticalTrack="true" />
-
- </LinearLayout>
-
- </LinearLayout>
-
- </com.android.settings.widget.SetupWizardIllustration>
-
- <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
- android:id="@+id/navigation_bar"
- style="@style/setup_wizard_navbar_style" />
-
-</LinearLayout>
-
diff --git a/res/layout-sw600dp-land/setup_template.xml b/res/layout-sw600dp-land/setup_template.xml
deleted file mode 100644
index a58dd3d..0000000
--- a/res/layout-sw600dp-land/setup_template.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <com.android.settings.widget.SetupWizardIllustration
- android:id="@+id/setup_illustration"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:background="@drawable/setup_illustration_bg"
- android:foreground="@drawable/setup_illustration">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="start|top"
- android:weightSum="16">
-
- <TextView
- android:id="@+id/title"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/setup_wizard_tablet_illustration_height"
- android:layout_weight="6"
- android:text="@string/wifi_setup_title"
- style="@style/SetupCardTitle"/>
-
- <ScrollView
- android:id="@+id/bottom_scroll_view"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_marginTop="@dimen/setup_wizard_card_land_margin_top"
- android:layout_weight="8"
- android:background="@drawable/setup_wizard_card_bg"
- android:elevation="@dimen/setup_wizard_card_elevation"
- android:fillViewport="true">
-
- <FrameLayout android:id="@+id/setup_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"/>
-
- </ScrollView>
-
- </LinearLayout>
-
- </com.android.settings.widget.SetupWizardIllustration>
-
- <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
- android:id="@+id/navigation_bar"
- android:layout_width="match_parent"
- android:layout_height="56dip"
- style="@style/setup_wizard_navbar_style"/>
-
-</LinearLayout>
-
diff --git a/res/layout-sw600dp/setup_preference.xml b/res/layout-sw600dp/setup_preference.xml
deleted file mode 100644
index cc12003..0000000
--- a/res/layout-sw600dp/setup_preference.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 Google Inc.
-
- 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="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <com.android.settings.widget.SetupWizardIllustration
- android:id="@+id/setup_illustration"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:background="@drawable/setup_illustration_bg"
- android:foreground="@drawable/setup_illustration">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginTop="@dimen/setup_wizard_tablet_illustration_height"
- android:orientation="vertical"
- android:paddingLeft="@dimen/setup_wizard_card_port_margin_sides"
- android:paddingRight="@dimen/setup_wizard_card_port_margin_sides">
-
- <TextView
- android:id="@+id/title"
- style="@style/SetupCardTitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/wifi_setup_wizard_title" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:background="@drawable/setup_wizard_card_bg"
- android:elevation="@dimen/setup_wizard_card_elevation"
- android:orientation="vertical">
-
- <ListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:cacheColorHint="@android:color/transparent"
- android:clipToPadding="false"
- android:drawSelectorOnTop="false"
- android:headerDividersEnabled="false"
- android:scrollbarAlwaysDrawVerticalTrack="true" />
-
- </LinearLayout>
-
- </LinearLayout>
-
- </com.android.settings.widget.SetupWizardIllustration>
-
- <fragment
- android:id="@+id/navigation_bar"
- android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
- style="@style/setup_wizard_navbar_style" />
-
-</LinearLayout>
-
diff --git a/res/layout-sw600dp/setup_template.xml b/res/layout-sw600dp/setup_template.xml
deleted file mode 100644
index 297e9fb..0000000
--- a/res/layout-sw600dp/setup_template.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <com.android.settings.widget.SetupWizardIllustration
- android:id="@+id/setup_illustration"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:background="@drawable/setup_illustration_bg"
- android:foreground="@drawable/setup_illustration">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginTop="@dimen/setup_wizard_tablet_illustration_height"
- android:orientation="vertical"
- android:paddingStart="@dimen/setup_wizard_card_port_margin_sides"
- android:paddingEnd="@dimen/setup_wizard_card_port_margin_sides">
-
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/wifi_setup_title"
- style="@style/SetupCardTitle"/>
-
- <ScrollView
- android:id="@+id/bottom_scroll_view"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:background="@drawable/setup_wizard_card_bg"
- android:elevation="@dimen/setup_wizard_card_elevation"
- android:fillViewport="true">
-
- <FrameLayout
- android:id="@+id/setup_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"/>
-
- </ScrollView>
-
- </LinearLayout>
-
- </com.android.settings.widget.SetupWizardIllustration>
-
- <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
- android:id="@+id/navigation_bar"
- android:layout_width="match_parent"
- android:layout_height="56dip"
- style="@style/setup_wizard_navbar_style"/>
-
-</LinearLayout>
-
diff --git a/res/layout/preference_vpn.xml b/res/layout/preference_vpn.xml
new file mode 100644
index 0000000..95d3253
--- /dev/null
+++ b/res/layout/preference_vpn.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+ <View
+ android:id="@+id/divider_manage"
+ android:layout_width="2dip"
+ android:layout_height="match_parent"
+ android:layout_marginTop="5dip"
+ android:layout_marginBottom="5dip"
+ android:background="@android:drawable/divider_horizontal_dark" />
+ <ImageView
+ android:id="@+id/manage"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:src="@drawable/ic_sysbar_quicksettings"
+ android:contentDescription="@string/settings_label"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground" />
+</LinearLayout>
diff --git a/res/layout/setup_choose_lock_generic.xml b/res/layout/setup_choose_lock_generic.xml
new file mode 100644
index 0000000..3b3b5e7
--- /dev/null
+++ b/res/layout/setup_choose_lock_generic.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.setupwizardlib.SetupWizardListLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ settings:suwBackgroundTile="@drawable/setup_illustration_tile"
+ settings:suwHeaderText="@string/wifi_setup_wizard_title"
+ settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
+ settings:suwIllustrationImage="@drawable/setup_illustration_lock_screen" />
diff --git a/res/layout/setup_choose_lock_password.xml b/res/layout/setup_choose_lock_password.xml
index 59fa523..3ec5241 100644
--- a/res/layout/setup_choose_lock_password.xml
+++ b/res/layout/setup_choose_lock_password.xml
@@ -15,70 +15,82 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.setupwizardlib.SetupWizardLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical">
-
- <!-- header text ('Enter Pin') -->
- <TextView android:id="@+id/headerText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:lines="2"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
-
- <!-- Password entry field -->
- <EditText android:id="@+id/password_entry"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginStart="30dip"
- android:layout_marginEnd="30dip"
- android:gravity="center"
- android:inputType="textPassword"
- android:imeOptions="actionNext|flagNoExtractUi"
- android:textSize="24sp"
- style="@style/TextAppearance.PasswordEntry"/>
-
- <!-- Spacer between password entry and keyboard -->
- <View
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"/>
+ settings:suwBackgroundTile="@drawable/setup_illustration_tile"
+ settings:suwHeaderText="@string/wifi_setup_wizard_title"
+ settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
+ settings:suwIllustrationImage="@drawable/setup_illustration_lock_screen">
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:visibility="gone"
- style="@style/SecurityPreferenceButtonContainer">
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
- <!-- left : cancel -->
- <Button android:id="@+id/cancel_button"
- android:layout_width="0dip"
+ <!-- header text ('Enter Pin') -->
+ <TextView android:id="@+id/headerText"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/lockpassword_cancel_label"
- style="@style/SecurityPreferenceButton"/>
+ android:gravity="center"
+ android:lines="2"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
- <!-- right : continue -->
- <Button android:id="@+id/next_button"
- android:layout_width="0dip"
+ <!-- Password entry field -->
+ <EditText android:id="@+id/password_entry"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/lockpassword_continue_label"
- style="@style/SecurityPreferenceButton"/>
+ android:layout_gravity="center"
+ android:layout_marginStart="30dip"
+ android:layout_marginEnd="30dip"
+ android:gravity="center"
+ android:inputType="textPassword"
+ android:imeOptions="actionNext|flagNoExtractUi"
+ android:textSize="24sp"
+ style="@style/TextAppearance.PasswordEntry"/>
+
+ <!-- Spacer between password entry and keyboard -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone"
+ style="@style/SecurityPreferenceButtonContainer">
+
+ <!-- left : cancel -->
+ <Button android:id="@+id/cancel_button"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/lockpassword_cancel_label"
+ style="@style/SecurityPreferenceButton"/>
+
+ <!-- right : continue -->
+ <Button android:id="@+id/next_button"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/lockpassword_continue_label"
+ style="@style/SecurityPreferenceButton"/>
+
+ </LinearLayout>
+
+ <!-- Alphanumeric keyboard -->
+ <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#00000000"
+ android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
+ android:visibility="gone"/>
</LinearLayout>
- <!-- Alphanumeric keyboard -->
- <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#00000000"
- android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
- android:visibility="gone"/>
-
-</LinearLayout>
+</com.android.setupwizardlib.SetupWizardLayout>
diff --git a/res/layout/setup_choose_lock_pattern.xml b/res/layout/setup_choose_lock_pattern.xml
index a906826..00c7018 100644
--- a/res/layout/setup_choose_lock_pattern.xml
+++ b/res/layout/setup_choose_lock_pattern.xml
@@ -15,80 +15,91 @@
limitations under the License.
-->
-<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+<com.android.setupwizardlib.SetupWizardLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/topLayout"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical">
+ settings:suwBackgroundTile="@drawable/setup_illustration_tile"
+ settings:suwHeaderText="@string/wifi_setup_wizard_title"
+ settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
+ settings:suwIllustrationImage="@drawable/setup_illustration_lock_screen">
- <!-- takes up all space above button bar at bottom -->
- <LinearLayout
+ <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+ android:id="@+id/topLayout"
android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:gravity="center"
+ android:layout_height="match_parent"
android:orientation="vertical">
- <TextView android:id="@+id/headerText"
+ <!-- takes up all space above button bar at bottom -->
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center"
- android:minHeight="50dp"
- android:textSize="18sp"/>
+ android:orientation="vertical">
- <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
+ <TextView android:id="@+id/headerText"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:minHeight="50dip"
+ android:textSize="18sp"/>
+
+ <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="4"
+ android:background="@color/lock_pattern_background"/>
+
+ </LinearLayout>
+
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="4"
- android:background="@color/lock_pattern_background"/>
-
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:orientation="horizontal">
-
- <Button android:id="@+id/retryButton"
- android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/lockpattern_retry_button_text"/>
+ android:gravity="center_horizontal"
+ android:orientation="horizontal">
- <TextView android:id="@+id/footerText"
- android:layout_width="wrap_content"
+ <Button android:id="@+id/retryButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/lockpattern_retry_button_text"/>
+
+ <TextView android:id="@+id/footerText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="50dip"
+ android:textSize="14sp"/>
+
+ </LinearLayout>
+
+ <!-- Buttons are hidden during setup, and use the buttons in setup navigation bar instead -->
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="50dp"
- android:textSize="14sp"/>
+ android:orientation="horizontal"
+ android:visibility="gone"
+ style="@style/SecurityPreferenceButtonContainer">
- </LinearLayout>
+ <!-- left : cancel, or re-try -->
+ <Button android:id="@+id/footerLeftButton"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/lockpattern_tutorial_cancel_label"
+ style="@style/SecurityPreferenceButton"/>
- <!-- Buttons are hidden during setup, and use the buttons in setup navigation bar instead -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:visibility="gone"
- style="@style/SecurityPreferenceButtonContainer">
+ <!-- right : confirm or ok -->
+ <Button android:id="@+id/footerRightButton"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/lockpattern_tutorial_continue_label"
+ style="@style/SecurityPreferenceButton"/>
- <!-- left : cancel, or re-try -->
- <Button android:id="@+id/footerLeftButton"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/lockpattern_tutorial_cancel_label"
- style="@style/SecurityPreferenceButton"/>
+ </LinearLayout>
- <!-- right : confirm or ok -->
- <Button android:id="@+id/footerRightButton"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/lockpattern_tutorial_continue_label"
- style="@style/SecurityPreferenceButton"/>
+ </com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
- </LinearLayout>
-
-</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
+</com.android.setupwizardlib.SetupWizardLayout>
diff --git a/res/layout/setup_preference.xml b/res/layout/setup_preference.xml
deleted file mode 100644
index d07d4a3..0000000
--- a/res/layout/setup_preference.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, 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:id="@+id/fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <com.android.settings.widget.StickyHeaderListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:cacheColorHint="@android:color/transparent"
- android:clipToPadding="false"
- android:drawSelectorOnTop="false"
- android:headerDividersEnabled="false"
- android:scrollbarAlwaysDrawVerticalTrack="true" />
-
- <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
- android:id="@+id/navigation_bar"
- style="@style/setup_wizard_navbar_style" />
-
-</LinearLayout>
diff --git a/res/layout/setup_screen_lock_fingerprint_details.xml b/res/layout/setup_screen_lock_fingerprint_details.xml
index 988468f..06ca778 100644
--- a/res/layout/setup_screen_lock_fingerprint_details.xml
+++ b/res/layout/setup_screen_lock_fingerprint_details.xml
@@ -20,6 +20,6 @@
android:layout_height="wrap_content"
android:paddingTop="6dip"
android:paddingBottom="6dip"
- android:paddingStart="@dimen/setup_wizard_margin_sides"
- android:paddingEnd="@dimen/setup_wizard_margin_sides"
+ android:paddingStart="@dimen/suw_layout_margin_sides"
+ android:paddingEnd="@dimen/suw_layout_margin_sides"
android:text="@string/unlock_setup_wizard_fingerprint_details" />
diff --git a/res/layout/setup_template.xml b/res/layout/setup_template.xml
deleted file mode 100644
index 0623e25..0000000
--- a/res/layout/setup_template.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <ScrollView
- android:id="@+id/bottom_scroll_view"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:fillViewport="true">
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false">
-
- <com.android.settings.widget.SetupWizardIllustration
- android:id="@+id/setup_illustration"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:elevation="@dimen/setup_wizard_title_area_elevation"
- android:background="@drawable/setup_illustration_bg"
- android:foreground="@drawable/setup_illustration"
- android:tag="stickyContainer"
- settings:aspectRatio="2.22">
-
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:tag="sticky"
- android:text="@string/wifi_setup_title"
- style="@style/SetupTitle"/>
-
- </com.android.settings.widget.SetupWizardIllustration>
-
- <FrameLayout
- android:id="@+id/setup_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/setup_illustration"
- android:clipChildren="false"/>
-
- </RelativeLayout>
-
- </ScrollView>
-
- <fragment android:name="com.android.setupwizard.navigationbar.SetupWizardNavBar"
- android:id="@+id/navigation_bar"
- android:layout_width="match_parent"
- android:layout_height="56dip"
- style="@style/setup_wizard_navbar_style"/>
-
-</LinearLayout>
-
diff --git a/res/layout/setup_wifi_empty.xml b/res/layout/setup_wifi_empty.xml
index 52864f9..b1659d7 100644
--- a/res/layout/setup_wifi_empty.xml
+++ b/res/layout/setup_wifi_empty.xml
@@ -19,6 +19,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/setup_wizard_margin_top"
- android:paddingStart="@dimen/setup_wizard_margin_sides"
- android:paddingEnd="@dimen/setup_wizard_margin_sides"
+ android:paddingStart="@dimen/suw_layout_margin_sides"
+ android:paddingEnd="@dimen/suw_layout_margin_sides"
android:textAppearance="@style/TextAppearance.SetupWizardDescription" />
diff --git a/res/layout/setup_wifi_layout.xml b/res/layout/setup_wifi_layout.xml
new file mode 100644
index 0000000..62bb6a1
--- /dev/null
+++ b/res/layout/setup_wifi_layout.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.setupwizardlib.SetupWizardListLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ settings:suwBackgroundTile="@drawable/setup_illustration_tile"
+ settings:suwHeaderText="@string/wifi_setup_wizard_title"
+ settings:suwIllustrationHorizontalTile="@drawable/setup_illustration_horizontal_tile"
+ settings:suwIllustrationImage="@drawable/setup_illustration_wifi" />
diff --git a/res/layout/setup_wizard_header.xml b/res/layout/setup_wizard_header.xml
deleted file mode 100644
index b030ad1..0000000
--- a/res/layout/setup_wizard_header.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.settings.widget.SetupWizardIllustration
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
- android:id="@+id/setup_illustration"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/setup_illustration_bg"
- android:elevation="@dimen/setup_wizard_title_area_elevation"
- android:foreground="@drawable/setup_illustration"
- android:tag="stickyContainer"
- settings:aspectRatio="2.22">
-
- <TextView
- android:id="@+id/title"
- style="@style/SetupTitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:tag="sticky"
- android:text="@string/wifi_setup_wizard_title"/>
-
-</com.android.settings.widget.SetupWizardIllustration>
diff --git a/res/layout/vpn_dialog.xml b/res/layout/vpn_dialog.xml
index 034b6bf..d7e7f95 100644
--- a/res/layout/vpn_dialog.xml
+++ b/res/layout/vpn_dialog.xml
@@ -20,7 +20,7 @@
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:padding="3mm">
+ android:padding="8dp">
<LinearLayout android:id="@+id/editor"
android:layout_width="match_parent"
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 5f524af..79529e2 100755
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -45,9 +45,4 @@
<!-- Dashboard tile image margin start / end -->
<dimen name="dashboard_tile_image_margin_start">12dp</dimen>
- <dimen name="setup_wizard_card_title_padding_end">32dp</dimen>
- <dimen name="setup_wizard_card_title_padding_start">56dp</dimen>
- <dimen name="setup_wizard_card_title_padding_top">24dp</dimen>
- <dimen name="setup_wizard_card_title_padding_bottom">0dp</dimen>
-
</resources>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index d9521db..03809c4 100755
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -24,7 +24,6 @@
<dimen name="content_margin_left">16dip</dimen>
<dimen name="description_margin_top">26dip</dimen>
<dimen name="crypt_clock_size">120sp</dimen>
- <dimen name="setup_title_size">24sp</dimen>
<dimen name="appwidget_min_width">325dip</dimen>
<dimen name="appwidget_min_height">50dip</dimen>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8a58c5c..cd11c2c 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -38,28 +38,10 @@
<dimen name="crypt_clock_size">100sp</dimen>
- <dimen name="setup_margin_bottom">0dip</dimen>
- <dimen name="setup_title_size">24sp</dimen>
- <dimen name="setup_title_margin_bottom">16dp</dimen>
- <!-- This is the extra spacing required to make the leading exactly 32sp -->
- <dimen name="setup_title_line_spacing_extra">3.67sp</dimen>
- <dimen name="setup_title_padding_bottom">2dp</dimen>
- <dimen name="setup_title_padding_top">16dp</dimen>
<!-- Size of padding to give in the wifi list when there is no icon -->
<dimen name="setup_list_no_icon_padding">56dp</dimen>
<dimen name="setup_add_network_item_height">56dp</dimen>
- <dimen name="setup_wizard_card_corner_radius">2dp</dimen>
- <dimen name="setup_wizard_card_elevation">5dp</dimen>
- <dimen name="setup_wizard_tablet_illustration_height">256dp</dimen>
- <dimen name="setup_wizard_card_title_padding_end">0dp</dimen>
- <dimen name="setup_wizard_card_title_padding_start">0dp</dimen>
- <dimen name="setup_wizard_card_title_padding_top">16dp</dimen>
- <dimen name="setup_wizard_card_title_padding_bottom">28dp</dimen>
- <dimen name="setup_wizard_card_port_margin_sides">56dp</dimen>
- <dimen name="setup_wizard_card_land_margin_top">128dp</dimen>
- <dimen name="setup_wizard_margin_sides">40dp</dimen>
<dimen name="setup_wizard_margin_top">24dp</dimen>
- <dimen name="setup_wizard_title_area_elevation">3dp</dimen>
<dimen name="divider_height">3dip</dimen>
<dimen name="divider_margin_top">6dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 080cd3e..3c8e8ce 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5237,14 +5237,24 @@
<!-- Button label to cancel changing a VPN profile. [CHAR LIMIT=40] -->
<string name="vpn_cancel">Cancel</string>
+ <!-- Button label to finish editing a VPN profile. [CHAR LIMIT=40] -->
+ <string name="vpn_done">Dismiss</string>
<!-- Button label to save a VPN profile. [CHAR LIMIT=40] -->
<string name="vpn_save">Save</string>
<!-- Button label to connect to a VPN profile. [CHAR LIMIT=40] -->
<string name="vpn_connect">Connect</string>
<!-- Dialog title to edit a VPN profile. [CHAR LIMIT=40] -->
<string name="vpn_edit">Edit VPN profile</string>
+ <!-- Button label to forget a VPN profile. [CHAR LIMIT=40] -->
+ <string name="vpn_forget">Forget</string>
<!-- Dialog title to connect to a VPN profile. [CHAR LIMIT=40] -->
<string name="vpn_connect_to">Connect to <xliff:g id="profile" example="School">%s</xliff:g></string>
+ <!-- Dialog message body to disconnect from a VPN profile. -->
+ <string name="vpn_disconnect_confirm">Disconnect this VPN.</string>
+ <!-- Button label to disconnect from a VPN profile. [CHAR LIMIT=40] -->
+ <string name="vpn_disconnect">Disconnect</string>
+ <!-- Field label to show the version number for a VPN app. [CHAR LIMIT=40] -->
+ <string name="vpn_version">Version <xliff:g id="version" example="3.3.0">%s</xliff:g></string>
<!-- Preference title for VPN settings. [CHAR LIMIT=40] -->
<string name="vpn_title">VPN</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 22061e6..f9f4561 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -146,45 +146,6 @@
<item name="android:clickable">false</item>
</style>
- <!-- We'd like to have this as 16dip hight including paddingTop/paddingBottom to
- be consistent with ProgressBar -->
- <style name="TopDivider">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">@dimen/divider_height</item>
- <item name="android:background">?attr/setup_divider_color</item>
- <item name="android:focusable">false</item>
- <item name="android:clickable">false</item>
- <item name="android:layout_marginTop">@dimen/divider_margin_top</item>
- <item name="android:layout_marginBottom">@dimen/divider_margin_bottom</item>
- </style>
-
- <style name="SetupTitle">
- <item name="android:layout_marginBottom">@dimen/setup_title_margin_bottom</item>
- <item name="android:layout_marginEnd">@dimen/setup_wizard_margin_sides</item>
- <item name="android:layout_marginStart">@dimen/setup_wizard_margin_sides</item>
- <item name="android:clickable">false</item>
- <item name="android:fontFamily">sans-serif</item>
- <item name="android:lineSpacingExtra">@dimen/setup_title_line_spacing_extra</item>
- <item name="android:longClickable">false</item>
- <item name="android:textColor">@android:color/white</item>
- <item name="android:textSize">@dimen/setup_title_size</item>
- <item name="android:paddingBottom">@dimen/setup_title_padding_bottom</item>
- <item name="android:paddingTop">@dimen/setup_title_padding_top</item>
- </style>
-
- <!-- Alternate title style used for some tablet layouts -->
- <style name="SetupCardTitle">
- <item name="android:paddingBottom">@dimen/setup_wizard_card_title_padding_bottom</item>
- <item name="android:paddingEnd">@dimen/setup_wizard_card_title_padding_end</item>
- <item name="android:paddingStart">@dimen/setup_wizard_card_title_padding_start</item>
- <item name="android:paddingTop">@dimen/setup_wizard_card_title_padding_top</item>
- <item name="android:textAppearance">@style/TextAppearance.SetupWizardCardTitle</item>
- </style>
-
- <style name="TextAppearance.SetupWizardCardTitle" parent="@android:style/TextAppearance.Material.Display1">
- <item name="android:textColor">@android:color/white</item>
- </style>
-
<style name="TextAppearance.SetupWizardDescription" parent="@android:style/TextAppearance.Material.Subhead">
</style>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 2c900bb..a52ea04 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -31,8 +31,8 @@
<style name="SetupWizardTheme" parent="SuwThemeMaterial">
<item name="android:alertDialogTheme">@style/Theme.WifiDialog</item>
- <item name="android:listPreferredItemPaddingEnd">@dimen/setup_wizard_margin_sides</item>
- <item name="android:listPreferredItemPaddingStart">@dimen/setup_wizard_margin_sides</item>
+ <item name="android:listPreferredItemPaddingEnd">@dimen/suw_layout_margin_sides</item>
+ <item name="android:listPreferredItemPaddingStart">@dimen/suw_layout_margin_sides</item>
<item name="android:windowBackground">?android:attr/colorBackground</item>
<item name="@*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
<item name="ic_menu_add">@drawable/ic_menu_add_dark</item>
@@ -52,8 +52,8 @@
<style name="SetupWizardTheme.Light" parent="SuwThemeMaterial.Light">
<item name="android:alertDialogTheme">@style/Theme.Light.WifiDialog</item>
- <item name="android:listPreferredItemPaddingEnd">@dimen/setup_wizard_margin_sides</item>
- <item name="android:listPreferredItemPaddingStart">@dimen/setup_wizard_margin_sides</item>
+ <item name="android:listPreferredItemPaddingEnd">@dimen/suw_layout_margin_sides</item>
+ <item name="android:listPreferredItemPaddingStart">@dimen/suw_layout_margin_sides</item>
<item name="android:windowBackground">?android:attr/colorBackground</item>
<item name="@*android:preferencePanelStyle">@*android:style/PreferencePanel.Dialog</item>
<item name="ic_menu_add">@drawable/ic_menu_add_light</item>
diff --git a/src/com/android/settings/SetupChooseLockGeneric.java b/src/com/android/settings/SetupChooseLockGeneric.java
index f2da8cc..82157c0 100644
--- a/src/com/android/settings/SetupChooseLockGeneric.java
+++ b/src/com/android/settings/SetupChooseLockGeneric.java
@@ -16,8 +16,10 @@
package com.android.settings;
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
+import com.android.setupwizardlib.SetupWizardListLayout;
+import com.android.setupwizardlib.view.NavigationBar;
+import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
@@ -37,8 +39,7 @@
* Other changes should be done to ChooseLockGeneric class instead and let this class inherit
* those changes.
*/
-public class SetupChooseLockGeneric extends ChooseLockGeneric
- implements SetupWizardNavBar.NavigationBarListener {
+public class SetupChooseLockGeneric extends ChooseLockGeneric {
@Override
protected boolean isValidFragment(String fragmentName) {
@@ -56,33 +57,16 @@
super.onApplyThemeResource(theme, resid, first);
}
- @Override
- public void onNavigationBarCreated(SetupWizardNavBar bar) {
- SetupWizardUtils.setImmersiveMode(this);
- bar.getNextButton().setEnabled(false);
- }
-
- @Override
- public void onNavigateBack() {
- onBackPressed();
- }
-
- @Override
- public void onNavigateNext() {
- }
-
- public static class SetupChooseLockGenericFragment extends ChooseLockGenericFragment {
+ public static class SetupChooseLockGenericFragment extends ChooseLockGenericFragment
+ implements NavigationBar.NavigationBarListener {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- final View view = inflater.inflate(R.layout.setup_preference, container, false);
- ListView list = (ListView) view.findViewById(android.R.id.list);
- View title = view.findViewById(R.id.title);
- if (title == null) {
- final View header = inflater.inflate(R.layout.setup_wizard_header, list, false);
- list.addHeaderView(header, null, false);
- }
+ final SetupWizardListLayout layout = (SetupWizardListLayout) inflater.inflate(
+ R.layout.setup_choose_lock_generic, container, false);
+ layout.setHeaderText(getActivity().getTitle());
+ ListView list = layout.getListView();
final FingerprintManager fpm = (FingerprintManager)
getActivity().getSystemService(Context.FINGERPRINT_SERVICE);
if (fpm != null && fpm.isHardwareDetected()) {
@@ -90,15 +74,18 @@
R.layout.setup_screen_lock_fingerprint_details, list, false);
list.addFooterView(footer, null, false);
}
- return view;
+
+ final NavigationBar navigationBar = layout.getNavigationBar();
+ navigationBar.getNextButton().setEnabled(false);
+ navigationBar.setNavigationBarListener(this);
+
+ return layout;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
- SetupWizardUtils.setIllustration(getActivity(),
- R.drawable.setup_illustration_lock_screen);
- SetupWizardUtils.setHeaderText(getActivity(), getActivity().getTitle());
+ SetupWizardUtils.setImmersiveMode(getActivity());
}
@Override
@@ -192,5 +179,17 @@
SetupWizardUtils.copySetupExtras(getActivity().getIntent(), intent);
return intent;
}
+
+ @Override
+ public void onNavigateBack() {
+ Activity activity = getActivity();
+ if (activity != null) {
+ activity.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onNavigateNext() {
+ }
}
}
diff --git a/src/com/android/settings/SetupChooseLockPassword.java b/src/com/android/settings/SetupChooseLockPassword.java
index 461b67c..6f384a4 100644
--- a/src/com/android/settings/SetupChooseLockPassword.java
+++ b/src/com/android/settings/SetupChooseLockPassword.java
@@ -16,9 +16,11 @@
package com.android.settings;
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
+import com.android.setupwizardlib.SetupWizardLayout;
import com.android.setupwizardlib.util.SystemBarHelper;
+import com.android.setupwizardlib.view.NavigationBar;
+import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
@@ -35,8 +37,7 @@
* Other changes should be done to ChooseLockPassword class instead and let this class inherit
* those changes.
*/
-public class SetupChooseLockPassword extends ChooseLockPassword
- implements SetupWizardNavBar.NavigationBarListener {
+public class SetupChooseLockPassword extends ChooseLockPassword {
public static Intent createIntent(Context context, int quality,
int minLength, final int maxLength, boolean requirePasswordToDecrypt,
@@ -66,9 +67,6 @@
return intent;
}
- private SetupWizardNavBar mNavigationBar;
- private SetupChooseLockPasswordFragment mFragment;
-
@Override
protected boolean isValidFragment(String fragmentName) {
return SetupChooseLockPasswordFragment.class.getName().equals(fragmentName);
@@ -85,51 +83,22 @@
super.onApplyThemeResource(theme, resid, first);
}
- @Override
- public void onNavigationBarCreated(SetupWizardNavBar bar) {
- mNavigationBar = bar;
- SetupWizardUtils.setImmersiveMode(this);
- }
+ public static class SetupChooseLockPasswordFragment extends ChooseLockPasswordFragment
+ implements NavigationBar.NavigationBarListener {
- @Override
- public void onNavigateBack() {
- onBackPressed();
- }
-
- @Override
- public void onNavigateNext() {
- if (mFragment != null) {
- mFragment.handleNext();
- }
- }
-
- @Override
- public void onAttachFragment(Fragment fragment) {
- super.onAttachFragment(fragment);
- if (fragment instanceof SetupChooseLockPasswordFragment) {
- mFragment = (SetupChooseLockPasswordFragment) fragment;
- }
- }
-
- public static class SetupChooseLockPasswordFragment extends ChooseLockPasswordFragment {
+ private NavigationBar mNavigationBar;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- final View view = inflater.inflate(R.layout.setup_template, container, false);
- View scrollView = view.findViewById(R.id.bottom_scroll_view);
- SystemBarHelper.setImeInsetView(scrollView);
- ViewGroup setupContent = (ViewGroup) view.findViewById(R.id.setup_content);
- inflater.inflate(R.layout.setup_choose_lock_password, setupContent, true);
- return view;
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- SetupWizardUtils.setIllustration(getActivity(),
- R.drawable.setup_illustration_lock_screen);
- SetupWizardUtils.setHeaderText(getActivity(), getActivity().getTitle());
+ final SetupWizardLayout layout = (SetupWizardLayout) inflater.inflate(
+ R.layout.setup_choose_lock_password, container, false);
+ SystemBarHelper.setImeInsetView(layout.findViewById(R.id.suw_bottom_scroll_view));
+ mNavigationBar = layout.getNavigationBar();
+ mNavigationBar.setNavigationBarListener(this);
+ layout.setHeaderText(getActivity().getTitle());
+ SetupWizardUtils.setImmersiveMode(getActivity());
+ return layout;
}
@Override
@@ -141,14 +110,25 @@
@Override
protected void setNextEnabled(boolean enabled) {
- SetupChooseLockPassword activity = (SetupChooseLockPassword) getActivity();
- activity.mNavigationBar.getNextButton().setEnabled(enabled);
+ mNavigationBar.getNextButton().setEnabled(enabled);
}
@Override
protected void setNextText(int text) {
- SetupChooseLockPassword activity = (SetupChooseLockPassword) getActivity();
- activity.mNavigationBar.getNextButton().setText(text);
+ mNavigationBar.getNextButton().setText(text);
+ }
+
+ @Override
+ public void onNavigateBack() {
+ final Activity activity = getActivity();
+ if (activity != null) {
+ activity.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onNavigateNext() {
+ handleNext();
}
}
}
diff --git a/src/com/android/settings/SetupChooseLockPattern.java b/src/com/android/settings/SetupChooseLockPattern.java
index ca79744..2ea8d4a 100644
--- a/src/com/android/settings/SetupChooseLockPattern.java
+++ b/src/com/android/settings/SetupChooseLockPattern.java
@@ -16,8 +16,10 @@
package com.android.settings;
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
+import com.android.setupwizardlib.SetupWizardLayout;
+import com.android.setupwizardlib.view.NavigationBar;
+import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
@@ -35,8 +37,7 @@
* Other changes should be done to ChooseLockPattern class instead and let this class inherit
* those changes.
*/
-public class SetupChooseLockPattern extends ChooseLockPattern
- implements SetupWizardNavBar.NavigationBarListener {
+public class SetupChooseLockPattern extends ChooseLockPattern {
public static Intent createIntent(Context context, boolean requirePassword,
boolean confirmCredentials) {
@@ -58,9 +59,6 @@
return intent;
}
- private SetupWizardNavBar mNavigationBar;
- private SetupChooseLockPatternFragment mFragment;
-
@Override
protected boolean isValidFragment(String fragmentName) {
return SetupChooseLockPatternFragment.class.getName().equals(fragmentName);
@@ -77,43 +75,21 @@
super.onApplyThemeResource(theme, resid, first);
}
- @Override
- public void onNavigationBarCreated(SetupWizardNavBar bar) {
- mNavigationBar = bar;
- SetupWizardUtils.setImmersiveMode(this);
- }
+ public static class SetupChooseLockPatternFragment extends ChooseLockPatternFragment
+ implements NavigationBar.NavigationBarListener {
- @Override
- public void onNavigateBack() {
- onBackPressed();
- }
-
- @Override
- public void onNavigateNext() {
- if (mFragment != null) {
- mFragment.handleRightButton();
- }
- }
-
- @Override
- public void onAttachFragment(Fragment fragment) {
- super.onAttachFragment(fragment);
- if (fragment instanceof ChooseLockPatternFragment) {
- mFragment = (SetupChooseLockPatternFragment) fragment;
- }
- }
-
- public static class SetupChooseLockPatternFragment extends ChooseLockPatternFragment {
-
+ private NavigationBar mNavigationBar;
private Button mRetryButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- final View view = inflater.inflate(R.layout.setup_template, container, false);
- ViewGroup setupContent = (ViewGroup) view.findViewById(R.id.setup_content);
- inflater.inflate(R.layout.setup_choose_lock_pattern, setupContent, true);
- return view;
+ final SetupWizardLayout layout = (SetupWizardLayout) inflater.inflate(
+ R.layout.setup_choose_lock_pattern, container, false);
+ mNavigationBar = layout.getNavigationBar();
+ mNavigationBar.setNavigationBarListener(this);
+ layout.setHeaderText(getActivity().getTitle());
+ return layout;
}
@Override
@@ -121,9 +97,7 @@
mRetryButton = (Button) view.findViewById(R.id.retryButton);
mRetryButton.setOnClickListener(this);
super.onViewCreated(view, savedInstanceState);
- SetupWizardUtils.setIllustration(getActivity(),
- R.drawable.setup_illustration_lock_screen);
- SetupWizardUtils.setHeaderText(getActivity(), getActivity().getTitle());
+ SetupWizardUtils.setImmersiveMode(getActivity());
}
@Override
@@ -144,14 +118,12 @@
@Override
protected void setRightButtonEnabled(boolean enabled) {
- SetupChooseLockPattern activity = (SetupChooseLockPattern) getActivity();
- activity.mNavigationBar.getNextButton().setEnabled(enabled);
+ mNavigationBar.getNextButton().setEnabled(enabled);
}
@Override
protected void setRightButtonText(int text) {
- SetupChooseLockPattern activity = (SetupChooseLockPattern) getActivity();
- activity.mNavigationBar.getNextButton().setText(text);
+ mNavigationBar.getNextButton().setText(text);
}
@Override
@@ -160,5 +132,18 @@
// Only enable the button for retry
mRetryButton.setEnabled(stage == Stage.FirstChoiceValid);
}
+
+ @Override
+ public void onNavigateBack() {
+ final Activity activity = getActivity();
+ if (activity != null) {
+ activity.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onNavigateNext() {
+ handleRightButton();
+ }
}
}
diff --git a/src/com/android/settings/SetupEncryptionInterstitial.java b/src/com/android/settings/SetupEncryptionInterstitial.java
index 8d5f613..61ff81c 100644
--- a/src/com/android/settings/SetupEncryptionInterstitial.java
+++ b/src/com/android/settings/SetupEncryptionInterstitial.java
@@ -16,8 +16,10 @@
package com.android.settings;
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
+import com.android.setupwizardlib.SetupWizardLayout;
+import com.android.setupwizardlib.view.NavigationBar;
+import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
@@ -34,8 +36,7 @@
* Setup Wizard. Other changes should be done to EncryptionInterstitial class instead and let this
* class inherit those changes.
*/
-public class SetupEncryptionInterstitial extends EncryptionInterstitial
- implements SetupWizardNavBar.NavigationBarListener{
+public class SetupEncryptionInterstitial extends EncryptionInterstitial {
public static Intent createStartIntent(Context ctx, int quality,
boolean requirePasswordDefault) {
@@ -66,32 +67,29 @@
super.onApplyThemeResource(theme, resid, first);
}
- @Override
- public void onNavigationBarCreated(SetupWizardNavBar bar) {
- SetupWizardUtils.setImmersiveMode(this);
- }
-
- @Override
- public void onNavigateBack() {
- onBackPressed();
- }
-
- @Override
- public void onNavigateNext() {
- setResult(RESULT_OK, getResultIntentData());
- finish();
- }
-
- public static class SetupEncryptionInterstitialFragment extends EncryptionInterstitialFragment {
+ public static class SetupEncryptionInterstitialFragment extends EncryptionInterstitialFragment
+ implements NavigationBar.NavigationBarListener {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.setup_template, container, false);
- ViewGroup setupContent = (ViewGroup) view.findViewById(R.id.setup_content);
- View content = super.onCreateView(inflater, setupContent, savedInstanceState);
- setupContent.addView(content);
- return view;
+ final SetupWizardLayout layout = new SetupWizardLayout(inflater.getContext());
+ layout.setIllustration(R.drawable.setup_illustration_lock_screen,
+ R.drawable.setup_illustration_horizontal_tile);
+ layout.setBackgroundTile(R.drawable.setup_illustration_tile);
+ final int headerTextResource = getHeaderTextResource();
+ layout.setHeaderText(headerTextResource);
+
+ View content = super.onCreateView(inflater, layout, savedInstanceState);
+ layout.addView(content);
+ layout.getNavigationBar().setNavigationBarListener(this);
+
+ Activity activity = getActivity();
+ if (activity != null) {
+ activity.setTitle(headerTextResource);
+ SetupWizardUtils.setImmersiveMode(activity);
+ }
+ return layout;
}
private int getHeaderTextResource() {
@@ -108,13 +106,21 @@
}
@Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- SetupWizardUtils.setIllustration(getActivity(),
- R.drawable.setup_illustration_lock_screen);
- final int title = getHeaderTextResource();
- getActivity().setTitle(title);
- SetupWizardUtils.setHeaderText(getActivity(), title);
+ public void onNavigateBack() {
+ final Activity activity = getActivity();
+ if (activity != null) {
+ activity.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onNavigateNext() {
+ final SetupEncryptionInterstitial activity =
+ (SetupEncryptionInterstitial) getActivity();
+ if (activity != null) {
+ activity.setResult(RESULT_OK, activity.getResultIntentData());
+ finish();
+ }
}
}
}
diff --git a/src/com/android/settings/SetupRedactionInterstitial.java b/src/com/android/settings/SetupRedactionInterstitial.java
index 266a0490..c6437e0 100644
--- a/src/com/android/settings/SetupRedactionInterstitial.java
+++ b/src/com/android/settings/SetupRedactionInterstitial.java
@@ -17,8 +17,10 @@
package com.android.settings;
import com.android.settings.notification.RedactionInterstitial;
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
+import com.android.setupwizardlib.SetupWizardLayout;
+import com.android.setupwizardlib.view.NavigationBar;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -34,8 +36,7 @@
* Wizard. Other changes should be done to RedactionInterstitial class instead and let this class
* inherit those changes.
*/
-public class SetupRedactionInterstitial extends RedactionInterstitial
- implements SetupWizardNavBar.NavigationBarListener{
+public class SetupRedactionInterstitial extends RedactionInterstitial {
public static Intent createStartIntent(Context ctx) {
Intent startIntent = RedactionInterstitial.createStartIntent(ctx);
@@ -64,41 +65,43 @@
super.onApplyThemeResource(theme, resid, first);
}
- @Override
- public void onNavigationBarCreated(SetupWizardNavBar bar) {
- SetupWizardUtils.setImmersiveMode(this);
- bar.getBackButton().setEnabled(false);
- }
-
- @Override
- public void onNavigateBack() {
- onBackPressed();
- }
-
- @Override
- public void onNavigateNext() {
- setResult(RESULT_OK, getResultIntentData());
- finish();
- }
-
- public static class SetupEncryptionInterstitialFragment extends RedactionInterstitialFragment {
+ public static class SetupEncryptionInterstitialFragment extends RedactionInterstitialFragment
+ implements NavigationBar.NavigationBarListener {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.setup_template, container, false);
- ViewGroup setupContent = (ViewGroup) view.findViewById(R.id.setup_content);
- View content = super.onCreateView(inflater, setupContent, savedInstanceState);
- setupContent.addView(content);
- return view;
+ final SetupWizardLayout layout = new SetupWizardLayout(inflater.getContext());
+ layout.setIllustration(R.drawable.setup_illustration_lock_screen,
+ R.drawable.setup_illustration_horizontal_tile);
+ layout.setBackgroundTile(R.drawable.setup_illustration_tile);
+ layout.setHeaderText(R.string.notification_section_header);
+
+ View content = super.onCreateView(inflater, layout, savedInstanceState);
+ layout.addView(content);
+
+ final NavigationBar navigationBar = layout.getNavigationBar();
+ navigationBar.setNavigationBarListener(this);
+ navigationBar.getBackButton().setEnabled(false);
+ SetupWizardUtils.setImmersiveMode(getActivity());
+ return layout;
}
@Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- SetupWizardUtils.setIllustration(getActivity(),
- R.drawable.setup_illustration_lock_screen);
- SetupWizardUtils.setHeaderText(getActivity(), R.string.notification_section_header);
+ public void onNavigateBack() {
+ final Activity activity = getActivity();
+ if (activity != null) {
+ activity.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onNavigateNext() {
+ final SetupRedactionInterstitial activity = (SetupRedactionInterstitial) getActivity();
+ if (activity != null) {
+ activity.setResult(RESULT_OK, activity.getResultIntentData());
+ finish();
+ }
}
}
}
diff --git a/src/com/android/settings/SetupWizardUtils.java b/src/com/android/settings/SetupWizardUtils.java
index 0f93c67..61043ca 100644
--- a/src/com/android/settings/SetupWizardUtils.java
+++ b/src/com/android/settings/SetupWizardUtils.java
@@ -16,17 +16,12 @@
package com.android.settings;
-import com.android.settings.widget.SetupWizardIllustration;
import com.android.setupwizardlib.util.SystemBarHelper;
import com.android.setupwizardlib.util.WizardManagerHelper;
import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.widget.TextView;
public class SetupWizardUtils {
private static final String TAG = "SetupWizardUtils";
@@ -62,42 +57,10 @@
SystemBarHelper.hideSystemBars(dialog);
}
- public static TextView getHeader(Activity activity) {
- return (TextView) activity.findViewById(R.id.title);
- }
-
- public static void setHeaderText(Activity activity, int text) {
- getHeader(activity).setText(text);
- }
-
- public static void setHeaderText(Activity activity, CharSequence text) {
- getHeader(activity).setText(text);
- }
-
public static void copySetupExtras(Intent fromIntent, Intent toIntent) {
toIntent.putExtra(WizardManagerHelper.EXTRA_THEME,
fromIntent.getStringExtra(WizardManagerHelper.EXTRA_THEME));
toIntent.putExtra(WizardManagerHelper.EXTRA_USE_IMMERSIVE_MODE,
fromIntent.getBooleanExtra(WizardManagerHelper.EXTRA_USE_IMMERSIVE_MODE, false));
}
-
- public static void setIllustration(Activity activity, int asset) {
- SetupWizardIllustration illustration =
- (SetupWizardIllustration) activity.findViewById(R.id.setup_illustration);
- if (illustration != null) {
- Drawable drawable = activity.getDrawable(R.drawable.setup_illustration);
- Drawable newIllustration = activity.getDrawable(asset);
- if (drawable instanceof LayerDrawable) {
- LayerDrawable layers = (LayerDrawable) drawable;
- Drawable oldIllustration = layers.findDrawableByLayerId(R.id.illustration_image);
- if (newIllustration instanceof BitmapDrawable
- && oldIllustration instanceof BitmapDrawable) {
- final int gravity = ((BitmapDrawable) oldIllustration).getGravity();
- ((BitmapDrawable) newIllustration).setGravity(gravity);
- }
- layers.setDrawableByLayerId(R.id.illustration_image, newIllustration);
- illustration.setForeground(layers);
- }
- }
- }
}
diff --git a/src/com/android/settings/sim/SimSettings.java b/src/com/android/settings/sim/SimSettings.java
index 184e882..efc71d8 100644
--- a/src/com/android/settings/sim/SimSettings.java
+++ b/src/com/android/settings/sim/SimSettings.java
@@ -29,6 +29,7 @@
import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.provider.SearchIndexableResource;
+import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -52,6 +53,8 @@
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settings.R;
+import android.os.SystemProperties;
+import com.android.internal.telephony.TelephonyProperties;
import java.util.ArrayList;
import java.util.List;
@@ -245,11 +248,39 @@
// FIXME: b/18385348, needs to handle null from getActiveSubscriptionInfoList
if (DBG) log("[onResme] mSubInfoList=" + mSubInfoList);
+ final TelephonyManager tm =
+ (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
+ tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+
updateAvailableSubInfos();
updateAllOptions();
}
@Override
+ public void onPause() {
+ super.onPause();
+ final TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+ tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ }
+
+ private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ // Disable Sim selection for Data when voice call is going on as changing the default data
+ // sim causes a modem reset currently and call gets disconnected
+ // ToDo : Add subtext on disabled preference to let user know that default data sim cannot
+ // be changed while call is going on
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (DBG) log("PhoneStateListener.onCallStateChanged: state=" + state);
+ final Preference pref = findPreference(KEY_CELLULAR_DATA);
+ if (pref != null) {
+ final boolean ecbMode = SystemProperties.getBoolean(
+ TelephonyProperties.PROPERTY_INECM_MODE, false);
+ pref.setEnabled((state == TelephonyManager.CALL_STATE_IDLE) && !ecbMode);
+ }
+ }
+ };
+
+ @Override
public boolean onPreferenceTreeClick(final PreferenceScreen preferenceScreen,
final Preference preference) {
final Context context = getActivity();
diff --git a/src/com/android/settings/vpn2/AppDialog.java b/src/com/android/settings/vpn2/AppDialog.java
new file mode 100644
index 0000000..2145297
--- /dev/null
+++ b/src/com/android/settings/vpn2/AppDialog.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.vpn2;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.net.VpnConfig;
+import com.android.settings.R;
+
+/**
+ * UI for managing the connection controlled by an app.
+ *
+ * Among the actions available are (depending on context):
+ * <ul>
+ * <li><strong>Forget</strong>: revoke the managing app's VPN permission</li>
+ * <li><strong>Dismiss</strong>: continue to use the VPN</li>
+ * </ul>
+ *
+ * {@see ConfigDialog}
+ */
+class AppDialog extends AlertDialog implements DialogInterface.OnClickListener {
+ private final PackageInfo mPkgInfo;
+ private final Listener mListener;
+ private final boolean mConnected;
+
+ AppDialog(Context context, Listener listener, PackageInfo pkgInfo, boolean connected) {
+ super(context);
+
+ mListener = listener;
+ mPkgInfo = pkgInfo;
+ mConnected = connected;
+ }
+
+ public final PackageInfo getPackageInfo() {
+ return mPkgInfo;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ CharSequence vpnName;
+ try {
+ vpnName = VpnConfig.getVpnLabel(getContext(), mPkgInfo.packageName);
+ } catch (PackageManager.NameNotFoundException ex) {
+ vpnName = mPkgInfo.packageName;
+ }
+
+ setTitle(vpnName);
+ setMessage(getContext().getString(R.string.vpn_version, mPkgInfo.versionName));
+
+ createButtons();
+ super.onCreate(savedState);
+ }
+
+ protected void createButtons() {
+ Context context = getContext();
+
+ if (mConnected) {
+ // Forget the network
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ context.getString(R.string.vpn_forget), this);
+ }
+
+ // Dismiss
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(R.string.vpn_done), this);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_NEGATIVE) {
+ mListener.onForget(dialog);
+ }
+ dismiss();
+ }
+
+ public interface Listener {
+ public void onForget(DialogInterface dialog);
+ }
+}
diff --git a/src/com/android/settings/vpn2/AppDialogFragment.java b/src/com/android/settings/vpn2/AppDialogFragment.java
new file mode 100644
index 0000000..fc8d9e3
--- /dev/null
+++ b/src/com/android/settings/vpn2/AppDialogFragment.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.vpn2;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.IConnectivityManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.net.VpnConfig;
+import com.android.settings.R;
+
+/**
+ * Fragment wrapper around an {@link AppDialog}.
+ */
+public class AppDialogFragment extends DialogFragment implements AppDialog.Listener {
+ private static final String TAG_APP_DIALOG = "vpnappdialog";
+ private static final String TAG = "AppDialogFragment";
+
+ private static final String ARG_MANAGING = "managing";
+ private static final String ARG_PACKAGE = "package";
+ private static final String ARG_CONNECTED = "connected";
+
+ private final IConnectivityManager mService = IConnectivityManager.Stub.asInterface(
+ ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+
+ public static void show(VpnSettings parent, PackageInfo pkgInfo, boolean managing,
+ boolean connected) {
+ if (!parent.isAdded()) return;
+
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_PACKAGE, pkgInfo);
+ args.putBoolean(ARG_MANAGING, managing);
+ args.putBoolean(ARG_CONNECTED, connected);
+
+ final AppDialogFragment frag = new AppDialogFragment();
+ frag.setArguments(args);
+ frag.setTargetFragment(parent, 0);
+ frag.show(parent.getFragmentManager(), TAG_APP_DIALOG);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Bundle args = getArguments();
+ PackageInfo pkgInfo = (PackageInfo) args.getParcelable(ARG_PACKAGE);
+ boolean managing = args.getBoolean(ARG_MANAGING);
+ boolean connected = args.getBoolean(ARG_CONNECTED);
+
+ if (managing) {
+ return new AppDialog(getActivity(), this, pkgInfo, connected);
+ } else {
+ // Build an AlertDialog with an option to disconnect.
+
+ CharSequence vpnName;
+ try {
+ vpnName = VpnConfig.getVpnLabel(getActivity(), pkgInfo.packageName);
+ } catch (PackageManager.NameNotFoundException ex) {
+ vpnName = pkgInfo.packageName;
+ }
+
+ AlertDialog.Builder dlog = new AlertDialog.Builder(getActivity())
+ .setTitle(vpnName)
+ .setMessage(getActivity().getString(R.string.vpn_disconnect_confirm))
+ .setNegativeButton(getActivity().getString(R.string.vpn_cancel), null);
+
+ if (connected) {
+ dlog.setPositiveButton(getActivity().getString(R.string.vpn_disconnect),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ onDisconnect(dialog);
+ }
+ });
+ }
+ return dlog.create();
+ }
+ }
+
+ @Override
+ public void dismiss() {
+ ((VpnSettings) getTargetFragment()).update();
+ super.dismiss();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ dismiss();
+ super.onCancel(dialog);
+ }
+
+ @Override
+ public void onForget(final DialogInterface dialog) {
+ PackageInfo pkgInfo = (PackageInfo) getArguments().getParcelable(ARG_PACKAGE);
+ final String pkg = pkgInfo.packageName;
+ try {
+ VpnConfig vpnConfig = mService.getVpnConfig();
+ if (vpnConfig != null && pkg.equals(vpnConfig.user) && !vpnConfig.legacy) {
+ mService.setVpnPackageAuthorization(false);
+ onDisconnect(dialog);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to forget authorization for " + pkg, e);
+ }
+ }
+
+ private void onDisconnect(final DialogInterface dialog) {
+ PackageInfo pkgInfo = (PackageInfo) getArguments().getParcelable(ARG_PACKAGE);
+ try {
+ mService.prepareVpn(pkgInfo.packageName, VpnConfig.LEGACY_VPN);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to disconnect package " + pkgInfo.packageName, e);
+ }
+ }
+}
diff --git a/src/com/android/settings/vpn2/AppPreference.java b/src/com/android/settings/vpn2/AppPreference.java
new file mode 100644
index 0000000..1935dd8
--- /dev/null
+++ b/src/com/android/settings/vpn2/AppPreference.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.vpn2;
+
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.preference.Preference;
+import android.view.View.OnClickListener;
+
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.settings.R;
+
+/**
+ * {@link android.preference.Preference} containing information about a VPN
+ * application. Tracks the package name and connection state.
+ */
+public class AppPreference extends ManageablePreference {
+ public static final int STATE_CONNECTED = LegacyVpnInfo.STATE_CONNECTED;
+ public static final int STATE_DISCONNECTED = LegacyVpnInfo.STATE_DISCONNECTED;
+
+ private int mState = STATE_DISCONNECTED;
+ private String mPackageName;
+ private String mName;
+ private int mUid;
+
+ public AppPreference(Context context, OnClickListener onManage, final String packageName,
+ int uid) {
+ super(context, null /* attrs */, onManage);
+ mPackageName = packageName;
+ mUid = uid;
+ update();
+ }
+
+ public PackageInfo getPackageInfo() {
+ UserHandle user = new UserHandle(UserHandle.getUserId(mUid));
+ try {
+ IPackageManager ipm = AppGlobals.getPackageManager();
+ return ipm.getPackageInfo(mPackageName, 0 /* flags */, user.getIdentifier());
+ } catch (RemoteException rme) {
+ return null;
+ }
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ public void setState(int state) {
+ mState = state;
+ update();
+ }
+
+ private void update() {
+ final String[] states = getContext().getResources().getStringArray(R.array.vpn_states);
+ setSummary(mState != STATE_DISCONNECTED ? states[mState] : "");
+
+ mName = mPackageName;
+ Drawable icon = null;
+ try {
+ // Make all calls to the package manager as the appropriate user.
+ int userId = UserHandle.getUserId(mUid);
+ Context userContext = getContext().createPackageContextAsUser(
+ getContext().getPackageName(), 0 /* flags */, new UserHandle(userId));
+ PackageManager pm = userContext.getPackageManager();
+
+ // Fetch icon and VPN label
+ PackageInfo pkgInfo = pm.getPackageInfo(mPackageName, 0 /* flags */);
+ if (pkgInfo != null) {
+ icon = pkgInfo.applicationInfo.loadIcon(pm);
+ mName = VpnConfig.getVpnLabel(userContext, mPackageName).toString();
+ }
+ } catch (PackageManager.NameNotFoundException nnfe) {
+ // Failed - use default app label and icon as fallback
+ }
+ if (icon == null) {
+ icon = getContext().getPackageManager().getDefaultActivityIcon();
+ }
+ setTitle(mName);
+ setIcon(icon);
+
+ notifyHierarchyChanged();
+ }
+
+ public int compareTo(Preference preference) {
+ if (preference instanceof AppPreference) {
+ AppPreference another = (AppPreference) preference;
+ int result;
+ if ((result = another.mState - mState) == 0 &&
+ (result = mName.compareToIgnoreCase(another.mName)) == 0 &&
+ (result = mPackageName.compareTo(another.mPackageName)) == 0) {
+ result = mUid - another.mUid;
+ }
+ return result;
+ } else if (preference instanceof ConfigPreference) {
+ // Use comparator from ConfigPreference
+ ConfigPreference another = (ConfigPreference) preference;
+ return -another.compareTo(this);
+ } else {
+ return super.compareTo(preference);
+ }
+ }
+}
+
diff --git a/src/com/android/settings/vpn2/VpnDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java
similarity index 95%
rename from src/com/android/settings/vpn2/VpnDialog.java
rename to src/com/android/settings/vpn2/ConfigDialog.java
index 2f95bce..57f43f4 100644
--- a/src/com/android/settings/vpn2/VpnDialog.java
+++ b/src/com/android/settings/vpn2/ConfigDialog.java
@@ -16,9 +16,6 @@
package com.android.settings.vpn2;
-import com.android.internal.net.VpnProfile;
-import com.android.settings.R;
-
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -35,15 +32,26 @@
import android.widget.Spinner;
import android.widget.TextView;
+import com.android.internal.net.VpnProfile;
+import com.android.settings.R;
+
import java.net.InetAddress;
-class VpnDialog extends AlertDialog implements TextWatcher,
+/**
+ * Dialog showing information about a VPN configuration. The dialog
+ * can be launched to either edit or prompt for credentials to connect
+ * to a user-added VPN.
+ *
+ * {@see AppDialog}
+ */
+class ConfigDialog extends AlertDialog implements TextWatcher,
View.OnClickListener, AdapterView.OnItemSelectedListener {
private final KeyStore mKeyStore = KeyStore.getInstance();
private final DialogInterface.OnClickListener mListener;
private final VpnProfile mProfile;
private boolean mEditing;
+ private boolean mExists;
private View mView;
@@ -64,19 +72,20 @@
private Spinner mIpsecServerCert;
private CheckBox mSaveLogin;
- VpnDialog(Context context, DialogInterface.OnClickListener listener,
- VpnProfile profile, boolean editing) {
+ ConfigDialog(Context context, DialogInterface.OnClickListener listener,
+ VpnProfile profile, boolean editing, boolean exists) {
super(context);
+
mListener = listener;
mProfile = profile;
mEditing = editing;
+ mExists = exists;
}
@Override
protected void onCreate(Bundle savedState) {
mView = getLayoutInflater().inflate(R.layout.vpn_dialog, null);
setView(mView);
- setInverseBackgroundForced(true);
Context context = getContext();
@@ -154,6 +163,12 @@
onClick(showOptions);
}
+ // Create a button to forget the profile if it has already been saved..
+ if (mExists) {
+ setButton(DialogInterface.BUTTON_NEUTRAL,
+ context.getString(R.string.vpn_forget), mListener);
+ }
+
// Create a button to save the profile.
setButton(DialogInterface.BUTTON_POSITIVE,
context.getString(R.string.vpn_save), mListener);
@@ -173,7 +188,7 @@
context.getString(R.string.vpn_cancel), mListener);
// Let AlertDialog create everything.
- super.onCreate(null);
+ super.onCreate(savedState);
// Disable the action button if necessary.
getButton(DialogInterface.BUTTON_POSITIVE)
diff --git a/src/com/android/settings/vpn2/ConfigDialogFragment.java b/src/com/android/settings/vpn2/ConfigDialogFragment.java
new file mode 100644
index 0000000..42e1614
--- /dev/null
+++ b/src/com/android/settings/vpn2/ConfigDialogFragment.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.vpn2;
+
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.IConnectivityManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.settings.R;
+
+/**
+ * Fragment wrapper around a {@link ConfigDialog}.
+ */
+public class ConfigDialogFragment extends DialogFragment implements
+ DialogInterface.OnClickListener {
+ private static final String TAG_CONFIG_DIALOG = "vpnconfigdialog";
+ private static final String TAG = "ConfigDialogFragment";
+
+ private static final String ARG_PROFILE = "profile";
+ private static final String ARG_EDITING = "editing";
+ private static final String ARG_EXISTS = "exists";
+
+ private final IConnectivityManager mService = IConnectivityManager.Stub.asInterface(
+ ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+
+ private boolean mUnlocking = false;
+
+ public static void show(VpnSettings parent, VpnProfile profile, boolean edit, boolean exists) {
+ if (!parent.isAdded()) return;
+
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_PROFILE, profile);
+ args.putBoolean(ARG_EDITING, edit);
+ args.putBoolean(ARG_EXISTS, exists);
+
+ final ConfigDialogFragment frag = new ConfigDialogFragment();
+ frag.setArguments(args);
+ frag.setTargetFragment(parent, 0);
+ frag.show(parent.getFragmentManager(), TAG_CONFIG_DIALOG);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // Check KeyStore here, so others do not need to deal with it.
+ if (!KeyStore.getInstance().isUnlocked()) {
+ if (!mUnlocking) {
+ // Let us unlock KeyStore. See you later!
+ Credentials.getInstance().unlock(getActivity());
+ } else {
+ // We already tried, but it is still not working!
+ dismiss();
+ }
+ mUnlocking = !mUnlocking;
+ return;
+ }
+
+ // Now KeyStore is always unlocked. Reset the flag.
+ mUnlocking = false;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Bundle args = getArguments();
+ VpnProfile profile = (VpnProfile) args.getParcelable(ARG_PROFILE);
+ boolean editing = args.getBoolean(ARG_EDITING);
+ boolean exists = args.getBoolean(ARG_EXISTS);
+
+ return new ConfigDialog(getActivity(), this, profile, editing, exists);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialogInterface, int button) {
+ ConfigDialog dialog = (ConfigDialog) getDialog();
+ VpnProfile profile = dialog.getProfile();
+
+ if (button == DialogInterface.BUTTON_POSITIVE) {
+ // Update KeyStore entry
+ KeyStore.getInstance().put(Credentials.VPN + profile.key, profile.encode(),
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+
+ // Flush out old version of profile
+ disconnect(profile);
+
+ // If we are not editing, connect!
+ if (!dialog.isEditing()) {
+ try {
+ connect(profile);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to connect", e);
+ }
+ }
+ } else if (button == DialogInterface.BUTTON_NEUTRAL) {
+ // Disable profile if connected
+ disconnect(profile);
+
+ // Delete from KeyStore
+ KeyStore.getInstance().delete(Credentials.VPN + profile.key, KeyStore.UID_SELF);
+ }
+ dismiss();
+ }
+
+ @Override
+ public void dismiss() {
+ ((VpnSettings) getTargetFragment()).update();
+ super.dismiss();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ dismiss();
+ super.onCancel(dialog);
+ }
+
+ private void connect(VpnProfile profile) throws RemoteException {
+ try {
+ mService.startLegacyVpn(profile);
+ } catch (IllegalStateException e) {
+ Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void disconnect(VpnProfile profile) {
+ try {
+ LegacyVpnInfo connected = mService.getLegacyVpnInfo();
+ if (connected != null && profile.key.equals(connected.key)) {
+ mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to disconnect", e);
+ }
+ }
+}
diff --git a/src/com/android/settings/vpn2/ConfigPreference.java b/src/com/android/settings/vpn2/ConfigPreference.java
new file mode 100644
index 0000000..4e6e16f
--- /dev/null
+++ b/src/com/android/settings/vpn2/ConfigPreference.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.vpn2;
+
+import android.content.Context;
+import android.preference.Preference;
+import android.view.View.OnClickListener;
+
+import static com.android.internal.net.LegacyVpnInfo.STATE_CONNECTED;
+
+import com.android.internal.net.VpnProfile;
+import com.android.settings.R;
+
+/**
+ * {@link android.preference.Preference} referencing a VPN
+ * configuration. Tracks the underlying profile and its connection
+ * state.
+ */
+public class ConfigPreference extends ManageablePreference {
+ private VpnProfile mProfile;
+ private int mState = -1;
+
+ ConfigPreference(Context context, OnClickListener onManage, VpnProfile profile) {
+ super(context, null /* attrs */, onManage);
+ setProfile(profile);
+ }
+
+ public VpnProfile getProfile() {
+ return mProfile;
+ }
+
+ public void setProfile(VpnProfile profile) {
+ mProfile = profile;
+ update();
+ }
+
+ public void setState(int state) {
+ mState = state;
+ update();
+ }
+
+ private void update() {
+ if (mState < 0) {
+ setSummary("");
+ } else {
+ String[] states = getContext().getResources()
+ .getStringArray(R.array.vpn_states);
+ setSummary(states[mState]);
+ }
+ setIcon(R.mipmap.ic_launcher_settings);
+ setTitle(mProfile.name);
+ notifyHierarchyChanged();
+ }
+
+ @Override
+ public int compareTo(Preference preference) {
+ if (preference instanceof ConfigPreference) {
+ ConfigPreference another = (ConfigPreference) preference;
+ int result;
+ 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;
+ } else if (preference instanceof AppPreference) {
+ // Try to sort connected VPNs first
+ AppPreference another = (AppPreference) preference;
+ if (mState != STATE_CONNECTED && another.getState() == AppPreference.STATE_CONNECTED) {
+ return 1;
+ }
+ // Show configured VPNs before app VPNs
+ return -1;
+ } else {
+ return super.compareTo(preference);
+ }
+ }
+}
+
diff --git a/src/com/android/settings/vpn2/LockdownConfigFragment.java b/src/com/android/settings/vpn2/LockdownConfigFragment.java
new file mode 100644
index 0000000..f36cb46
--- /dev/null
+++ b/src/com/android/settings/vpn2/LockdownConfigFragment.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.vpn2;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.android.internal.net.VpnProfile;
+import com.android.settings.R;
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Dialog to configure always-on VPN.
+ */
+public class LockdownConfigFragment extends DialogFragment {
+ private List<VpnProfile> mProfiles;
+ private List<CharSequence> mTitles;
+ private int mCurrentIndex;
+
+ private static final String TAG_LOCKDOWN = "lockdown";
+
+ private static class TitleAdapter extends ArrayAdapter<CharSequence> {
+ public TitleAdapter(Context context, List<CharSequence> objects) {
+ super(context, com.android.internal.R.layout.select_dialog_singlechoice_material,
+ android.R.id.text1, objects);
+ }
+ }
+
+ public static void show(VpnSettings parent) {
+ if (!parent.isAdded()) return;
+
+ final LockdownConfigFragment dialog = new LockdownConfigFragment();
+ dialog.show(parent.getFragmentManager(), TAG_LOCKDOWN);
+ }
+
+ private static String getStringOrNull(KeyStore keyStore, String key) {
+ if (!keyStore.isUnlocked()) {
+ return null;
+ }
+ final byte[] value = keyStore.get(key);
+ return value == null ? null : new String(value);
+ }
+
+ private void initProfiles(KeyStore keyStore, Resources res) {
+ final String lockdownKey = getStringOrNull(keyStore, Credentials.LOCKDOWN_VPN);
+
+ mProfiles = VpnSettings.loadVpnProfiles(keyStore, VpnProfile.TYPE_PPTP);
+ mTitles = new ArrayList<>(1 + mProfiles.size());
+ mTitles.add(res.getText(R.string.vpn_lockdown_none));
+
+ mCurrentIndex = 0;
+ for (VpnProfile profile : mProfiles) {
+ if (TextUtils.equals(profile.key, lockdownKey)) {
+ mCurrentIndex = mTitles.size();
+ }
+ mTitles.add(profile.name);
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ final KeyStore keyStore = KeyStore.getInstance();
+
+ initProfiles(keyStore, context.getResources());
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
+
+ builder.setTitle(R.string.vpn_menu_lockdown);
+
+ final View view = dialogInflater.inflate(R.layout.vpn_lockdown_editor, null, false);
+ final ListView listView = (ListView) view.findViewById(android.R.id.list);
+ listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ listView.setAdapter(new TitleAdapter(context, mTitles));
+ listView.setItemChecked(mCurrentIndex, true);
+ builder.setView(view);
+
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final int newIndex = listView.getCheckedItemPosition();
+ if (mCurrentIndex == newIndex) return;
+
+ if (newIndex == 0) {
+ keyStore.delete(Credentials.LOCKDOWN_VPN);
+ } else {
+ final VpnProfile profile = mProfiles.get(newIndex - 1);
+ if (!profile.isValidLockdownProfile()) {
+ Toast.makeText(context, R.string.vpn_lockdown_config_error,
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+ keyStore.put(Credentials.LOCKDOWN_VPN, profile.key.getBytes(),
+ KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
+ }
+
+ // kick profiles since we changed them
+ ConnectivityManager.from(getActivity()).updateLockdownVpn();
+ }
+ });
+
+ return builder.create();
+ }
+}
+
diff --git a/src/com/android/settings/vpn2/ManageablePreference.java b/src/com/android/settings/vpn2/ManageablePreference.java
new file mode 100644
index 0000000..5e507c1
--- /dev/null
+++ b/src/com/android/settings/vpn2/ManageablePreference.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.vpn2;
+
+import android.content.Context;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import com.android.settings.R;
+
+/**
+ * Preference with an additional gear icon. Touching the gear icon triggers an
+ * onChange event.
+ */
+public class ManageablePreference extends Preference {
+ OnClickListener mListener;
+ View mManageView;
+
+ public ManageablePreference(Context context, AttributeSet attrs, OnClickListener onManage) {
+ super(context, attrs);
+ mListener = onManage;
+ setPersistent(false);
+ setOrder(0);
+ setWidgetLayoutResource(R.layout.preference_vpn);
+ }
+
+ @Override
+ protected void onBindView(View view) {
+ mManageView = view.findViewById(R.id.manage);
+ mManageView.setOnClickListener(mListener);
+ mManageView.setTag(this);
+ super.onBindView(view);
+ }
+}
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
index 04853f1..a333de9 100644
--- a/src/com/android/settings/vpn2/VpnSettings.java
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -16,39 +16,36 @@
package com.android.settings.vpn2;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
+import android.app.AppOpsManager;
import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.IConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.UserManager;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.security.Credentials;
import android.security.KeyStore;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.LayoutInflater;
+import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
import android.widget.TextView;
-import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.net.LegacyVpnInfo;
@@ -61,33 +58,39 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
-public class VpnSettings extends SettingsPreferenceFragment implements
- Handler.Callback, Preference.OnPreferenceClickListener,
- DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
- private static final String TAG = "VpnSettings";
+import static android.app.AppOpsManager.OP_ACTIVATE_VPN;
- private static final String TAG_LOCKDOWN = "lockdown";
+/**
+ * Settings screen listing VPNs. Configured VPNs and networks managed by apps
+ * are shown in the same list.
+ */
+public class VpnSettings extends SettingsPreferenceFragment implements
+ Handler.Callback, Preference.OnPreferenceClickListener {
+ private static final String LOG_TAG = "VpnSettings";
private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
+ private static final NetworkRequest VPN_REQUEST = new NetworkRequest.Builder()
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
- // TODO: migrate to using DialogFragment when editing
-
- private final IConnectivityManager mService = IConnectivityManager.Stub
+ private final IConnectivityManager mConnectivityService = IConnectivityManager.Stub
.asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
- private final KeyStore mKeyStore = KeyStore.getInstance();
- private boolean mUnlocking = false;
+ private ConnectivityManager mConnectivityManager;
+ private UserManager mUserManager;
- private HashMap<String, VpnPreference> mPreferences = new HashMap<String, VpnPreference>();
- private VpnDialog mDialog;
+ private final KeyStore mKeyStore = KeyStore.getInstance();
+
+ private HashMap<String, ConfigPreference> mConfigPreferences = new HashMap<>();
+ private HashMap<String, AppPreference> mAppPreferences = new HashMap<>();
private Handler mUpdater;
- private LegacyVpnInfo mInfo;
- private UserManager mUm;
-
- // The key of the profile for the current ContextMenu.
- private String mSelectedKey;
+ private LegacyVpnInfo mConnectedLegacyVpn;
+ private HashSet<String> mConnectedVpns = new HashSet<>();
private boolean mUnavailable;
@@ -100,25 +103,24 @@
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
- mUm = (UserManager) getSystemService(Context.USER_SERVICE);
-
- if (mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
+ mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
mUnavailable = true;
setPreferenceScreen(new PreferenceScreen(getActivity(), null));
return;
}
+ mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+ mConnectivityManager.registerNetworkCallback(VPN_REQUEST, mNetworkCallback);
+
setHasOptionsMenu(true);
addPreferencesFromResource(R.xml.vpn_settings2);
+ }
- 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 onDestroy() {
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ super.onDestroy();
}
@Override
@@ -143,13 +145,11 @@
case R.id.vpn_create: {
// Generate a new key. Here we just use the current time.
long millis = System.currentTimeMillis();
- while (mPreferences.containsKey(Long.toHexString(millis))) {
+ while (mConfigPreferences.containsKey(Long.toHexString(millis))) {
++millis;
}
- mDialog = new VpnDialog(
- getActivity(), this, new VpnProfile(Long.toHexString(millis)), true);
- mDialog.setOnDismissListener(this);
- mDialog.show();
+ VpnProfile profile = new VpnProfile(Long.toHexString(millis));
+ ConfigDialogFragment.show(this, profile, true /* editing */, false /* exists */);
return true;
}
case R.id.vpn_lockdown: {
@@ -161,18 +161,6 @@
}
@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();
@@ -191,42 +179,32 @@
LockdownConfigFragment.show(this);
}
- // Check KeyStore here, so others do not need to deal with it.
- if (!mKeyStore.isUnlocked()) {
- if (!mUnlocking) {
- // Let us unlock KeyStore. See you later!
- Credentials.getInstance().unlock(getActivity());
- } else {
- // We already tried, but it is still not working!
- finishFragment();
- }
- mUnlocking = !mUnlocking;
- return;
+ update();
+ }
+
+ public void update() {
+ // Pref group within which to list VPNs
+ PreferenceGroup vpnGroup = getPreferenceScreen();
+ vpnGroup.removeAll();
+ mConfigPreferences.clear();
+ mAppPreferences.clear();
+
+ // Fetch configured VPN profiles from KeyStore
+ for (VpnProfile profile : loadVpnProfiles(mKeyStore)) {
+ final ConfigPreference pref = new ConfigPreference(getActivity(), mManageListener,
+ profile);
+ pref.setOnPreferenceClickListener(this);
+ mConfigPreferences.put(profile.key, pref);
+ vpnGroup.addPreference(pref);
}
- // 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.size() == 0) {
- PreferenceGroup group = getPreferenceScreen();
-
- final Context context = getActivity();
- final List<VpnProfile> profiles = loadVpnProfiles(mKeyStore);
- for (VpnProfile profile : profiles) {
- final VpnPreference pref = new VpnPreference(context, profile);
- pref.setOnPreferenceClickListener(this);
- mPreferences.put(profile.key, pref);
- group.addPreference(pref);
- }
- }
-
- // Show the dialog if there is one.
- if (mDialog != null) {
- mDialog.setOnDismissListener(this);
- mDialog.show();
+ // 3rd-party VPN apps can change elsewhere. Reload them every time.
+ for (AppOpsManager.PackageOps pkg : getVpnApps()) {
+ final AppPreference pref = new AppPreference(getActivity(), mManageListener,
+ pkg.getPackageName(), pkg.getUid());
+ pref.setOnPreferenceClickListener(this);
+ mAppPreferences.put(pkg.getPackageName(), pref);
+ vpnGroup.addPreference(pref);
}
// Start monitoring.
@@ -234,172 +212,111 @@
mUpdater = new Handler(this);
}
mUpdater.sendEmptyMessage(0);
-
- // Register for context menu. Hmmm, getListView() is hidden?
- registerForContextMenu(getListView());
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- if (mUnavailable) {
- return;
- }
-
- // Hide the dialog if there is one.
- if (mDialog != null) {
- mDialog.setOnDismissListener(null);
- mDialog.dismiss();
- }
-
- // Unregister for context menu.
- if (getView() != null) {
- 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(), KeyStore.UID_SELF,
- KeyStore.FLAG_ENCRYPTED);
-
- // Update the preference.
- VpnPreference preference = mPreferences.get(profile.key);
- if (preference != null) {
- disconnect(profile.key);
- preference.update(profile);
- } else {
- preference = new VpnPreference(getActivity(), profile);
- preference.setOnPreferenceClickListener(this);
- mPreferences.put(profile.key, preference);
- getPreferenceScreen().addPreference(preference);
- }
-
- // If we are not editing, connect!
- if (!mDialog.isEditing()) {
- try {
- connect(profile);
- } catch (Exception e) {
- Log.e(TAG, "connect", e);
- }
- }
- }
- }
-
- @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) {
- VpnProfile profile = ((VpnPreference) preference).getProfile();
- if (mInfo != null && profile.key.equals(mInfo.key) &&
- mInfo.state == LegacyVpnInfo.STATE_CONNECTED) {
+ if (preference instanceof ConfigPreference) {
+ VpnProfile profile = ((ConfigPreference) preference).getProfile();
+ if (mConnectedLegacyVpn != null && profile.key.equals(mConnectedLegacyVpn.key) &&
+ mConnectedLegacyVpn.state == LegacyVpnInfo.STATE_CONNECTED) {
try {
- mInfo.intent.send();
+ mConnectedLegacyVpn.intent.send();
return true;
} catch (Exception e) {
// ignore
}
}
- mDialog = new VpnDialog(getActivity(), this, profile, false);
- } else {
- // Generate a new key. Here we just use the current time.
- long millis = System.currentTimeMillis();
- while (mPreferences.containsKey(Long.toHexString(millis))) {
- ++millis;
+ ConfigDialogFragment.show(this, profile, false /* editing */, true /* exists */);
+ return true;
+ } else if (preference instanceof AppPreference) {
+ AppPreference pref = (AppPreference) preference;
+ boolean connected = (pref.getState() == AppPreference.STATE_CONNECTED);
+
+ if (!connected) {
+ try {
+ UserHandle user = new UserHandle(UserHandle.getUserId(pref.getUid()));
+ Context userContext = getActivity().createPackageContextAsUser(
+ getActivity().getPackageName(), 0 /* flags */, user);
+ PackageManager pm = userContext.getPackageManager();
+ Intent appIntent = pm.getLaunchIntentForPackage(pref.getPackageName());
+ if (appIntent != null) {
+ userContext.startActivityAsUser(appIntent, user);
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException nnfe) {
+ // Fall through
+ }
}
- mDialog = new VpnDialog(getActivity(), this,
- new VpnProfile(Long.toHexString(millis)), true);
+
+ // Already onnected or no launch intent available - show an info dialog
+ PackageInfo pkgInfo = pref.getPackageInfo();
+ AppDialogFragment.show(this, pkgInfo, false /* editing */, connected);
+ return true;
}
- mDialog.setOnDismissListener(this);
- mDialog.show();
- return true;
+ return false;
}
+ private View.OnClickListener mManageListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Object tag = view.getTag();
+
+ if (tag instanceof ConfigPreference) {
+ ConfigPreference pref = (ConfigPreference) tag;
+ ConfigDialogFragment.show(VpnSettings.this, pref.getProfile(), true /* editing */,
+ true /* exists */);
+ } else if (tag instanceof AppPreference) {
+ AppPreference pref = (AppPreference) tag;
+ AppDialogFragment.show(VpnSettings.this, pref.getPackageInfo(), true /* editing */,
+ (pref.getState() == AppPreference.STATE_CONNECTED) /* connected */);
+ }
+ }
+ };
+
@Override
public boolean handleMessage(Message message) {
mUpdater.removeMessages(0);
if (isResumed()) {
try {
- LegacyVpnInfo info = mService.getLegacyVpnInfo();
- if (mInfo != null) {
- VpnPreference preference = mPreferences.get(mInfo.key);
+ // Legacy VPNs
+ LegacyVpnInfo info = mConnectivityService.getLegacyVpnInfo();
+ if (mConnectedLegacyVpn != null) {
+ ConfigPreference preference = mConfigPreferences.get(mConnectedLegacyVpn.key);
if (preference != null) {
- preference.update(-1);
+ preference.setState(-1);
}
- mInfo = null;
+ mConnectedLegacyVpn = null;
}
if (info != null) {
- VpnPreference preference = mPreferences.get(info.key);
+ ConfigPreference preference = mConfigPreferences.get(info.key);
if (preference != null) {
- preference.update(info.state);
- mInfo = info;
+ preference.setState(info.state);
+ mConnectedLegacyVpn = info;
}
}
- } catch (Exception e) {
+
+ // VPN apps
+ for (String key : mConnectedVpns) {
+ AppPreference preference = mAppPreferences.get(key);
+ if (preference != null) {
+ preference.setState(AppPreference.STATE_DISCONNECTED);
+ }
+ }
+ mConnectedVpns.clear();
+ // TODO: also query VPN services in user profiles STOPSHIP
+ VpnConfig cfg = mConnectivityService.getVpnConfig();
+ if (cfg != null) {
+ mConnectedVpns.add(cfg.user);
+ }
+ for (String key : mConnectedVpns) {
+ AppPreference preference = mAppPreferences.get(key);
+ if (preference != null) {
+ preference.setState(AppPreference.STATE_CONNECTED);
+ }
+ }
+ } catch (RemoteException e) {
// ignore
}
mUpdater.sendEmptyMessageDelayed(0, 1000);
@@ -407,186 +324,76 @@
return true;
}
- private void connect(VpnProfile profile) throws Exception {
- try {
- mService.startLegacyVpn(profile);
- } catch (IllegalStateException e) {
- Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show();
- }
- }
-
- private void disconnect(String key) {
- if (mInfo != null && key.equals(mInfo.key)) {
- try {
- mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
- } catch (Exception e) {
- // ignore
+ private NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ if (mUpdater != null) {
+ mUpdater.sendEmptyMessage(0);
}
}
- }
+
+ @Override
+ public void onLost(Network network) {
+ if (mUpdater != null) {
+ mUpdater.sendEmptyMessage(0);
+ }
+ }
+ };
@Override
protected int getHelpResource() {
return R.string.help_url_vpn;
}
- private static class VpnPreference extends Preference {
- private VpnProfile mProfile;
- private int mState = -1;
+ private List<AppOpsManager.PackageOps> getVpnApps() {
+ List<AppOpsManager.PackageOps> result = Lists.newArrayList();
- VpnPreference(Context context, VpnProfile profile) {
- super(context);
- setPersistent(false);
- setOrder(0);
-
- mProfile = profile;
- update();
+ // Build a filter of currently active user profiles.
+ SparseArray<Boolean> currentProfileIds = new SparseArray<>();
+ for (UserHandle profile : mUserManager.getUserProfiles()) {
+ currentProfileIds.put(profile.getIdentifier(), Boolean.TRUE);
}
- VpnProfile getProfile() {
- return mProfile;
- }
-
- void update(VpnProfile profile) {
- mProfile = profile;
- update();
- }
-
- void update(int state) {
- mState = state;
- update();
- }
-
- void update() {
- if (mState < 0) {
- String[] types = getContext().getResources()
- .getStringArray(R.array.vpn_types_long);
- setSummary(types[mProfile.type]);
- } else {
- String[] states = getContext().getResources()
- .getStringArray(R.array.vpn_states);
- setSummary(states[mState]);
- }
- setTitle(mProfile.name);
- notifyHierarchyChanged();
- }
-
- @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);
+ // Fetch VPN-enabled apps from AppOps.
+ AppOpsManager aom = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
+ List<AppOpsManager.PackageOps> apps = aom.getPackagesForOps(new int[] {OP_ACTIVATE_VPN});
+ if (apps != null) {
+ for (AppOpsManager.PackageOps pkg : apps) {
+ int userId = UserHandle.getUserId(pkg.getUid());
+ if (currentProfileIds.get(userId) == null) {
+ // Skip packages for users outside of our profile group.
+ continue;
+ }
+ // Look for a MODE_ALLOWED permission to activate VPN.
+ boolean allowed = false;
+ for (AppOpsManager.OpEntry op : pkg.getOps()) {
+ if (op.getOp() == OP_ACTIVATE_VPN &&
+ op.getMode() == AppOpsManager.MODE_ALLOWED) {
+ allowed = true;
+ }
+ }
+ if (allowed) {
+ result.add(pkg);
}
}
+ }
+ return result;
+ }
+
+ protected static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) {
+ final ArrayList<VpnProfile> result = Lists.newArrayList();
+
+ // This might happen if the user does not yet have a keystore. Quietly short-circuit because
+ // no keystore means no VPN configs.
+ if (!keyStore.isUnlocked()) {
return result;
}
- }
- /**
- * Dialog to configure always-on VPN.
- */
- public static class LockdownConfigFragment extends DialogFragment {
- private List<VpnProfile> mProfiles;
- private List<CharSequence> mTitles;
- private int mCurrentIndex;
-
- private static class TitleAdapter extends ArrayAdapter<CharSequence> {
- public TitleAdapter(Context context, List<CharSequence> objects) {
- super(context, com.android.internal.R.layout.select_dialog_singlechoice_material,
- android.R.id.text1, objects);
- }
- }
-
- public static void show(VpnSettings parent) {
- if (!parent.isAdded()) return;
-
- final LockdownConfigFragment dialog = new LockdownConfigFragment();
- dialog.show(parent.getFragmentManager(), TAG_LOCKDOWN);
- }
-
- private static String getStringOrNull(KeyStore keyStore, String key) {
- final byte[] value = keyStore.get(Credentials.LOCKDOWN_VPN);
- return value == null ? null : new String(value);
- }
-
- private void initProfiles(KeyStore keyStore, Resources res) {
- final String lockdownKey = getStringOrNull(keyStore, Credentials.LOCKDOWN_VPN);
-
- mProfiles = loadVpnProfiles(keyStore, VpnProfile.TYPE_PPTP);
- mTitles = Lists.newArrayList();
- mTitles.add(res.getText(R.string.vpn_lockdown_none));
- mCurrentIndex = 0;
-
- for (VpnProfile profile : mProfiles) {
- if (TextUtils.equals(profile.key, lockdownKey)) {
- mCurrentIndex = mTitles.size();
- }
- mTitles.add(profile.name);
- }
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Context context = getActivity();
- final KeyStore keyStore = KeyStore.getInstance();
-
- initProfiles(keyStore, context.getResources());
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(context);
- final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
-
- builder.setTitle(R.string.vpn_menu_lockdown);
-
- final View view = dialogInflater.inflate(R.layout.vpn_lockdown_editor, null, false);
- final ListView listView = (ListView) view.findViewById(android.R.id.list);
- listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- listView.setAdapter(new TitleAdapter(context, mTitles));
- listView.setItemChecked(mCurrentIndex, true);
- builder.setView(view);
-
- builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final int newIndex = listView.getCheckedItemPosition();
- if (mCurrentIndex == newIndex) return;
-
- if (newIndex == 0) {
- keyStore.delete(Credentials.LOCKDOWN_VPN);
-
- } else {
- final VpnProfile profile = mProfiles.get(newIndex - 1);
- if (!profile.isValidLockdownProfile()) {
- Toast.makeText(context, R.string.vpn_lockdown_config_error,
- Toast.LENGTH_LONG).show();
- return;
- }
- keyStore.put(Credentials.LOCKDOWN_VPN, profile.key.getBytes(),
- KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
- }
-
- // kick profiles since we changed them
- ConnectivityManager.from(getActivity()).updateLockdownVpn();
- }
- });
-
- return builder.create();
- }
- }
-
- private static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) {
- final ArrayList<VpnProfile> result = Lists.newArrayList();
- final String[] keys = keyStore.saw(Credentials.VPN);
- if (keys != null) {
- for (String key : keys) {
- final VpnProfile profile = VpnProfile.decode(
- key, keyStore.get(Credentials.VPN + key));
- if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) {
- result.add(profile);
- }
+ // We are the only user of profiles in KeyStore so no locks are needed.
+ for (String key : keyStore.saw(Credentials.VPN)) {
+ final VpnProfile profile = VpnProfile.decode(key, keyStore.get(Credentials.VPN + key));
+ if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) {
+ result.add(profile);
}
}
return result;
diff --git a/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java b/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
index 4dfea08..ba78794 100644
--- a/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
+++ b/src/com/android/settings/wifi/WifiSettingsForSetupWizard.java
@@ -30,6 +30,8 @@
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
+import com.android.setupwizardlib.SetupWizardListLayout;
+import com.android.setupwizardlib.view.NavigationBar;
/**
* This customized version of WifiSettings is shown to the user only during Setup Wizard. Menu
@@ -48,15 +50,9 @@
@Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
-
- final View view = inflater.inflate(R.layout.setup_preference, container, false);
-
- final ListView list = (ListView) view.findViewById(android.R.id.list);
- final View title = view.findViewById(R.id.title);
- if (title == null) {
- final View header = inflater.inflate(R.layout.setup_wizard_header, list, false);
- list.addHeaderView(header, null, false);
- }
+ final SetupWizardListLayout layout = (SetupWizardListLayout) inflater.inflate(
+ R.layout.setup_wifi_layout, container, false);
+ final ListView list = layout.getListView();
mAddOtherNetworkItem = inflater.inflate(R.layout.setup_wifi_add_network, list, false);
list.addFooterView(mAddOtherNetworkItem, null, true);
@@ -69,19 +65,19 @@
}
});
- return view;
+ final NavigationBar navigationBar = layout.getNavigationBar();
+ if (navigationBar != null) {
+ WifiSetupActivity activity = (WifiSetupActivity) getActivity();
+ activity.onNavigationBarCreated(navigationBar);
+ }
+
+ return layout;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- getView().setSystemUiVisibility(
- View.STATUS_BAR_DISABLE_HOME |
- View.STATUS_BAR_DISABLE_RECENT |
- View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS |
- View.STATUS_BAR_DISABLE_CLOCK);
-
if (hasNextButton()) {
getNextButton().setVisibility(View.GONE);
}
diff --git a/src/com/android/settings/wifi/WifiSetupActivity.java b/src/com/android/settings/wifi/WifiSetupActivity.java
index 321ecb6..38fc32d 100644
--- a/src/com/android/settings/wifi/WifiSetupActivity.java
+++ b/src/com/android/settings/wifi/WifiSetupActivity.java
@@ -35,11 +35,10 @@
import com.android.settings.ButtonBarHandler;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
-import com.android.setupwizard.navigationbar.SetupWizardNavBar;
-import com.android.setupwizard.navigationbar.SetupWizardNavBar.NavigationBarListener;
+import com.android.setupwizardlib.view.NavigationBar;
public class WifiSetupActivity extends WifiPickerActivity
- implements ButtonBarHandler, NavigationBarListener {
+ implements ButtonBarHandler, NavigationBar.NavigationBarListener {
private static final String TAG = "WifiSetupActivity";
// this boolean extra specifies whether to auto finish when connection is established
@@ -72,7 +71,7 @@
// Whether the device is connected to WiFi
private boolean mWifiConnected;
- private SetupWizardNavBar mNavigationBar;
+ private NavigationBar mNavigationBar;
private IntentFilter mFilter = new IntentFilter();
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -203,9 +202,9 @@
finish();
}
- @Override
- public void onNavigationBarCreated(final SetupWizardNavBar bar) {
+ public void onNavigationBarCreated(final NavigationBar bar) {
mNavigationBar = bar;
+ bar.setNavigationBarListener(this);
SetupWizardUtils.setImmersiveMode(this);
}