Merge "Updating call to set icon on task description"
diff --git a/Android.bp b/Android.bp
index cc273fb..4112d04 100644
--- a/Android.bp
+++ b/Android.bp
@@ -54,6 +54,8 @@
"settings-logtags",
"statslog-settings",
"zxing-core-1.7",
+ "android.hardware.dumpstate-V1.0-java",
+ "android.hardware.dumpstate-V1.1-java",
],
libs: [
diff --git a/res/layout-land/confirm_lock_password.xml b/res/layout-land/confirm_lock_password.xml
index 3d2589c..cad1bfc 100644
--- a/res/layout-land/confirm_lock_password.xml
+++ b/res/layout-land/confirm_lock_password.xml
@@ -62,7 +62,8 @@
android:layout_height="wrap_content"
android:layout_marginStart="?attr/sudMarginSides"
android:layout_marginEnd="?attr/sudMarginSides"
- android:layout_gravity="center" />
+ android:layout_gravity="center"
+ android:visibility="gone" />
</LinearLayout>
<Space
diff --git a/res/layout-land/confirm_lock_pattern.xml b/res/layout-land/confirm_lock_pattern.xml
index 55e1ef2..861c00a 100644
--- a/res/layout-land/confirm_lock_pattern.xml
+++ b/res/layout-land/confirm_lock_pattern.xml
@@ -59,7 +59,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="?attr/sudMarginSides"
- android:layout_marginEnd="?attr/sudMarginSides" />x
+ android:layout_marginEnd="?attr/sudMarginSides" />
<Button
android:id="@+id/cancelButton"
@@ -77,7 +77,8 @@
android:layout_height="wrap_content"
android:layout_marginStart="?attr/sudMarginSides"
android:layout_marginEnd="?attr/sudMarginSides"
- android:layout_gravity="center" />
+ android:layout_gravity="center"
+ android:visibility="gone" />
<Space
android:layout_width="match_parent"
diff --git a/res/layout/adb_qrcode_scanner_fragment.xml b/res/layout/adb_qrcode_scanner_fragment.xml
new file mode 100644
index 0000000..975256d
--- /dev/null
+++ b/res/layout/adb_qrcode_scanner_fragment.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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.google.android.setupdesign.GlifLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:theme="@style/GlifV3Theme.Light"
+ android:icon="@drawable/ic_scan_32dp">
+
+ <LinearLayout
+ style="@style/SudContentFrame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:id="@+id/camera_layout">
+
+ <TextView
+ android:id="@android:id/summary"
+ style="@style/TextAppearance.SudGlifBody"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="?attr/sudMarginSides"
+ android:layout_marginEnd="?attr/sudMarginSides"
+ android:textAlignment="center"
+ android:accessibilityLiveRegion="polite"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="true">
+ <TextureView
+ android:id="@+id/preview_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qrcode_preview_size"/>
+ <com.android.settings.wifi.qrcode.QrDecorateView
+ android:id="@+id/decorate_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qrcode_preview_size"/>
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/error_message"
+ style="@style/TextAppearance.ErrorText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginStart="?attr/sudMarginSides"
+ android:layout_marginEnd="?attr/sudMarginSides"
+ android:textAlignment="center"
+ android:visibility="invisible"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <!--
+ The spinner indicating that the device is waiting for pairing
+ after getting valid QR code
+ -->
+ <LinearLayout
+ android:id="@+id/verifying_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+ <ProgressBar
+ android:id="@+id/verifying_progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_item_progress_text"
+ android:text="@string/adb_wireless_verifying_qrcode_text"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+</com.google.android.setupdesign.GlifLayout>
diff --git a/res/layout/adb_wireless_dialog.xml b/res/layout/adb_wireless_dialog.xml
new file mode 100644
index 0000000..31f94c9
--- /dev/null
+++ b/res/layout/adb_wireless_dialog.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/dialog_scrollview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fadeScrollbars="false"
+ android:scrollIndicators="top|bottom">
+
+ <LinearLayout
+ android:id="@+id/l_adbwirelessdialog"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="8dip">
+
+ <LinearLayout android:id="@+id/l_pairing_six_digit"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_section"
+ android:visibility="gone">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_item" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_item_label"
+ android:text="@string/adb_pairing_device_dialog_pairing_code_label"
+ android:textDirection="locale" />
+
+ <TextView
+ android:id="@+id/pairing_code"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_item_content" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_item_label"
+ android:text="@string/adb_wireless_ip_addr_preference_title"
+ android:textDirection="locale"
+ android:paddingTop="8dip"/>
+
+ <TextView
+ android:id="@+id/ip_addr"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_item_label"
+ android:text="@string/summary_placeholder"
+ android:textDirection="locale" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/l_pairing_failed"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_section"
+ android:visibility="gone">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_item" >
+ <TextView
+ android:id="@+id/pairing_failed_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_item_label"
+ android:textDirection="locale" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/l_qrcode_pairing_failed"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_section"
+ android:visibility="gone">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_item" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/adb_wireless_item_label"
+ android:textDirection="locale"
+ android:text="@string/adb_qrcode_pairing_device_failed_msg"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ </LinearLayout>
+</ScrollView>
+
diff --git a/res/layout/confirm_lock_password_base.xml b/res/layout/confirm_lock_password_base.xml
index 9dff190..a0935bf 100644
--- a/res/layout/confirm_lock_password_base.xml
+++ b/res/layout/confirm_lock_password_base.xml
@@ -61,7 +61,8 @@
android:layout_height="wrap_content"
android:layout_marginStart="?attr/sudMarginSides"
android:layout_marginEnd="?attr/sudMarginSides"
- android:layout_gravity="center" />
+ android:layout_gravity="center"
+ android:visibility="gone" />
</LinearLayout>
<Space
diff --git a/res/layout/confirm_lock_pattern_base.xml b/res/layout/confirm_lock_pattern_base.xml
index 7bb4d01..9a37eaa 100644
--- a/res/layout/confirm_lock_pattern_base.xml
+++ b/res/layout/confirm_lock_pattern_base.xml
@@ -72,7 +72,8 @@
android:layout_height="wrap_content"
android:layout_marginStart="?attr/sudMarginSides"
android:layout_marginEnd="?attr/sudMarginSides"
- android:layout_gravity="center" />
+ android:layout_gravity="center"
+ android:visibility="gone" />
</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 30eb00c..aa37f06 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10990,8 +10990,8 @@
<string name="graphics_driver_app_preference_default">Default</string>
<!-- The game driver value for Game Driver app preference [CHAR LIMIT=50] -->
<string name="graphics_driver_app_preference_game_driver">Game Driver</string>
- <!-- The prerelase driver value for Prerelease Driver app preference [CHAR LIMIT=50] -->
- <string name="graphics_driver_app_preference_prerelease_driver">Prerelease Driver</string>
+ <!-- The prerelase driver value for Developer Driver app preference [CHAR LIMIT=50] -->
+ <string name="graphics_driver_app_preference_prerelease_driver">Developer Driver</string>
<!-- The system driver value for system graphics driver app preference [CHAR LIMIT=50] -->
<string name="graphics_driver_app_preference_system">System Graphics Driver</string>
<!-- All the graphics driver preference values for all apps globally [CHAR LIMIT=50] -->
@@ -11002,6 +11002,7 @@
<!-- All the values of graphics driver for app preference [CHAR LIMIT=50] -->
<string-array name="graphics_driver_app_preference_values">
<item>@string/graphics_driver_app_preference_default</item>
+ <item>@string/graphics_driver_app_preference_prerelease_driver</item>
<item>@string/graphics_driver_app_preference_game_driver</item>
<item>@string/graphics_driver_app_preference_system</item>
</string-array>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c42fab2..85b25e4 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -153,6 +153,44 @@
<item name="android:orientation">vertical</item>
</style>
+ <style name="adb_wireless_item">
+ <item name="android:layout_marginTop">8dp</item>
+ <item name="android:layout_marginStart">8dp</item>
+ <item name="android:layout_marginEnd">8dp</item>
+ <item name="android:paddingStart">8dp</item>
+ <item name="android:paddingEnd">8dp</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:gravity">start</item>
+ </style>
+
+ <style name="adb_wireless_item_label">
+ <item name="android:paddingStart">8dp</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textAlignment">viewStart</item>
+ <item name="android:textAppearance">@android:style/TextAppearance.Material.Body1</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="adb_wireless_item_content">
+ <item name="android:paddingStart">8dp</item>
+ <item name="android:textSize">24sp</item>
+ <item name="android:textAlignment">viewStart</item>
+ <item name="android:textAppearance">@android:style/TextAppearance.Material.Body1</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="adb_wireless_item_progress_text">
+ <item name="android:paddingTop">16dp</item>
+ <item name="android:textSize">18sp</item>
+ <item name="android:textAlignment">viewStart</item>
+ <item name="android:textAppearance">@android:style/TextAppearance.Material.Body1</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="adb_wireless_section">
+ <item name="android:orientation">vertical</item>
+ </style>
+
<style name="ConfirmDeviceCredentialsAnimationStyle"
parent="@*android:style/Animation.Material.Activity">
<item name="android:activityOpenEnterAnimation">@anim/confirm_credential_open_enter</item>
diff --git a/res/xml/adb_device_details_fragment.xml b/res/xml/adb_device_details_fragment.xml
new file mode 100644
index 0000000..2e149ed
--- /dev/null
+++ b/res/xml/adb_device_details_fragment.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/device_details_title"
+ settings:initialExpandedChildrenCount="3">
+
+ <com.android.settingslib.widget.LayoutPreference
+ android:key="adb_device_header"
+ android:layout="@layout/settings_entity_header"
+ android:selectable="false" />
+
+ <!-- Buttons -->
+ <com.android.settingslib.widget.ActionButtonsPreference
+ android:key="buttons"
+ android:selectable="false" />
+
+ <!-- Device Fingerprint Details -->
+ <PreferenceCategory
+ android:key="fingerprint_category"
+ android:layout="@layout/preference_category_no_label">
+ </PreferenceCategory>
+</PreferenceScreen>
+
diff --git a/res/xml/adb_wireless_settings.xml b/res/xml/adb_wireless_settings.xml
new file mode 100644
index 0000000..7f60c69
--- /dev/null
+++ b/res/xml/adb_wireless_settings.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/adb_wireless_settings">
+
+ <PreferenceCategory
+ android:layout="@layout/preference_category_no_label">
+ <!-- ADB device name -->
+ <Preference
+ android:key="adb_device_name_pref"
+ android:title="@string/my_device_info_device_name_preference_title"
+ android:summary="@string/summary_placeholder"
+ android:selectable="false"
+ settings:controller="com.android.settings.development.AdbDeviceNamePreferenceController"
+ settings:enableCopying="true"/>
+
+ <!-- IP address & port -->
+ <Preference
+ android:key="adb_ip_addr_pref"
+ android:title="@string/adb_wireless_ip_addr_preference_title"
+ android:summary="@string/summary_placeholder"
+ android:selectable="false"/>
+ </PreferenceCategory>
+
+ <!-- Pairing methods category -->
+ <PreferenceCategory
+ android:key="adb_pairing_methods_category"
+ android:layout="@layout/preference_category_no_label">
+ <!-- qrcode scanner -->
+ <Preference
+ android:key="adb_pair_method_qrcode_pref"
+ android:icon="@drawable/ic_scan_24dp"
+ android:title="@string/adb_pair_method_qrcode_title"
+ android:summary="@string/adb_pair_method_qrcode_summary"/>
+ <Preference
+ android:key="adb_pair_method_code_pref"
+ android:title="@string/adb_pair_method_code_title"
+ android:summary="@string/adb_pair_method_code_summary"/>
+ </PreferenceCategory>
+
+ <!-- Paired devices list -->
+ <PreferenceCategory
+ android:key="adb_paired_devices_category"
+ android:title="@string/adb_paired_devices_title"/>
+
+ <!-- Off message: Shown only in the off state -->
+ <PreferenceCategory
+ android:key="adb_wireless_footer_category"
+ android:layout="@layout/preference_category_no_label"
+ settings:allowDividerAbove="false"/>
+
+</PreferenceScreen>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 940cb59..834fb48 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -141,6 +141,13 @@
<Preference android:key="clear_adb_keys"
android:title="@string/clear_adb_keys" />
+ <com.android.settings.widget.MasterSwitchPreference
+ android:fragment="com.android.settings.development.WirelessDebuggingFragment"
+ android:key="toggle_adb_wireless"
+ android:title="@string/enable_adb_wireless"
+ android:summary="@string/enable_adb_wireless_summary"
+ settings:keywords="@string/keywords_adb_wireless" />
+
<SwitchPreference
android:key="enable_terminal"
android:title="@string/enable_terminal_title"
@@ -152,6 +159,11 @@
android:summary="@string/bugreport_in_power_summary" />
<SwitchPreference
+ android:key="enable_verbose_vendor_logging"
+ android:title="@string/enable_verbose_vendor_logging"
+ android:summary="@string/enable_verbose_vendor_logging_summary" />
+
+ <SwitchPreference
android:key="automatic_system_server_heap_dumps"
android:title="@string/automatic_system_heap_dump_title"
android:summary="@string/automatic_system_heap_dump_summary" />
@@ -264,6 +276,11 @@
android:summary="@string/mobile_data_always_on_summary" />
<SwitchPreference
+ android:key="enhanced_connectivity"
+ android:title="@string/enhanced_connectivity"
+ android:summary="@string/enhanced_connectivity_summary" />
+
+ <SwitchPreference
android:key="tethering_hardware_offload"
android:title="@string/tethering_hardware_offload"
android:summary="@string/tethering_hardware_offload_summary" />
diff --git a/src/com/android/settings/TestingSettingsBroadcastReceiver.java b/src/com/android/settings/TestingSettingsBroadcastReceiver.java
index aa28a18..0e1296b 100644
--- a/src/com/android/settings/TestingSettingsBroadcastReceiver.java
+++ b/src/com/android/settings/TestingSettingsBroadcastReceiver.java
@@ -1,10 +1,9 @@
package com.android.settings;
-import static com.android.internal.telephony.TelephonyIntents.SECRET_CODE_ACTION;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.telephony.TelephonyManager;
import com.android.settings.Settings.TestingSettingsActivity;
@@ -16,7 +15,7 @@
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(SECRET_CODE_ACTION)) {
+ if (intent.getAction().equals(TelephonyManager.ACTION_SECRET_CODE)) {
Intent i = new Intent(Intent.ACTION_MAIN);
i.setClass(context, TestingSettingsActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/src/com/android/settings/development/AdbDeviceDetailsActionController.java b/src/com/android/settings/development/AdbDeviceDetailsActionController.java
new file mode 100644
index 0000000..da4430d
--- /dev/null
+++ b/src/com/android/settings/development/AdbDeviceDetailsActionController.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.debug.PairDevice;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.ActionButtonsPreference;
+
+/**
+ * Controller for logic pertaining to displaying adb device information for the
+ * {@link AdbDeviceDetailsFragment}.
+ */
+public class AdbDeviceDetailsActionController extends AbstractPreferenceController {
+ private static final String TAG = "AdbDeviceDetailsAction";
+
+ @VisibleForTesting
+ static final String KEY_BUTTONS_PREF = "buttons";
+
+ private PairDevice mPairedDevice;
+ private final Fragment mFragment;
+
+ private ActionButtonsPreference mButtonsPref;
+
+ public AdbDeviceDetailsActionController(
+ PairDevice pairedDevice,
+ Context context,
+ Fragment fragment) {
+ super(context);
+
+ mPairedDevice = pairedDevice;
+ mFragment = fragment;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_BUTTONS_PREF;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mButtonsPref = ((ActionButtonsPreference) screen.findPreference(getPreferenceKey()))
+ .setButton1Visible(false)
+ .setButton2Icon(R.drawable.ic_settings_delete)
+ .setButton2Text(R.string.adb_device_forget)
+ .setButton2OnClickListener(view -> forgetDevice());
+ }
+
+ /**
+ * Forgets the device.
+ */
+ private void forgetDevice() {
+ Intent intent = new Intent();
+ intent.putExtra(
+ WirelessDebuggingFragment.PAIRED_DEVICE_REQUEST_TYPE,
+ WirelessDebuggingFragment.FORGET_ACTION);
+ intent.putExtra(
+ WirelessDebuggingFragment.PAIRED_DEVICE_EXTRA,
+ mPairedDevice);
+ mFragment.getActivity().setResult(Activity.RESULT_OK, intent);
+ mFragment.getActivity().finish();
+ }
+}
+
diff --git a/src/com/android/settings/development/AdbDeviceDetailsFingerprintController.java b/src/com/android/settings/development/AdbDeviceDetailsFingerprintController.java
new file mode 100644
index 0000000..b9c2d43
--- /dev/null
+++ b/src/com/android/settings/development/AdbDeviceDetailsFingerprintController.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.content.Context;
+import android.debug.PairDevice;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.FooterPreference;
+
+/**
+ * Controller for logic pertaining to displaying adb device information for the
+ * {@link AdbDeviceDetailsFragment}.
+ */
+public class AdbDeviceDetailsFingerprintController extends AbstractPreferenceController {
+
+ private static final String TAG = "AdbDeviceDetailsFinger";
+
+ @VisibleForTesting
+ static final String KEY_FINGERPRINT_CATEGORY = "fingerprint_category";
+
+ private PairDevice mPairedDevice;
+ private final Fragment mFragment;
+
+ private PreferenceCategory mFingerprintCategory;
+ private FooterPreference mFingerprintPref;
+
+ public AdbDeviceDetailsFingerprintController(
+ PairDevice pairedDevice,
+ Context context,
+ Fragment fragment) {
+ super(context);
+
+ mPairedDevice = pairedDevice;
+ mFragment = fragment;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_FINGERPRINT_CATEGORY;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mFingerprintCategory = (PreferenceCategory) screen.findPreference(getPreferenceKey());
+ mFingerprintPref = new FooterPreference(mFingerprintCategory.getContext());
+ final CharSequence titleFormat = mContext.getText(
+ R.string.adb_device_fingerprint_title_format);
+ mFingerprintPref.setTitle(String.format(
+ titleFormat.toString(), mPairedDevice.getGuid()));
+ mFingerprintCategory.addPreference(mFingerprintPref);
+ }
+}
+
diff --git a/src/com/android/settings/development/AdbDeviceDetailsFragment.java b/src/com/android/settings/development/AdbDeviceDetailsFragment.java
new file mode 100644
index 0000000..9861345
--- /dev/null
+++ b/src/com/android/settings/development/AdbDeviceDetailsFragment.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.debug.PairDevice;
+import android.os.Bundle;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment shown when clicking on a paired device in the Wireless
+ * Debugging fragment.
+ */
+public class AdbDeviceDetailsFragment extends DashboardFragment {
+ private static final String TAG = "AdbDeviceDetailsFrag";
+ private PairDevice mPairedDevice;
+
+ public AdbDeviceDetailsFragment() {
+ super();
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ // Get the paired device stored in the extras
+ Bundle bundle = getArguments();
+ if (bundle.containsKey(AdbPairedDevicePreference.PAIRED_DEVICE_EXTRA)) {
+ mPairedDevice = (PairDevice) bundle.getParcelable(
+ AdbPairedDevicePreference.PAIRED_DEVICE_EXTRA);
+ }
+ super.onAttach(context);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.ADB_WIRELESS_DEVICE_DETAILS;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.adb_device_details_fragment;
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ controllers.add(new AdbDeviceDetailsHeaderController(mPairedDevice, context, this));
+ controllers.add(new AdbDeviceDetailsActionController(mPairedDevice, context, this));
+ controllers.add(new AdbDeviceDetailsFingerprintController(mPairedDevice, context, this));
+
+ return controllers;
+ }
+}
diff --git a/src/com/android/settings/development/AdbDeviceDetailsHeaderController.java b/src/com/android/settings/development/AdbDeviceDetailsHeaderController.java
new file mode 100644
index 0000000..a4e79ea
--- /dev/null
+++ b/src/com/android/settings/development/AdbDeviceDetailsHeaderController.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.content.Context;
+import android.debug.PairDevice;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.widget.LayoutPreference;
+
+/**
+ * Controller for logic pertaining to displaying adb device information for the
+ * {@link AdbDeviceDetailsFragment}.
+ */
+public class AdbDeviceDetailsHeaderController extends AbstractPreferenceController
+ implements PreferenceControllerMixin, LifecycleObserver {
+
+ private static final String TAG = "AdbDeviceDetailsHeader";
+
+ @VisibleForTesting
+ static final String KEY_HEADER = "adb_device_header";
+
+ private PairDevice mPairedDevice;
+ private final Fragment mFragment;
+ private EntityHeaderController mEntityHeaderController;
+
+ public AdbDeviceDetailsHeaderController(
+ PairDevice pairedDevice,
+ Context context,
+ Fragment fragment) {
+ super(context);
+
+ mPairedDevice = pairedDevice;
+ mFragment = fragment;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_HEADER;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ setupEntityHeader(screen);
+ }
+
+ private void setupEntityHeader(PreferenceScreen screen) {
+ LayoutPreference headerPref = (LayoutPreference) screen.findPreference(KEY_HEADER);
+ mEntityHeaderController =
+ EntityHeaderController.newInstance(
+ mFragment.getActivity(), mFragment,
+ headerPref.findViewById(R.id.entity_header));
+
+ mEntityHeaderController
+ .setIcon(mContext.getDrawable(com.android.internal.R.drawable.ic_bt_laptop))
+ .setLabel(mPairedDevice.getDeviceName())
+ .done(mFragment.getActivity(), true);
+ }
+}
+
diff --git a/src/com/android/settings/development/AdbDeviceNamePreferenceController.java b/src/com/android/settings/development/AdbDeviceNamePreferenceController.java
new file mode 100644
index 0000000..7706c3c
--- /dev/null
+++ b/src/com/android/settings/development/AdbDeviceNamePreferenceController.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import static android.content.Context.CLIPBOARD_SERVICE;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Build;
+import android.provider.Settings;
+import android.widget.Toast;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Controller for the device name preference in the Wireless debugging
+ * fragment.
+ */
+public class AdbDeviceNamePreferenceController extends BasePreferenceController {
+ private static final String TAG = "AdbDeviceNamePrefCtrl";
+
+ private String mDeviceName;
+
+ public AdbDeviceNamePreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ // Keep device name in sync with Settings > About phone > Device name
+ mDeviceName = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.DEVICE_NAME);
+ if (mDeviceName == null) {
+ mDeviceName = Build.MODEL;
+ }
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return mDeviceName;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void copy() {
+ final ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(
+ CLIPBOARD_SERVICE);
+ clipboard.setPrimaryClip(ClipData.newPlainText("text", mDeviceName));
+
+ final String toast = mContext.getString(R.string.copyable_slice_toast,
+ mDeviceName);
+ Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/src/com/android/settings/development/AdbIpAddressPreferenceController.java b/src/com/android/settings/development/AdbIpAddressPreferenceController.java
new file mode 100644
index 0000000..dbc329e
--- /dev/null
+++ b/src/com/android/settings/development/AdbIpAddressPreferenceController.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.content.Context;
+import android.debug.IAdbManager;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.wifi.WifiManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.deviceinfo.AbstractConnectivityPreferenceController;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.Iterator;
+
+/**
+ * Controller for the ip address preference in the Wireless debugging
+ * fragment.
+ */
+public class AdbIpAddressPreferenceController extends AbstractConnectivityPreferenceController {
+ private static final String TAG = "AdbIpAddrPrefCtrl";
+
+ private static final String[] CONNECTIVITY_INTENTS = {
+ ConnectivityManager.CONNECTIVITY_ACTION,
+ WifiManager.ACTION_LINK_CONFIGURATION_CHANGED,
+ WifiManager.NETWORK_STATE_CHANGED_ACTION,
+ };
+
+ private static final String PREF_KEY = "adb_ip_addr_pref";
+ private Preference mAdbIpAddrPref;
+ private int mPort;
+ private final ConnectivityManager mCM;
+ private IAdbManager mAdbManager;
+
+ public AdbIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context, lifecycle);
+ mCM = context.getSystemService(ConnectivityManager.class);
+ mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(
+ Context.ADB_SERVICE));
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return PREF_KEY;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mAdbIpAddrPref = screen.findPreference(PREF_KEY);
+ updateConnectivity();
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ updateConnectivity();
+ }
+
+ @Override
+ protected String[] getConnectivityIntents() {
+ return CONNECTIVITY_INTENTS;
+ }
+
+ protected int getPort() {
+ try {
+ return mAdbManager.getAdbWirelessPort();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get the adbwifi port");
+ }
+ return 0;
+ }
+
+ public String getIpv4Address() {
+ return getDefaultIpAddresses(mCM);
+ }
+
+ @Override
+ protected void updateConnectivity() {
+ String ipAddress = getDefaultIpAddresses(mCM);
+ if (ipAddress != null) {
+ int port = getPort();
+ if (port <= 0) {
+ mAdbIpAddrPref.setSummary(R.string.status_unavailable);
+ } else {
+ ipAddress += ":" + port;
+ }
+ mAdbIpAddrPref.setSummary(ipAddress);
+ } else {
+ mAdbIpAddrPref.setSummary(R.string.status_unavailable);
+ }
+ }
+
+ /**
+ * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
+ * addresses.
+ * @param cm ConnectivityManager
+ * @return the formatted and newline-separated IP addresses, or null if none.
+ */
+ private static String getDefaultIpAddresses(ConnectivityManager cm) {
+ LinkProperties prop = cm.getActiveLinkProperties();
+ return formatIpAddresses(prop);
+ }
+
+ private static String formatIpAddresses(LinkProperties prop) {
+ if (prop == null) {
+ return null;
+ }
+
+ Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
+ // If there are no entries, return null
+ if (!iter.hasNext()) {
+ return null;
+ }
+
+ // Concatenate all available addresses, newline separated
+ StringBuilder addresses = new StringBuilder();
+ while (iter.hasNext()) {
+ InetAddress addr = iter.next();
+ if (addr instanceof Inet4Address) {
+ // adb only supports ipv4 at the moment
+ addresses.append(addr.getHostAddress());
+ break;
+ }
+ }
+ return addresses.toString();
+ }
+}
diff --git a/src/com/android/settings/development/AdbPairedDevicePreference.java b/src/com/android/settings/development/AdbPairedDevicePreference.java
new file mode 100644
index 0000000..a82a949
--- /dev/null
+++ b/src/com/android/settings/development/AdbPairedDevicePreference.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.content.Context;
+import android.debug.PairDevice;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+
+/**
+ * An AP preference for the currently connected AP
+ */
+public class AdbPairedDevicePreference extends Preference {
+ private static final String TAG = "AdbPairedDevicePref";
+
+ private PairDevice mPairedDevice;
+
+ // Extract using getSerializable(PAIRED_DEVICE_EXTRA)
+ public static final String PAIRED_DEVICE_EXTRA = "paired_device";
+
+ public AdbPairedDevicePreference(PairDevice pairedDevice, Context context) {
+ super(context);
+
+ mPairedDevice = pairedDevice;
+ setWidgetLayoutResource(getWidgetLayoutResourceId());
+ refresh();
+ }
+
+ protected int getWidgetLayoutResourceId() {
+ return R.layout.preference_widget_gear_optional_background;
+ }
+
+ /**
+ * Refreshes the preference bound to the paired device previously passed in.
+ */
+ public void refresh() {
+ setTitle(this, mPairedDevice);
+ }
+
+ public void setPairedDevice(PairDevice pairedDevice) {
+ mPairedDevice = pairedDevice;
+ }
+
+ public PairDevice getPairedDevice() {
+ return mPairedDevice;
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ final View gear = holder.findViewById(R.id.settings_button);
+ final View gearNoBg = holder.findViewById(R.id.settings_button_no_background);
+
+ gear.setVisibility(View.INVISIBLE);
+ gearNoBg.setVisibility(View.VISIBLE);
+ }
+
+ static void setTitle(AdbPairedDevicePreference preference,
+ PairDevice pairedDevice) {
+ preference.setTitle(pairedDevice.getDeviceName());
+ preference.setSummary(pairedDevice.isConnected()
+ ? preference.getContext().getText(R.string.adb_wireless_device_connected_summary)
+ : "");
+ }
+
+ /**
+ * Writes the paired devices bound to this preference to the bundle.
+ *
+ * @param bundle the bundle to write the paired device to
+ */
+ public void savePairedDeviceToExtras(Bundle bundle) {
+ bundle.putParcelable(PAIRED_DEVICE_EXTRA, mPairedDevice);
+ }
+}
+
diff --git a/src/com/android/settings/development/AdbQrcodeScannerFragment.java b/src/com/android/settings/development/AdbQrcodeScannerFragment.java
new file mode 100644
index 0000000..4f8b113
--- /dev/null
+++ b/src/com/android/settings/development/AdbQrcodeScannerFragment.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.debug.AdbManager;
+import android.debug.IAdbManager;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.Size;
+import android.view.LayoutInflater;
+import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.TextView;
+
+import androidx.annotation.StringRes;
+
+import com.android.settings.R;
+import com.android.settings.wifi.dpp.AdbQrCode;
+import com.android.settings.wifi.dpp.WifiDppQrCodeBaseFragment;
+import com.android.settings.wifi.dpp.WifiNetworkConfig;
+import com.android.settings.wifi.qrcode.QrCamera;
+import com.android.settings.wifi.qrcode.QrDecorateView;
+
+/**
+ * Fragment shown when clicking on the "Pair by QR code" preference in
+ * the Wireless Debugging fragment.
+ */
+public class AdbQrcodeScannerFragment extends WifiDppQrCodeBaseFragment implements
+ SurfaceTextureListener,
+ QrCamera.ScannerCallback {
+ private static final String TAG = "AdbQrcodeScannerFrag";
+
+ /** Message sent to hide error message */
+ private static final int MESSAGE_HIDE_ERROR_MESSAGE = 1;
+
+ /** Message sent to show error message */
+ private static final int MESSAGE_SHOW_ERROR_MESSAGE = 2;
+
+ private static final long SHOW_ERROR_MESSAGE_INTERVAL = 10000;
+ private static final long SHOW_SUCCESS_SQUARE_INTERVAL = 1000;
+
+ private QrCamera mCamera;
+ private TextureView mTextureView;
+ private QrDecorateView mDecorateView;
+ private View mQrCameraView;
+ private View mVerifyingView;
+ private TextView mErrorMessage;
+
+ /** QR code data scanned by camera */
+ private AdbQrCode mAdbQrCode;
+ private WifiNetworkConfig mAdbConfig;
+
+ private IAdbManager mAdbManager;
+
+ private IntentFilter mIntentFilter;
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
+ Integer res = intent.getIntExtra(
+ AdbManager.WIRELESS_STATUS_EXTRA,
+ AdbManager.WIRELESS_STATUS_FAIL);
+ if (res.equals(AdbManager.WIRELESS_STATUS_SUCCESS)) {
+ Intent i = new Intent();
+ i.putExtra(
+ WirelessDebuggingFragment.PAIRING_DEVICE_REQUEST_TYPE,
+ WirelessDebuggingFragment.SUCCESS_ACTION);
+ getActivity().setResult(Activity.RESULT_OK, i);
+ getActivity().finish();
+ } else if (res.equals(AdbManager.WIRELESS_STATUS_FAIL)) {
+ Intent i = new Intent();
+ i.putExtra(
+ WirelessDebuggingFragment.PAIRING_DEVICE_REQUEST_TYPE,
+ WirelessDebuggingFragment.FAIL_ACTION);
+ getActivity().setResult(Activity.RESULT_OK, i);
+ getActivity().finish();
+ } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
+ int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
+ Log.i(TAG, "Got Qr pairing code port=" + port);
+ }
+ }
+ }
+ };
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_HIDE_ERROR_MESSAGE:
+ mErrorMessage.setVisibility(View.INVISIBLE);
+ break;
+
+ case MESSAGE_SHOW_ERROR_MESSAGE:
+ final String errorMessage = (String) msg.obj;
+
+ mErrorMessage.setVisibility(View.VISIBLE);
+ mErrorMessage.setText(errorMessage);
+ mErrorMessage.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+
+ // Cancel any pending messages to hide error view and requeue the message so
+ // user has time to see error
+ removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
+ sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,
+ SHOW_ERROR_MESSAGE_INTERVAL);
+ break;
+
+ default:
+ return;
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mIntentFilter = new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+ }
+
+ @Override
+ public final View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.adb_qrcode_scanner_fragment, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ mTextureView = (TextureView) view.findViewById(R.id.preview_view);
+ mTextureView.setSurfaceTextureListener(this);
+
+ mDecorateView = view.findViewById(R.id.decorate_view);
+ setProgressBarShown(false);
+
+ setHeaderIconImageResource(R.drawable.ic_scan_24dp);
+
+ mQrCameraView = view.findViewById(R.id.camera_layout);
+ mVerifyingView = view.findViewById(R.id.verifying_layout);
+
+ setHeaderTitle(R.string.wifi_dpp_scan_qr_code);
+ mSummary.setText(R.string.adb_wireless_qrcode_pairing_description);
+
+ mErrorMessage = view.findViewById(R.id.error_message);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(Context.ADB_SERVICE));
+ getActivity().registerReceiver(mReceiver, mIntentFilter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ getActivity().unregisterReceiver(mReceiver);
+ try {
+ mAdbManager.disablePairing();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to cancel pairing");
+ }
+ getActivity().finish();
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ // Do nothing
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ initCamera(surface);
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ // Do nothing
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ destroyCamera();
+ return true;
+ }
+
+ @Override
+ public Size getViewSize() {
+ return new Size(mTextureView.getWidth(), mTextureView.getHeight());
+ }
+
+ @Override
+ public void setTransform(Matrix transform) {
+ mTextureView.setTransform(transform);
+ }
+
+ @Override
+ public Rect getFramePosition(Size previewSize, int cameraOrientation) {
+ return new Rect(0, 0, previewSize.getHeight(), previewSize.getHeight());
+ }
+
+ @Override
+ public boolean isValid(String qrCode) {
+ try {
+ // WIFI:T:ADB;S:myname;P:mypass;;
+ mAdbQrCode = new AdbQrCode(qrCode);
+ } catch (IllegalArgumentException e) {
+ showErrorMessage(R.string.wifi_dpp_qr_code_is_not_valid_format);
+ return false;
+ }
+
+ mAdbConfig = mAdbQrCode.getAdbNetworkConfig();
+
+ return true;
+ }
+
+ @Override
+ public void handleSuccessfulResult(String qrCode) {
+ destroyCamera();
+ mDecorateView.setFocused(true);
+ mQrCameraView.setVisibility(View.GONE);
+ mVerifyingView.setVisibility(View.VISIBLE);
+ try {
+ mAdbManager.enablePairingByQrCode(mAdbConfig.getSsid(),
+ mAdbConfig.getPreSharedKey());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to enable QR code pairing");
+ getActivity().finish();
+ }
+ }
+
+ @Override
+ public void handleCameraFailure() {
+ destroyCamera();
+ }
+
+ private void initCamera(SurfaceTexture surface) {
+ // Check if the camera has alread been created.
+ if (mCamera == null) {
+ mCamera = new QrCamera(getContext(), this);
+ mCamera.start(surface);
+ }
+ }
+
+ /**
+ * To resume camera decoding task after handshake fail or Wi-Fi connection fail.
+ */
+ private void restartCamera() {
+ if (mCamera == null) {
+ Log.d(TAG, "mCamera is not available for restarting camera");
+ return;
+ }
+
+ if (mCamera.isDecodeTaskAlive()) {
+ mCamera.stop();
+ }
+
+ final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
+ if (surfaceTexture == null) {
+ throw new IllegalStateException("SurfaceTexture is not ready for restarting camera");
+ }
+
+ mCamera.start(surfaceTexture);
+ }
+
+ private void destroyCamera() {
+ if (mCamera != null) {
+ mCamera.stop();
+ mCamera = null;
+ }
+ }
+
+ private void showErrorMessage(@StringRes int messageResId) {
+ final Message message = mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE,
+ getString(messageResId));
+ message.sendToTarget();
+ }
+
+ @Override
+ protected boolean isFooterAvailable() {
+ return false;
+ }
+}
diff --git a/src/com/android/settings/development/AdbWirelessDialog.java b/src/com/android/settings/development/AdbWirelessDialog.java
new file mode 100644
index 0000000..48884b9
--- /dev/null
+++ b/src/com/android/settings/development/AdbWirelessDialog.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+
+/**
+ * Class to show a variety of dialogs for the Wireless debugging
+ * fragment.
+ */
+public class AdbWirelessDialog extends AlertDialog implements
+ AdbWirelessDialogUiBase,
+ DialogInterface.OnClickListener {
+
+ /**
+ * Interface for subscribers to implement in order to listen
+ * to AdbWirelessDialog events.
+ */
+ public interface AdbWirelessDialogListener {
+ /**
+ * Called when the dialog was closed by clicking a negative button.
+ */
+ default void onCancel() {
+ }
+
+ /**
+ * Called when the dialog was closed by clicking a positive button.
+ *
+ * @param dialog the dialog that was closed.
+ */
+ default void onSubmit(AdbWirelessDialog dialog) {
+ }
+
+ /**
+ * Called when the dialog was dismissed.
+ */
+ default void onDismiss() {
+ }
+ }
+
+ private static final String TAG = "AdbWirelessDialog";
+
+ private static final int BUTTON_CANCEL = DialogInterface.BUTTON_NEGATIVE;
+ private static final int BUTTON_SUBMIT = DialogInterface.BUTTON_POSITIVE;
+
+ private final AdbWirelessDialogListener mListener;
+ private final int mMode;
+
+ private View mView;
+ private AdbWirelessDialogController mController;
+
+ /**
+ * Creates a AdbWirelessDialog with no additional style. It displays as a dialog above the
+ * current view.
+ */
+ public static AdbWirelessDialog createModal(
+ Context context,
+ AdbWirelessDialogListener listener,
+ int mode) {
+ return new AdbWirelessDialog(context, listener, mode);
+ }
+
+ AdbWirelessDialog(Context context, AdbWirelessDialogListener listener, int mode) {
+ super(context);
+ mListener = listener;
+ mMode = mode;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ mView = getLayoutInflater().inflate(R.layout.adb_wireless_dialog, null);
+ setView(mView);
+ mController = new AdbWirelessDialogController(this, mView, mMode);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ dismiss();
+ if (mListener != null) {
+ mListener.onDismiss();
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialogInterface, int id) {
+ if (mListener != null) {
+ switch (id) {
+ case BUTTON_CANCEL:
+ mListener.onCancel();
+ break;
+ }
+ }
+ }
+
+ @Override
+ public AdbWirelessDialogController getController() {
+ return mController;
+ }
+
+ @Override
+ public void dispatchSubmit() {
+ if (mListener != null) {
+ mListener.onSubmit(this);
+ }
+ dismiss();
+ }
+
+ @Override
+ public int getMode() {
+ return mMode;
+ }
+
+ @Override
+ public Button getSubmitButton() {
+ return getButton(BUTTON_SUBMIT);
+ }
+
+ @Override
+ public Button getCancelButton() {
+ return getButton(BUTTON_NEGATIVE);
+ }
+
+ @Override
+ public void setSubmitButton(CharSequence text) {
+ setButton(BUTTON_SUBMIT, text, this);
+ }
+
+ @Override
+ public void setCancelButton(CharSequence text) {
+ setButton(BUTTON_NEGATIVE, text, this);
+ }
+}
diff --git a/src/com/android/settings/development/AdbWirelessDialogController.java b/src/com/android/settings/development/AdbWirelessDialogController.java
new file mode 100644
index 0000000..136c7b3
--- /dev/null
+++ b/src/com/android/settings/development/AdbWirelessDialogController.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+/**
+ * The class for allowing UIs like {@link AdbWirelessDialog} and {@link AdbWirelessDialogUiBase} to
+ * share the logic for controlling buttons, text fields, etc.
+ */
+public class AdbWirelessDialogController {
+ private static final String TAG = "AdbWirelessDialogCtrl";
+
+ private final AdbWirelessDialogUiBase mUi;
+ private final View mView;
+
+ private int mMode;
+
+ // The dialog for showing the six-digit code
+ private TextView mPairingCodeTitle;
+ private TextView mSixDigitCode;
+ private TextView mIpAddr;
+
+ // The dialog for showing pairing failed message
+ private TextView mFailedMsg;
+
+ private Context mContext;
+
+ public AdbWirelessDialogController(AdbWirelessDialogUiBase parent, View view,
+ int mode) {
+ mUi = parent;
+ mView = view;
+ mMode = mode;
+
+ mContext = mUi.getContext();
+ final Resources res = mContext.getResources();
+
+ mSixDigitCode = mView.findViewById(R.id.pairing_code);
+ mIpAddr = mView.findViewById(R.id.ip_addr);
+
+ switch (mMode) {
+ case AdbWirelessDialogUiBase.MODE_PAIRING:
+ String title = res.getString(R.string.adb_pairing_device_dialog_title);
+ mUi.setTitle(title);
+ mView.findViewById(R.id.l_pairing_six_digit).setVisibility(View.VISIBLE);
+ mUi.setCancelButton(res.getString(R.string.cancel));
+ mUi.setCanceledOnTouchOutside(false);
+ break;
+ case AdbWirelessDialogUiBase.MODE_PAIRING_FAILED:
+ String msg = res.getString(R.string.adb_pairing_device_dialog_failed_msg);
+ mUi.setTitle(R.string.adb_pairing_device_dialog_failed_title);
+ mView.findViewById(R.id.l_pairing_failed).setVisibility(View.VISIBLE);
+ mFailedMsg = (TextView) mView.findViewById(R.id.pairing_failed_label);
+ mFailedMsg.setText(msg);
+ mUi.setSubmitButton(res.getString(R.string.okay));
+ break;
+ case AdbWirelessDialogUiBase.MODE_QRCODE_FAILED:
+ mUi.setTitle(R.string.adb_pairing_device_dialog_failed_title);
+ mView.findViewById(R.id.l_qrcode_pairing_failed).setVisibility(View.VISIBLE);
+ mUi.setSubmitButton(res.getString(R.string.okay));
+ break;
+ }
+
+ // After done view show and hide, request focus from parent view
+ mView.findViewById(R.id.l_adbwirelessdialog).requestFocus();
+ }
+
+ /**
+ * Set the pairing code UI text field to code.
+ *
+ * @param code the pairing code string
+ */
+ public void setPairingCode(String code) {
+ mSixDigitCode.setText(code);
+ }
+
+ /**
+ * Set the Ip address UI text field to ipAddr.
+ *
+ * @param ipAddr the ip address string
+ */
+ public void setIpAddr(String ipAddr) {
+ mIpAddr.setText(ipAddr);
+ }
+}
diff --git a/src/com/android/settings/development/AdbWirelessDialogUiBase.java b/src/com/android/settings/development/AdbWirelessDialogUiBase.java
new file mode 100644
index 0000000..b2fc01c
--- /dev/null
+++ b/src/com/android/settings/development/AdbWirelessDialogUiBase.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.widget.Button;
+
+/**
+ * Foundation interface glues between Activities and UIs like {@link AdbWirelessDialog}.
+ */
+public interface AdbWirelessDialogUiBase {
+ /**
+ * Dialog shown when pairing a device via six-digit code.
+ */
+ int MODE_PAIRING = 0;
+ /**
+ * Dialog shown when connecting to a paired device failed.
+ */
+ int MODE_CONNECTION_FAILED = 1;
+ /**
+ * Dialog shown when pairing failed.
+ */
+ int MODE_PAIRING_FAILED = 2;
+
+ /**
+ * Dialog shown when QR code pairing failed.
+ */
+ int MODE_QRCODE_FAILED = 3;
+
+ /**
+ * Gets the context for the dialog.
+ *
+ * @return the context for the dialog
+ */
+ Context getContext();
+
+ /**
+ * Gets the controller for the dialog.
+ *
+ * @return the controller for the dialog.
+ */
+ AdbWirelessDialogController getController();
+
+ /**
+ * Gets the layout for the dialog.
+ *
+ * @return the {@link LayoutInflater} for the dialog
+ */
+ LayoutInflater getLayoutInflater();
+
+ /**
+ * Gets the dialog mode/ID.
+ *
+ * @return the mode of the dialog
+ */
+ int getMode();
+
+ /**
+ * Sends a submit command to the dialog.
+ */
+ void dispatchSubmit();
+
+ /**
+ * Enables if user can cancel a dialog by clicking outside of the dialog.
+ *
+ * @param cancel The flag indicating if can cancel by clicking outside
+ */
+ void setCanceledOnTouchOutside(boolean cancel);
+
+ /**
+ * Sets the title of the dialog.
+ *
+ * @param id the string id
+ */
+ void setTitle(int id);
+
+ /**
+ * Sets the title of the dialog.
+ *
+ * @param title the title string
+ */
+ void setTitle(CharSequence title);
+
+ /**
+ * Sets the text for the submit button.
+ *
+ * @param text the submit text
+ */
+ void setSubmitButton(CharSequence text);
+
+ /**
+ * Sets the text for the cancel button.
+ *
+ * @param text the cancel text
+ */
+ void setCancelButton(CharSequence text);
+
+ /**
+ * Gets the button widget for the submit button.
+ *
+ * @return the submit {@link Button} widget
+ */
+ Button getSubmitButton();
+
+ /**
+ * Gets the button widget for the cancel button.
+ *
+ * @return the cancel {@link Button} widget
+ */
+ Button getCancelButton();
+}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 1c08131..f3f2a3a 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -443,6 +443,7 @@
controllers.add(new SelectDSUPreferenceController(context));
controllers.add(new AdbPreferenceController(context, fragment));
controllers.add(new ClearAdbKeysPreferenceController(context, fragment));
+ controllers.add(new WirelessDebuggingPreferenceController(context, lifecycle));
controllers.add(new LocalTerminalPreferenceController(context));
controllers.add(new BugReportInPowerPreferenceController(context));
controllers.add(new AutomaticSystemServerHeapDumpPreferenceController(context));
@@ -451,6 +452,7 @@
controllers.add(new SelectDebugAppPreferenceController(context, fragment));
controllers.add(new WaitForDebuggerPreferenceController(context));
controllers.add(new EnableGpuDebugLayersPreferenceController(context));
+ controllers.add(new EnableVerboseVendorLoggingPreferenceController(context));
controllers.add(new VerifyAppsOverUsbPreferenceController(context));
controllers.add(new ArtVerifierPreferenceController(context));
controllers.add(new LogdSizePreferenceController(context));
@@ -468,6 +470,7 @@
controllers.add(new BluetoothMapVersionPreferenceController(context));
controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
controllers.add(new BluetoothMaxConnectedAudioDevicesPreferenceController(context));
+ controllers.add(new EnhancedConnectivityPreferenceController(context));
controllers.add(new ShowTapsPreferenceController(context));
controllers.add(new PointerLocationPreferenceController(context));
controllers.add(new ShowSurfaceUpdatesPreferenceController(context));
diff --git a/src/com/android/settings/development/EnableVerboseVendorLoggingPreferenceController.java b/src/com/android/settings/development/EnableVerboseVendorLoggingPreferenceController.java
new file mode 100644
index 0000000..e1db74d
--- /dev/null
+++ b/src/com/android/settings/development/EnableVerboseVendorLoggingPreferenceController.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.dumpstate.V1_0.IDumpstateDevice;
+import android.os.RemoteException;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+import java.util.NoSuchElementException;
+
+public class EnableVerboseVendorLoggingPreferenceController
+ extends DeveloperOptionsPreferenceController
+ implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+ private static final String TAG = "EnableVerboseVendorLoggingPreferenceController";
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final String ENABLE_VERBOSE_VENDOR_LOGGING_KEY = "enable_verbose_vendor_logging";
+ private static final int DUMPSTATE_HAL_VERSION_UNKNOWN = -1;
+ private static final int DUMPSTATE_HAL_VERSION_1_0 = 0;
+ private static final int DUMPSTATE_HAL_VERSION_1_1 = 1;
+
+ private int mDumpstateHalVersion;
+
+ public EnableVerboseVendorLoggingPreferenceController(Context context) {
+ super(context);
+ mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_UNKNOWN;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return ENABLE_VERBOSE_VENDOR_LOGGING_KEY;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ // Only show preference when IDumpstateDevice v1.1 is avalaible
+ // This is temperary strategy that may change later.
+ return isIDumpstateDeviceV1_1ServiceAvailable();
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean isEnabled = (Boolean) newValue;
+ setVerboseLoggingEnabled(isEnabled);
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final boolean enabled = getVerboseLoggingEnabled();
+ ((SwitchPreference) mPreference).setChecked(enabled);
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchDisabled() {
+ super.onDeveloperOptionsSwitchDisabled();
+ setVerboseLoggingEnabled(false);
+ ((SwitchPreference) mPreference).setChecked(false);
+ }
+
+ @VisibleForTesting
+ boolean isIDumpstateDeviceV1_1ServiceAvailable() {
+ IDumpstateDevice service = getDumpstateDeviceService();
+ if (service == null) {
+ if (DBG) Log.d(TAG, "IDumpstateDevice service is not available.");
+ }
+ return service != null && mDumpstateHalVersion >= DUMPSTATE_HAL_VERSION_1_1;
+ }
+
+ @VisibleForTesting
+ void setVerboseLoggingEnabled(boolean enable) {
+ IDumpstateDevice service = getDumpstateDeviceService();
+
+ if (service == null || mDumpstateHalVersion < DUMPSTATE_HAL_VERSION_1_1) {
+ if (DBG) Log.d(TAG, "setVerboseLoggingEnabled not supported.");
+ return;
+ }
+
+ try {
+ android.hardware.dumpstate.V1_1.IDumpstateDevice service11 =
+ (android.hardware.dumpstate.V1_1.IDumpstateDevice) service;
+ if (service11 != null) {
+ service11.setVerboseLoggingEnabled(enable);
+ }
+ } catch (RemoteException | RuntimeException e) {
+ if (DBG) Log.e(TAG, "setVerboseLoggingEnabled fail: " + e);
+ }
+ }
+
+ @VisibleForTesting
+ boolean getVerboseLoggingEnabled() {
+ IDumpstateDevice service = getDumpstateDeviceService();
+
+ if (service == null || mDumpstateHalVersion < DUMPSTATE_HAL_VERSION_1_1) {
+ if (DBG) Log.d(TAG, "getVerboseLoggingEnabled not supported.");
+ return false;
+ }
+
+ try {
+ android.hardware.dumpstate.V1_1.IDumpstateDevice service11 =
+ (android.hardware.dumpstate.V1_1.IDumpstateDevice) service;
+ if (service11 != null) {
+ return service11.getVerboseLoggingEnabled();
+ }
+ } catch (RemoteException | RuntimeException e) {
+ if (DBG) Log.e(TAG, "getVerboseLoggingEnabled fail: " + e);
+ }
+ return false;
+ }
+
+ /** Return a {@IDumpstateDevice} instance or null if service is not available. */
+ @VisibleForTesting
+ @Nullable IDumpstateDevice getDumpstateDeviceService() {
+ IDumpstateDevice service = null;
+ try {
+ service = android.hardware.dumpstate.V1_1.IDumpstateDevice
+ .getService(true /* retry */);
+ mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_1_1;
+ } catch (NoSuchElementException | RemoteException e) {
+ }
+
+ if (service == null) {
+ try {
+ service = android.hardware.dumpstate.V1_0.IDumpstateDevice
+ .getService(true /* retry */);
+ mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_1_0;
+ } catch (NoSuchElementException | RemoteException e) {
+ }
+ }
+
+ if (service == null) {
+ mDumpstateHalVersion = DUMPSTATE_HAL_VERSION_UNKNOWN;
+ }
+ return service;
+ }
+}
diff --git a/src/com/android/settings/development/EnhancedConnectivityPreferenceController.java b/src/com/android/settings/development/EnhancedConnectivityPreferenceController.java
new file mode 100644
index 0000000..2dbd7d1
--- /dev/null
+++ b/src/com/android/settings/development/EnhancedConnectivityPreferenceController.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * Preference controller for Enhanced Connectivity feature
+ */
+public class EnhancedConnectivityPreferenceController extends
+ DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
+ PreferenceControllerMixin {
+
+ private static final String ENHANCED_CONNECTIVITY_KEY = "enhanced_connectivity";
+
+ @VisibleForTesting
+ static final int ENHANCED_CONNECTIVITY_ON = 1;
+ // default is enhanced connectivity disabled.
+ @VisibleForTesting
+ static final int ENHANCED_CONNECTIVITY_OFF = 0;
+
+ public EnhancedConnectivityPreferenceController(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object o) {
+ final boolean isEnabled = (Boolean) o;
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
+ isEnabled
+ ? ENHANCED_CONNECTIVITY_ON
+ : ENHANCED_CONNECTIVITY_OFF);
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return ENHANCED_CONNECTIVITY_KEY;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final int enhancedConnectivityEnabled = Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
+ ENHANCED_CONNECTIVITY_OFF);
+ ((SwitchPreference) mPreference).setChecked(
+ enhancedConnectivityEnabled == ENHANCED_CONNECTIVITY_ON);
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchDisabled() {
+ super.onDeveloperOptionsSwitchDisabled();
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
+ ENHANCED_CONNECTIVITY_OFF);
+ ((SwitchPreference) mPreference).setChecked(false);
+ }
+}
diff --git a/src/com/android/settings/development/WirelessDebuggingEnabler.java b/src/com/android/settings/development/WirelessDebuggingEnabler.java
new file mode 100644
index 0000000..51b81f6
--- /dev/null
+++ b/src/com/android/settings/development/WirelessDebuggingEnabler.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.settings.widget.SwitchWidgetController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+/**
+ * Class to control the switch bar in the wireless debugging fragment.
+ */
+public class WirelessDebuggingEnabler implements SwitchWidgetController.OnSwitchChangeListener,
+ LifecycleObserver, OnResume, OnPause {
+ private static final String TAG = "WirelessDebuggingEnabler";
+
+ private final SwitchWidgetController mSwitchWidget;
+ private Context mContext;
+ private boolean mListeningToOnSwitchChange = false;
+ private OnEnabledListener mListener;
+ private final ContentResolver mContentResolver;
+ private final ContentObserver mSettingsObserver;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ public WirelessDebuggingEnabler(Context context, SwitchWidgetController switchWidget,
+ OnEnabledListener listener, Lifecycle lifecycle) {
+ mContext = context;
+ mSwitchWidget = switchWidget;
+ mSwitchWidget.setListener(this);
+ mSwitchWidget.setupView();
+
+ mListener = listener;
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+
+ mContentResolver = context.getContentResolver();
+ mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ Log.i(TAG, "ADB_WIFI_ENABLED=" + isAdbWifiEnabled());
+ onWirelessDebuggingEnabled(isAdbWifiEnabled());
+ }
+ };
+ }
+
+ private boolean isAdbWifiEnabled() {
+ return Settings.Global.getInt(mContentResolver, Settings.Global.ADB_WIFI_ENABLED,
+ AdbPreferenceController.ADB_SETTING_OFF)
+ != AdbPreferenceController.ADB_SETTING_OFF;
+ }
+
+ /**
+ * Tears down the switch controller for the wireless debugging switch.
+ */
+ public void teardownSwitchController() {
+ if (mListeningToOnSwitchChange) {
+ mSwitchWidget.stopListening();
+ mListeningToOnSwitchChange = false;
+ }
+ mSwitchWidget.teardownView();
+ }
+
+ @Override
+ public void onResume() {
+ if (!mListeningToOnSwitchChange) {
+ mSwitchWidget.startListening();
+ mListeningToOnSwitchChange = true;
+ }
+ onWirelessDebuggingEnabled(isAdbWifiEnabled());
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED), false,
+ mSettingsObserver);
+ }
+
+ @Override
+ public void onPause() {
+ if (mListeningToOnSwitchChange) {
+ mSwitchWidget.stopListening();
+ mListeningToOnSwitchChange = false;
+ }
+ mContentResolver.unregisterContentObserver(mSettingsObserver);
+ }
+
+ private void onWirelessDebuggingEnabled(boolean enabled) {
+ mSwitchWidget.setChecked(enabled);
+ if (mListener != null) {
+ mListener.onEnabled(enabled);
+ }
+ }
+
+ protected void writeAdbWifiSetting(boolean enabled) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.ADB_WIFI_ENABLED, enabled ? AdbPreferenceController.ADB_SETTING_ON
+ : AdbPreferenceController.ADB_SETTING_OFF);
+ }
+
+ @Override
+ public boolean onSwitchToggled(boolean isChecked) {
+ writeAdbWifiSetting(isChecked);
+ return true;
+ }
+
+ /**
+ * Interface for subscribers to implement in order to listen for
+ * wireless debugging state changes.
+ */
+ public interface OnEnabledListener {
+ /**
+ * Called when wireless debugging state changes.
+ *
+ * @param enabled the state of wireless debugging
+ */
+ void onEnabled(boolean enabled);
+ }
+}
diff --git a/src/com/android/settings/development/WirelessDebuggingFragment.java b/src/com/android/settings/development/WirelessDebuggingFragment.java
new file mode 100644
index 0000000..2edadcc
--- /dev/null
+++ b/src/com/android/settings/development/WirelessDebuggingFragment.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2020 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.development;
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.debug.AdbManager;
+import android.debug.IAdbManager;
+import android.debug.PairDevice;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.widget.SwitchBarController;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.development.DevelopmentSettingsEnabler;
+import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.widget.FooterPreference;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Fragment shown when clicking in the "Wireless Debugging" preference in
+ * the developer options.
+ */
+@SearchIndexable
+public class WirelessDebuggingFragment extends DashboardFragment
+ implements WirelessDebuggingEnabler.OnEnabledListener {
+
+ private static final String TAG = "WirelessDebuggingFrag";
+
+ // Activity result from clicking on a paired device.
+ private static final int PAIRED_DEVICE_REQUEST = 0;
+ public static final String PAIRED_DEVICE_REQUEST_TYPE = "request_type";
+ public static final int FORGET_ACTION = 0;
+
+ // Activity result from pairing a device.
+ private static final int PAIRING_DEVICE_REQUEST = 1;
+ public static final String PAIRING_DEVICE_REQUEST_TYPE = "request_type_pairing";
+ public static final int SUCCESS_ACTION = 0;
+ public static final int FAIL_ACTION = 1;
+
+ public static final String PAIRED_DEVICE_EXTRA = "paired_device";
+ public static final String DEVICE_NAME_EXTRA = "device_name";
+ public static final String IP_ADDR_EXTRA = "ip_addr";
+
+ private WirelessDebuggingEnabler mWifiDebuggingEnabler;
+
+ private static AdbIpAddressPreferenceController sAdbIpAddressPreferenceController;
+ // UI components
+ private static final String PREF_KEY_ADB_DEVICE_NAME = "adb_device_name_pref";
+ private static final String PREF_KEY_ADB_IP_ADDR = "adb_ip_addr_pref";
+ private static final String PREF_KEY_PAIRING_METHODS_CATEGORY = "adb_pairing_methods_category";
+ private static final String PREF_KEY_ADB_QRCODE_PAIRING = "adb_pair_method_qrcode_pref";
+ private static final String PREF_KEY_ADB_CODE_PAIRING = "adb_pair_method_code_pref";
+ private static final String PREF_KEY_PAIRED_DEVICES_CATEGORY = "adb_paired_devices_category";
+ private static final String PREF_KEY_FOOTER_CATEGORY = "adb_wireless_footer_category";
+
+ private Preference mDeviceNamePreference;
+ private Preference mIpAddrPreference;
+
+ private PreferenceCategory mPairingMethodsCategory;
+ private Preference mQrcodePairingPreference;
+ private Preference mCodePairingPreference;
+
+ private PreferenceCategory mPairedDevicesCategory;
+
+ private PreferenceCategory mFooterCategory;
+ private FooterPreference mOffMessagePreference;
+
+ // Map of paired devices, with the device GUID is the key
+ private Map<String, AdbPairedDevicePreference> mPairedDevicePreferences;
+
+ private IAdbManager mAdbManager;
+ private int mConnectionPort;
+
+ class PairingCodeDialogListener implements AdbWirelessDialog.AdbWirelessDialogListener {
+ @Override
+ public void onDismiss() {
+ Log.i(TAG, "onDismiss");
+ mPairingCodeDialog = null;
+ try {
+ mAdbManager.disablePairing();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to cancel pairing");
+ }
+ }
+ }
+ final PairingCodeDialogListener mPairingCodeDialogListener = new PairingCodeDialogListener();
+ AdbWirelessDialog mPairingCodeDialog;
+
+ private IntentFilter mIntentFilter;
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION.equals(action)) {
+ Map<String, PairDevice> newPairedDevicesList =
+ (HashMap<String, PairDevice>) intent.getSerializableExtra(
+ AdbManager.WIRELESS_DEVICES_EXTRA);
+ updatePairedDevicePreferences(newPairedDevicesList);
+ } else if (AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION.equals(action)) {
+ int status = intent.getIntExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+ AdbManager.WIRELESS_STATUS_DISCONNECTED);
+ if (status == AdbManager.WIRELESS_STATUS_CONNECTED) {
+ int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
+ Log.i(TAG, "Got adbwifi port=" + port);
+ } else {
+ Log.i(TAG, "adbwifi server disconnected");
+ }
+ } else if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
+ Integer res = intent.getIntExtra(
+ AdbManager.WIRELESS_STATUS_EXTRA,
+ AdbManager.WIRELESS_STATUS_FAIL);
+
+ if (res.equals(AdbManager.WIRELESS_STATUS_PAIRING_CODE)) {
+ String pairingCode = intent.getStringExtra(
+ AdbManager.WIRELESS_PAIRING_CODE_EXTRA);
+ if (mPairingCodeDialog != null) {
+ mPairingCodeDialog.getController().setPairingCode(pairingCode);
+ }
+ } else if (res.equals(AdbManager.WIRELESS_STATUS_SUCCESS)) {
+ removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
+ mPairingCodeDialog = null;
+ } else if (res.equals(AdbManager.WIRELESS_STATUS_FAIL)) {
+ removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
+ mPairingCodeDialog = null;
+ showDialog(AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
+ } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
+ int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
+ Log.i(TAG, "Got pairing code port=" + port);
+ String ipAddr = sAdbIpAddressPreferenceController.getIpv4Address() + ":" + port;
+ if (mPairingCodeDialog != null) {
+ mPairingCodeDialog.getController().setIpAddr(ipAddr);
+ }
+ }
+ }
+ }
+ };
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ final SettingsActivity activity = (SettingsActivity) getActivity();
+ mWifiDebuggingEnabler = new WirelessDebuggingEnabler(activity,
+ new SwitchBarController(activity.getSwitchBar()), this,
+ getSettingsLifecycle());
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ addPreferences();
+ mIntentFilter = new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+ mIntentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+ }
+
+ private void addPreferences() {
+ mDeviceNamePreference =
+ (Preference) findPreference(PREF_KEY_ADB_DEVICE_NAME);
+ mIpAddrPreference =
+ (Preference) findPreference(PREF_KEY_ADB_IP_ADDR);
+ mPairingMethodsCategory =
+ (PreferenceCategory) findPreference(PREF_KEY_PAIRING_METHODS_CATEGORY);
+ mCodePairingPreference =
+ (Preference) findPreference(PREF_KEY_ADB_CODE_PAIRING);
+ mCodePairingPreference.setOnPreferenceClickListener(preference -> {
+ showDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
+ return true;
+ });
+ mQrcodePairingPreference =
+ (Preference) findPreference(PREF_KEY_ADB_QRCODE_PAIRING);
+ mQrcodePairingPreference.setOnPreferenceClickListener(preference -> {
+ launchQrcodeScannerFragment();
+ return true;
+ });
+
+ mPairedDevicesCategory =
+ (PreferenceCategory) findPreference(PREF_KEY_PAIRED_DEVICES_CATEGORY);
+ mFooterCategory =
+ (PreferenceCategory) findPreference(PREF_KEY_FOOTER_CATEGORY);
+
+ mOffMessagePreference =
+ new FooterPreference(mFooterCategory.getContext());
+ final CharSequence title = getText(R.string.adb_wireless_list_empty_off);
+ mOffMessagePreference.setTitle(title);
+ mFooterCategory.addPreference(mOffMessagePreference);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ mWifiDebuggingEnabler.teardownSwitchController();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ getActivity().registerReceiver(mReceiver, mIntentFilter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
+ getActivity().unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode == PAIRED_DEVICE_REQUEST) {
+ handlePairedDeviceRequest(resultCode, data);
+ } else if (requestCode == PAIRING_DEVICE_REQUEST) {
+ handlePairingDeviceRequest(resultCode, data);
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.SETTINGS_ADB_WIRELESS;
+ }
+
+ @Override
+ public int getDialogMetricsCategory(int dialogId) {
+ return SettingsEnums.ADB_WIRELESS_DEVICE_PAIRING_DIALOG;
+ }
+
+ @Override
+ public Dialog onCreateDialog(int dialogId) {
+ Dialog d = AdbWirelessDialog.createModal(getActivity(),
+ dialogId == AdbWirelessDialogUiBase.MODE_PAIRING
+ ? mPairingCodeDialogListener : null, dialogId);
+ if (dialogId == AdbWirelessDialogUiBase.MODE_PAIRING) {
+ mPairingCodeDialog = (AdbWirelessDialog) d;
+ try {
+ mAdbManager.enablePairingByPairingCode();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to enable pairing");
+ mPairingCodeDialog = null;
+ d = AdbWirelessDialog.createModal(getActivity(), null,
+ AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
+ }
+ }
+ if (d != null) {
+ return d;
+ }
+ return super.onCreateDialog(dialogId);
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.adb_wireless_settings;
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ return buildPreferenceControllers(context, getActivity(), this /* fragment */,
+ getSettingsLifecycle());
+ }
+
+ private static List<AbstractPreferenceController> buildPreferenceControllers(
+ Context context, Activity activity, WirelessDebuggingFragment fragment,
+ Lifecycle lifecycle) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ sAdbIpAddressPreferenceController =
+ new AdbIpAddressPreferenceController(context, lifecycle);
+ controllers.add(sAdbIpAddressPreferenceController);
+
+ return controllers;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public void onEnabled(boolean enabled) {
+ if (enabled) {
+ showDebuggingPreferences();
+ mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(
+ Context.ADB_SERVICE));
+ try {
+ Map<String, PairDevice> newList = mAdbManager.getPairedDevices();
+ updatePairedDevicePreferences(newList);
+ mConnectionPort = mAdbManager.getAdbWirelessPort();
+ if (mConnectionPort > 0) {
+ Log.i(TAG, "onEnabled(): connect_port=" + mConnectionPort);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to request the paired list for Adb wireless");
+ }
+ sAdbIpAddressPreferenceController.updateState(mIpAddrPreference);
+ } else {
+ showOffMessage();
+ }
+ }
+
+ private void showOffMessage() {
+ mDeviceNamePreference.setVisible(false);
+ mIpAddrPreference.setVisible(false);
+ mPairingMethodsCategory.setVisible(false);
+ mPairedDevicesCategory.setVisible(false);
+ mFooterCategory.setVisible(true);
+ }
+
+ private void showDebuggingPreferences() {
+ mDeviceNamePreference.setVisible(true);
+ mIpAddrPreference.setVisible(true);
+ mPairingMethodsCategory.setVisible(true);
+ mPairedDevicesCategory.setVisible(true);
+ mFooterCategory.setVisible(false);
+ }
+
+ private void updatePairedDevicePreferences(Map<String, PairDevice> newList) {
+ // TODO(joshuaduong): Move the non-UI stuff into another thread
+ // as the processing could take some time.
+ if (newList == null) {
+ mPairedDevicesCategory.removeAll();
+ return;
+ }
+ if (mPairedDevicePreferences == null) {
+ mPairedDevicePreferences = new HashMap<String, AdbPairedDevicePreference>();
+ }
+ if (mPairedDevicePreferences.isEmpty()) {
+ for (Map.Entry<String, PairDevice> entry : newList.entrySet()) {
+ AdbPairedDevicePreference p =
+ new AdbPairedDevicePreference(entry.getValue(),
+ mPairedDevicesCategory.getContext());
+ mPairedDevicePreferences.put(
+ entry.getKey(),
+ p);
+ p.setOnPreferenceClickListener(preference -> {
+ AdbPairedDevicePreference pref =
+ (AdbPairedDevicePreference) preference;
+ launchPairedDeviceDetailsFragment(pref);
+ return true;
+ });
+ mPairedDevicesCategory.addPreference(p);
+ }
+ } else {
+ // Remove any devices no longer on the newList
+ mPairedDevicePreferences.entrySet().removeIf(entry -> {
+ if (newList.get(entry.getKey()) == null) {
+ mPairedDevicesCategory.removePreference(entry.getValue());
+ return true;
+ } else {
+ // It is in the newList. Just update the PairDevice value
+ AdbPairedDevicePreference p =
+ entry.getValue();
+ p.setPairedDevice(newList.get(entry.getKey()));
+ p.refresh();
+ return false;
+ }
+ });
+ // Add new devices if any.
+ for (Map.Entry<String, PairDevice> entry :
+ newList.entrySet()) {
+ if (mPairedDevicePreferences.get(entry.getKey()) == null) {
+ AdbPairedDevicePreference p =
+ new AdbPairedDevicePreference(entry.getValue(),
+ mPairedDevicesCategory.getContext());
+ mPairedDevicePreferences.put(
+ entry.getKey(),
+ p);
+ p.setOnPreferenceClickListener(preference -> {
+ AdbPairedDevicePreference pref =
+ (AdbPairedDevicePreference) preference;
+ launchPairedDeviceDetailsFragment(pref);
+ return true;
+ });
+ mPairedDevicesCategory.addPreference(p);
+ }
+ }
+ }
+ }
+
+ private void launchPairedDeviceDetailsFragment(AdbPairedDevicePreference p) {
+ // For sending to the device details fragment.
+ p.savePairedDeviceToExtras(p.getExtras());
+ new SubSettingLauncher(getContext())
+ .setTitleRes(R.string.adb_wireless_device_details_title)
+ .setDestination(AdbDeviceDetailsFragment.class.getName())
+ .setArguments(p.getExtras())
+ .setSourceMetricsCategory(getMetricsCategory())
+ .setResultListener(this, PAIRED_DEVICE_REQUEST)
+ .launch();
+ }
+
+ void handlePairedDeviceRequest(int result, Intent data) {
+ if (result != Activity.RESULT_OK) {
+ return;
+ }
+
+ Log.i(TAG, "Processing paired device request");
+ int requestType = data.getIntExtra(PAIRED_DEVICE_REQUEST_TYPE, -1);
+
+ PairDevice p;
+
+ switch (requestType) {
+ case FORGET_ACTION:
+ try {
+ p = (PairDevice) data.getParcelableExtra(PAIRED_DEVICE_EXTRA);
+ mAdbManager.unpairDevice(p.getGuid());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to forget the device");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ void handlePairingDeviceRequest(int result, Intent data) {
+ if (result != Activity.RESULT_OK) {
+ return;
+ }
+
+ int requestType = data.getIntExtra(PAIRING_DEVICE_REQUEST_TYPE, -1);
+ switch (requestType) {
+ case FAIL_ACTION:
+ showDialog(AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private String getDeviceName() {
+ // Keep device name in sync with Settings > About phone > Device name
+ String deviceName = Settings.Global.getString(getContext().getContentResolver(),
+ Settings.Global.DEVICE_NAME);
+ if (deviceName == null) {
+ deviceName = Build.MODEL;
+ }
+ return deviceName;
+ }
+
+ private void launchQrcodeScannerFragment() {
+ new SubSettingLauncher(getContext())
+ .setDestination(AdbQrcodeScannerFragment.class.getName())
+ .setSourceMetricsCategory(getMetricsCategory())
+ .setResultListener(this, PAIRING_DEVICE_REQUEST)
+ .launch();
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.development_tile_settings) {
+
+ @Override
+ protected boolean isPageSearchEnabled(Context context) {
+ return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context);
+ }
+ };
+}
diff --git a/src/com/android/settings/development/WirelessDebuggingPreferenceController.java b/src/com/android/settings/development/WirelessDebuggingPreferenceController.java
new file mode 100644
index 0000000..81575d2
--- /dev/null
+++ b/src/com/android/settings/development/WirelessDebuggingPreferenceController.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.debug.IAdbManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.MasterSwitchPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * This controls the master switch controller in the developer options page for
+ * "Wireless debugging".
+ */
+public class WirelessDebuggingPreferenceController extends DeveloperOptionsPreferenceController
+ implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin,
+ LifecycleObserver, OnResume, OnPause {
+ private static final String TAG = "WirelessDebugPrefCtrl";
+ private final IAdbManager mAdbManager;
+ private final ContentResolver mContentResolver;
+ private final ContentObserver mSettingsObserver;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ public static final String KEY_TOGGLE_ADB_WIRELESS = "toggle_adb_wireless";
+
+ public WirelessDebuggingPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context);
+
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(Context.ADB_SERVICE));
+ mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateState(mPreference);
+ }
+ };
+ mContentResolver = context.getContentResolver();
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ try {
+ return mAdbManager.isAdbWifiSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to check if adb wifi is supported.", e);
+ }
+
+ return false;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_TOGGLE_ADB_WIRELESS;
+ }
+
+ /**
+ * Called when developer options is enabled and the preference is available
+ */
+ @Override
+ protected void onDeveloperOptionsSwitchEnabled() {
+ super.onDeveloperOptionsSwitchEnabled();
+ mPreference.setEnabled(true);
+ }
+
+ /**
+ * Called when developer options is disabled and the preference is available
+ */
+ @Override
+ protected void onDeveloperOptionsSwitchDisabled() {
+ super.onDeveloperOptionsSwitchDisabled();
+ mPreference.setEnabled(false);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.ADB_WIFI_ENABLED,
+ AdbPreferenceController.ADB_SETTING_OFF);
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED), false,
+ mSettingsObserver);
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mSettingsObserver);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ boolean enabled = Settings.Global.getInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, AdbPreferenceController.ADB_SETTING_OFF)
+ != AdbPreferenceController.ADB_SETTING_OFF;
+ ((MasterSwitchPreference) preference).setChecked(enabled);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean enabled = (Boolean) newValue;
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.ADB_WIFI_ENABLED,
+ enabled ? AdbPreferenceController.ADB_SETTING_ON
+ : AdbPreferenceController.ADB_SETTING_OFF);
+ return true;
+ }
+}
diff --git a/src/com/android/settings/development/graphicsdriver/GraphicsDriverAppPreferenceController.java b/src/com/android/settings/development/graphicsdriver/GraphicsDriverAppPreferenceController.java
index c1376a8..2013b45 100644
--- a/src/com/android/settings/development/graphicsdriver/GraphicsDriverAppPreferenceController.java
+++ b/src/com/android/settings/development/graphicsdriver/GraphicsDriverAppPreferenceController.java
@@ -26,7 +26,9 @@
import android.content.res.Resources;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemProperties;
import android.provider.Settings;
+import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
import androidx.preference.ListPreference;
@@ -58,15 +60,19 @@
GraphicsDriverContentObserver.OnGraphicsDriverContentChangedListener, LifecycleObserver,
OnStart, OnStop {
+ private static final String PROPERTY_GFX_DRIVER_GAME = "ro.gfx.driver.0";
+ private static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1";
+
private final Context mContext;
private final ContentResolver mContentResolver;
- private final CharSequence[] mEntryList;
private final String mPreferenceTitle;
private final String mPreferenceDefault;
private final String mPreferenceGameDriver;
private final String mPreferencePrereleaseDriver;
private final String mPreferenceSystem;
@VisibleForTesting
+ CharSequence[] mEntryList;
+ @VisibleForTesting
GraphicsDriverContentObserver mGraphicsDriverContentObserver;
private final List<AppInfo> mAppInfos;
@@ -85,7 +91,6 @@
new GraphicsDriverContentObserver(new Handler(Looper.getMainLooper()), this);
final Resources resources = context.getResources();
- mEntryList = resources.getStringArray(R.array.graphics_driver_app_preference_values);
mPreferenceTitle = resources.getString(R.string.graphics_driver_app_preference_title);
mPreferenceDefault = resources.getString(R.string.graphics_driver_app_preference_default);
mPreferenceGameDriver =
@@ -93,6 +98,7 @@
mPreferencePrereleaseDriver =
resources.getString(R.string.graphics_driver_app_preference_prerelease_driver);
mPreferenceSystem = resources.getString(R.string.graphics_driver_app_preference_system);
+ mEntryList = constructEntryList();
// TODO: Move this task to background if there's potential ANR/Jank.
// Update the UI when all the app infos are ready.
@@ -189,6 +195,28 @@
updateState(mPreferenceGroup);
}
+ /**
+ * Constructs and returns a list of graphics driver choices.
+ */
+ public CharSequence[] constructEntryList() {
+ final String prereleaseDriverPackageName =
+ SystemProperties.get(PROPERTY_GFX_DRIVER_PRERELEASE);
+ final String gameDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER_GAME);
+
+ List<CharSequence> entryList = new ArrayList<>();
+ entryList.add(mPreferenceDefault);
+ if (!TextUtils.isEmpty(prereleaseDriverPackageName)) {
+ entryList.add(mPreferencePrereleaseDriver);
+ }
+ if (!TextUtils.isEmpty(gameDriverPackageName)) {
+ entryList.add(mPreferenceGameDriver);
+ }
+ entryList.add(mPreferenceSystem);
+ CharSequence[] filteredEntryList = new CharSequence[entryList.size()];
+ filteredEntryList = entryList.toArray(filteredEntryList);
+ return filteredEntryList;
+ }
+
// AppInfo class to achieve loading the application label only once
class AppInfo {
AppInfo(PackageManager packageManager, ApplicationInfo applicationInfo) {
diff --git a/src/com/android/settings/display/AdaptiveSleepPreferenceController.java b/src/com/android/settings/display/AdaptiveSleepPreferenceController.java
index 7f85f7d..8faae30 100644
--- a/src/com/android/settings/display/AdaptiveSleepPreferenceController.java
+++ b/src/com/android/settings/display/AdaptiveSleepPreferenceController.java
@@ -17,8 +17,12 @@
import android.Manifest;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.provider.Settings;
+import android.service.attention.AttentionService;
+import android.text.TextUtils;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
@@ -62,10 +66,24 @@
public static int isControllerAvailable(Context context) {
return context.getResources().getBoolean(
com.android.internal.R.bool.config_adaptive_sleep_available)
+ && isAttentionServiceAvailable(context)
? AVAILABLE_UNSEARCHABLE
: UNSUPPORTED_ON_DEVICE;
}
+ private static boolean isAttentionServiceAvailable(Context context) {
+ final PackageManager packageManager = context.getPackageManager();
+ final String resolvePackage = packageManager.getAttentionServicePackageName();
+ if (TextUtils.isEmpty(resolvePackage)) {
+ return false;
+ }
+ final Intent intent = new Intent(AttentionService.SERVICE_INTERFACE).setPackage(
+ resolvePackage);
+ final ResolveInfo resolveInfo = packageManager.resolveService(intent,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ return resolveInfo != null && resolveInfo.serviceInfo != null;
+ }
+
static boolean hasSufficientPermission(PackageManager packageManager) {
final String attentionPackage = packageManager.getAttentionServicePackageName();
return attentionPackage != null && packageManager.checkPermission(
diff --git a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java
index a9a832d..9627eb0 100644
--- a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java
+++ b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java
@@ -48,7 +48,7 @@
public static final Uri DELETE_CARD_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(CardContentProvider.CARD_AUTHORITY)
- .appendPath(CardDatabaseHelper.CardColumns.CARD_DISMISSED)
+ .appendPath(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP)
.build();
private static final String TAG = "CardContentProvider";
diff --git a/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java b/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java
index 8c471b0..5af0ca6 100644
--- a/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java
+++ b/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java
@@ -16,9 +16,7 @@
package com.android.settings.homepage.contextualcards;
-import android.content.ContentValues;
import android.content.Context;
-import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
@@ -31,7 +29,7 @@
public class CardDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "CardDatabaseHelper";
private static final String DATABASE_NAME = "homepage_cards.db";
- private static final int DATABASE_VERSION = 6;
+ private static final int DATABASE_VERSION = 7;
public static final String CARD_TABLE = "cards";
@@ -72,31 +70,32 @@
String APP_VERSION = "app_version";
/**
- * Decide the card is dismissed or not.
+ * Timestamp of card being dismissed.
*/
- String CARD_DISMISSED = "card_dismissed";
+ String DISMISSED_TIMESTAMP = "dismissed_timestamp";
}
private static final String CREATE_CARD_TABLE =
- "CREATE TABLE " + CARD_TABLE +
- "(" +
- CardColumns.NAME +
- " TEXT NOT NULL PRIMARY KEY, " +
- CardColumns.TYPE +
- " INTEGER NOT NULL, " +
- CardColumns.SCORE +
- " DOUBLE NOT NULL, " +
- CardColumns.SLICE_URI +
- " TEXT, " +
- CardColumns.CATEGORY +
- " INTEGER DEFAULT 0, " +
- CardColumns.PACKAGE_NAME +
- " TEXT NOT NULL, " +
- CardColumns.APP_VERSION +
- " INTEGER NOT NULL, " +
- CardColumns.CARD_DISMISSED +
- " INTEGER DEFAULT 0 " +
- ");";
+ "CREATE TABLE "
+ + CARD_TABLE
+ + "("
+ + CardColumns.NAME
+ + " TEXT NOT NULL PRIMARY KEY, "
+ + CardColumns.TYPE
+ + " INTEGER NOT NULL, "
+ + CardColumns.SCORE
+ + " DOUBLE NOT NULL, "
+ + CardColumns.SLICE_URI
+ + " TEXT, "
+ + CardColumns.CATEGORY
+ + " INTEGER DEFAULT 0, "
+ + CardColumns.PACKAGE_NAME
+ + " TEXT NOT NULL, "
+ + CardColumns.APP_VERSION
+ + " INTEGER NOT NULL, "
+ + CardColumns.DISMISSED_TIMESTAMP
+ + " INTEGER"
+ + ");";
public CardDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -125,32 +124,4 @@
}
return sCardDatabaseHelper;
}
-
- Cursor getContextualCards() {
- final SQLiteDatabase db = getReadableDatabase();
- final String selection = CardColumns.CARD_DISMISSED + "=0";
- return db.query(CARD_TABLE, null /* columns */, selection,
- null /* selectionArgs */, null /* groupBy */, null /* having */,
- CardColumns.SCORE + " DESC" /* orderBy */);
- }
-
- /**
- * Mark a specific ContextualCard with dismissal flag in the database to indicate that the
- * card has been dismissed.
- *
- * @param context Context
- * @param cardName The card name of the ContextualCard which is dismissed by user.
- * @return The number of rows updated
- */
- public int markContextualCardAsDismissed(Context context, String cardName) {
- final SQLiteDatabase database = getWritableDatabase();
- final ContentValues values = new ContentValues();
- values.put(CardColumns.CARD_DISMISSED, 1);
- final String selection = CardColumns.NAME + "=?";
- final String[] selectionArgs = {cardName};
- final int rowsUpdated = database.update(CARD_TABLE, values, selection, selectionArgs);
- database.close();
- context.getContentResolver().notifyChange(CardContentProvider.DELETE_CARD_URI, null);
- return rowsUpdated;
- }
}
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java
index bdf863e..60bffde 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java
@@ -16,10 +16,25 @@
package com.android.settings.homepage.contextualcards;
+import android.content.Context;
+import android.database.Cursor;
+
import androidx.slice.Slice;
/** Feature provider for the contextual card feature. */
public interface ContextualCardFeatureProvider {
+ /** Get contextual cards from the card provider */
+ Cursor getContextualCards();
+
+ /**
+ * Mark a specific {@link ContextualCard} as dismissed with dismissal signal in the database
+ * to indicate that the card has been dismissed.
+ *
+ * @param context Context
+ * @param cardName The card name of the ContextualCard which is dismissed by user.
+ * @return The number of rows updated
+ */
+ int markCardAsDismissed(Context context, String cardName);
/** Log package when user clicks contextual notification channel card. */
void logNotificationPackage(Slice slice);
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
index 4af2838..f4bb84c 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java
@@ -18,10 +18,19 @@
import static android.content.Context.MODE_PRIVATE;
+import static com.android.settings.homepage.contextualcards.CardDatabaseHelper.CARD_TABLE;
+
+import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
+import android.text.format.DateUtils;
import android.util.ArraySet;
+import android.util.Log;
+import androidx.annotation.VisibleForTesting;
import androidx.slice.Slice;
import androidx.slice.SliceMetadata;
import androidx.slice.core.SliceAction;
@@ -30,10 +39,13 @@
import com.android.settings.applications.AppInfoBase;
import com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice;
import com.android.settings.slices.CustomSliceRegistry;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.Set;
public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureProvider {
+ private static final String TAG = "ContextualCardFeatureProvider";
+
private final Context mContext;
public ContextualCardFeatureProviderImpl(Context context) {
@@ -41,6 +53,33 @@
}
@Override
+ public Cursor getContextualCards() {
+ final SQLiteDatabase db = CardDatabaseHelper.getInstance(mContext).getReadableDatabase();
+ //TODO(b/149542061): Make the dismissal duration configurable.
+ final long threshold = System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS;
+ final String selection = CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " < ? OR "
+ + CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " IS NULL";
+ final String[] selectionArgs = {String.valueOf(threshold)};
+ final Cursor cursor = db.query(CARD_TABLE, null /* columns */, selection,
+ selectionArgs /* selectionArgs */, null /* groupBy */, null /* having */,
+ CardDatabaseHelper.CardColumns.SCORE + " DESC" /* orderBy */);
+ ThreadUtils.postOnBackgroundThread(() -> resetDismissedTime(threshold));
+ return cursor;
+ }
+
+ @Override
+ public int markCardAsDismissed(Context context, String cardName) {
+ final SQLiteDatabase db = CardDatabaseHelper.getInstance(mContext).getWritableDatabase();
+ final ContentValues values = new ContentValues();
+ values.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP, System.currentTimeMillis());
+ final String selection = CardDatabaseHelper.CardColumns.NAME + "=?";
+ final String[] selectionArgs = {cardName};
+ final int rowsUpdated = db.update(CARD_TABLE, values, selection, selectionArgs);
+ context.getContentResolver().notifyChange(CardContentProvider.DELETE_CARD_URI, null);
+ return rowsUpdated;
+ }
+
+ @Override
public void logNotificationPackage(Slice slice) {
if (slice == null || !slice.getUri().equals(
CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI)) {
@@ -62,4 +101,20 @@
prefs.edit().putStringSet(ContextualNotificationChannelSlice.PREF_KEY_INTERACTED_PACKAGES,
newInteractedPackages).apply();
}
+
+ @VisibleForTesting
+ int resetDismissedTime(long threshold) {
+ final SQLiteDatabase database =
+ CardDatabaseHelper.getInstance(mContext).getWritableDatabase();
+ final ContentValues values = new ContentValues();
+ values.putNull(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP);
+ final String selection = CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " < ? AND "
+ + CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " IS NOT NULL";
+ final String[] selectionArgs = {String.valueOf(threshold)};
+ final int rowsUpdated = database.update(CARD_TABLE, values, selection, selectionArgs);
+ if (Build.IS_DEBUGGABLE) {
+ Log.d(TAG, "Reset " + rowsUpdated + " records of dismissed time.");
+ }
+ return rowsUpdated;
+ }
}
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
index 1c83a10..886a12d 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
@@ -178,7 +178,9 @@
@VisibleForTesting
Cursor getContextualCardsFromProvider() {
- return CardDatabaseHelper.getInstance(mContext).getContextualCards();
+ final ContextualCardFeatureProvider cardFeatureProvider =
+ FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
+ return cardFeatureProvider.getContextualCards();
}
@VisibleForTesting
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java
index 9eb7fae..ced3f8d 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java
@@ -25,9 +25,9 @@
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
-import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardController;
+import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog;
import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils;
@@ -68,8 +68,9 @@
@Override
public void onDismissed(ContextualCard card) {
ThreadUtils.postOnBackgroundThread(() -> {
- final CardDatabaseHelper dbHelper = CardDatabaseHelper.getInstance(mContext);
- dbHelper.markContextualCardAsDismissed(mContext, card.getName());
+ final ContextualCardFeatureProvider cardFeatureProvider =
+ FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
+ cardFeatureProvider.markCardAsDismissed(mContext, card.getName());
});
showFeedbackDialog(card);
diff --git a/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java b/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java
index b6a78a1..44187e4 100644
--- a/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java
+++ b/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java
@@ -21,6 +21,7 @@
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.euicc.EuiccManager;
+import android.text.TextUtils;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
@@ -60,19 +61,23 @@
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
- boolean confirmDeletion =
- Settings.Global.getInt(
- mContext.getContentResolver(),
- ConfirmSimDeletionPreferenceController.KEY_CONFIRM_SIM_DELETION,
- mConfirmationDefaultOn ? 1 : 0)
- == 1;
- if (confirmDeletion) {
- WifiDppUtils.showLockScreen(mContext, () -> deleteSim());
- } else {
- deleteSim();
+ if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+ boolean confirmDeletion =
+ Settings.Global.getInt(
+ mContext.getContentResolver(),
+ ConfirmSimDeletionPreferenceController.KEY_CONFIRM_SIM_DELETION,
+ mConfirmationDefaultOn ? 1 : 0)
+ == 1;
+ if (confirmDeletion) {
+ WifiDppUtils.showLockScreen(mContext, () -> deleteSim());
+ } else {
+ deleteSim();
+ }
+
+ return true;
}
- return true;
+ return false;
}
private void deleteSim() {
diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
index 9b78ae9..9f8a07f 100644
--- a/src/com/android/settings/notification/history/NotificationHistoryActivity.java
+++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
@@ -51,6 +51,7 @@
import com.android.settings.notification.NotificationBackend;
import com.android.settings.widget.SwitchBar;
+import java.util.ArrayList;
import java.util.Arrays;
public class NotificationHistoryActivity extends Activity {
@@ -252,7 +253,7 @@
rv.setNestedScrollingEnabled(false);
((NotificationSbnAdapter) rv.getAdapter()).onRebuildComplete(
- Arrays.asList(snoozed));
+ new ArrayList<>(Arrays.asList(snoozed)));
}
try {
@@ -268,7 +269,7 @@
rv.setNestedScrollingEnabled(false);
((NotificationSbnAdapter) rv.getAdapter()).onRebuildComplete(
- Arrays.asList(dismissed));
+ new ArrayList<>(Arrays.asList(dismissed)));
mDismissView.setVisibility(View.VISIBLE);
} catch (Exception e) {
Slog.e(TAG, "Cannot load recently dismissed", e);
diff --git a/src/com/android/settings/notification/history/NotificationSbnAdapter.java b/src/com/android/settings/notification/history/NotificationSbnAdapter.java
index a35b5d4..f1dcf47 100644
--- a/src/com/android/settings/notification/history/NotificationSbnAdapter.java
+++ b/src/com/android/settings/notification/history/NotificationSbnAdapter.java
@@ -17,6 +17,8 @@
package com.android.settings.notification.history;
import static android.content.pm.PackageManager.*;
+import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.USER_CURRENT;
import android.app.Notification;
import android.content.Context;
@@ -77,12 +79,13 @@
holder.setTitle(getTitleString(sbn.getNotification()));
holder.setSummary(getTextString(mContext, sbn.getNotification()));
holder.setPostedTime(sbn.getPostTime());
- if (!mUserBadgeCache.containsKey(sbn.getUserId())) {
+ int userId = normalizeUserId(sbn);
+ if (!mUserBadgeCache.containsKey(userId)) {
Drawable profile = mContext.getPackageManager().getUserBadgeForDensity(
- UserHandle.of(sbn.getUserId()), -1);
- mUserBadgeCache.put(sbn.getUserId(), profile);
+ UserHandle.of(userId), -1);
+ mUserBadgeCache.put(userId, profile);
}
- holder.setProfileBadge(mUserBadgeCache.get(sbn.getUserId()));
+ holder.setProfileBadge(mUserBadgeCache.get(userId));
} else {
Slog.w(TAG, "null entry in list at position " + position);
}
@@ -153,7 +156,7 @@
private Drawable loadIcon(StatusBarNotification sbn) {
Drawable draw = sbn.getNotification().getSmallIcon().loadDrawableAsUser(
- sbn.getPackageContext(mContext), sbn.getUserId());
+ sbn.getPackageContext(mContext), normalizeUserId(sbn));
if (draw == null) {
return null;
}
@@ -161,4 +164,12 @@
draw.setColorFilter(sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
return draw;
}
+
+ private int normalizeUserId(StatusBarNotification sbn) {
+ int userId = sbn.getUserId();
+ if (userId == USER_ALL) {
+ userId = USER_CURRENT;
+ }
+ return userId;
+ }
}
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
index 4422204..262a550 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
@@ -37,6 +37,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -57,7 +58,7 @@
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
*/
public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFragment {
-
+ public static final String TAG = ConfirmDeviceCredentialBaseFragment.class.getSimpleName();
public static final String TITLE_TEXT = SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.title";
public static final String HEADER_TEXT = SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.header";
public static final String DETAILS_TEXT = SETTINGS_PACKAGE_NAME + ".ConfirmCredentials.details";
@@ -78,7 +79,8 @@
protected boolean mReturnCredentials = false;
protected Button mCancelButton;
- protected Button mForgotButton;
+ /** Button allowing managed profile password reset, null when is not shown. */
+ @Nullable protected Button mForgotButton;
protected int mEffectiveUserId;
protected int mUserId;
protected UserManager mUserManager;
@@ -133,10 +135,18 @@
}
getActivity().finish();
});
- mForgotButton = view.findViewById(R.id.forgotButton);
+ setupForgotButtonIfManagedProfile(view);
+ }
+
+ private void setupForgotButtonIfManagedProfile(View view) {
if (mUserManager.isManagedProfile(mUserId)
&& mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))
&& mDevicePolicyManager.canProfileOwnerResetPasswordWhenLocked(mUserId)) {
+ mForgotButton = view.findViewById(R.id.forgotButton);
+ if (mForgotButton == null) {
+ Log.wtf(TAG, "Forgot button not found in managed profile credential dialog");
+ return;
+ }
mForgotButton.setVisibility(View.VISIBLE);
mForgotButton.setOnClickListener(v -> {
final Intent intent = new Intent();
@@ -145,8 +155,6 @@
getActivity().startActivity(intent);
getActivity().finish();
});
- } else {
- mForgotButton.setVisibility(View.GONE);
}
}
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index d77fd3a..ce8813f 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -267,7 +267,9 @@
mHeaderTextView.setAlpha(0f);
mDetailsTextView.setAlpha(0f);
mCancelButton.setAlpha(0f);
- mForgotButton.setAlpha(0f);
+ if (mForgotButton != null) {
+ mForgotButton.setAlpha(0f);
+ }
mPasswordEntry.setAlpha(0f);
mErrorTextView.setAlpha(0f);
}
@@ -279,7 +281,7 @@
if (mCancelButton.getVisibility() == View.VISIBLE) {
result.add(mCancelButton);
}
- if (mForgotButton.getVisibility() == View.VISIBLE) {
+ if (mForgotButton != null) {
result.add(mForgotButton);
}
result.add(mPasswordEntry);
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index fd164b8..b2afb22 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -240,7 +240,9 @@
super.prepareEnterAnimation();
mHeaderTextView.setAlpha(0f);
mCancelButton.setAlpha(0f);
- mForgotButton.setAlpha(0f);
+ if (mForgotButton != null) {
+ mForgotButton.setAlpha(0f);
+ }
mLockPatternView.setAlpha(0f);
mDetailsTextView.setAlpha(0f);
}
@@ -268,7 +270,7 @@
if (mCancelButton.getVisibility() == View.VISIBLE) {
result.add(new ArrayList<>(Collections.singletonList(mCancelButton)));
}
- if (mForgotButton.getVisibility() == View.VISIBLE) {
+ if (mForgotButton != null) {
result.add(new ArrayList<>(Collections.singletonList(mForgotButton)));
}
LockPatternView.CellState[][] cellStates = mLockPatternView.getCellStates();
diff --git a/src/com/android/settings/vpn2/AppManagementFragment.java b/src/com/android/settings/vpn2/AppManagementFragment.java
index 8e3d9bb..8280a3d 100644
--- a/src/com/android/settings/vpn2/AppManagementFragment.java
+++ b/src/com/android/settings/vpn2/AppManagementFragment.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.app.Dialog;
+import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -48,6 +49,8 @@
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.RestrictedSwitchPreference;
@@ -67,6 +70,7 @@
private static final String KEY_FORGET_VPN = "forget_vpn";
private PackageManager mPackageManager;
+ private DevicePolicyManager mDevicePolicyManager;
private ConnectivityManager mConnectivityManager;
private IConnectivityManager mConnectivityService;
@@ -119,6 +123,7 @@
addPreferencesFromResource(R.xml.vpn_app_management);
mPackageManager = getContext().getPackageManager();
+ mDevicePolicyManager = getContext().getSystemService(DevicePolicyManager.class);
mConnectivityManager = getContext().getSystemService(ConnectivityManager.class);
mConnectivityService = IConnectivityManager.Stub
.asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
@@ -250,6 +255,15 @@
mPreferenceForget.checkRestrictionAndSetDisabled(UserManager.DISALLOW_CONFIG_VPN,
mUserId);
+ if (mPackageName.equals(mDevicePolicyManager.getAlwaysOnVpnPackage())) {
+ EnforcedAdmin admin = RestrictedLockUtils.getProfileOrDeviceOwner(
+ getContext(), UserHandle.of(mUserId));
+ mPreferenceAlwaysOn.setDisabledByAdmin(admin);
+ mPreferenceForget.setDisabledByAdmin(admin);
+ if (mDevicePolicyManager.isAlwaysOnVpnLockdownEnabled()) {
+ mPreferenceLockdown.setDisabledByAdmin(admin);
+ }
+ }
if (mConnectivityManager.isAlwaysOnVpnPackageSupportedForUser(mUserId, mPackageName)) {
// setSummary doesn't override the admin message when user restriction is applied
mPreferenceAlwaysOn.setSummary(R.string.vpn_always_on_summary);
diff --git a/src/com/android/settings/wifi/dpp/AdbQrCode.java b/src/com/android/settings/wifi/dpp/AdbQrCode.java
new file mode 100644
index 0000000..fb63e0a
--- /dev/null
+++ b/src/com/android/settings/wifi/dpp/AdbQrCode.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 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.wifi.dpp;
+
+import android.text.TextUtils;
+
+/**
+ * Extension of WifiQrCode to support ADB QR code format.
+ * It will be based on the ZXing format:
+ *
+ * WIFI:T:ADB;S:myname;P:mypassword;;
+ */
+public class AdbQrCode extends WifiQrCode {
+ static final String SECURITY_ADB = "ADB";
+
+ private WifiNetworkConfig mAdbConfig;
+
+ public AdbQrCode(String qrCode) throws IllegalArgumentException {
+ super(qrCode);
+
+ // Only accept the zxing format.
+ if (!WifiQrCode.SCHEME_ZXING_WIFI_NETWORK_CONFIG.equals(getScheme())) {
+ throw new IllegalArgumentException("DPP format not supported for ADB QR code");
+ }
+
+ mAdbConfig = getWifiNetworkConfig();
+ if (!SECURITY_ADB.equals(mAdbConfig.getSecurity())) {
+ throw new IllegalArgumentException("Invalid security type");
+ }
+
+ if (TextUtils.isEmpty(mAdbConfig.getSsid())) {
+ throw new IllegalArgumentException("Empty service name");
+ }
+
+ if (TextUtils.isEmpty(mAdbConfig.getPreSharedKey())) {
+ throw new IllegalArgumentException("Empty password");
+ }
+ }
+
+ public WifiNetworkConfig getAdbNetworkConfig() {
+ return mAdbConfig;
+ }
+}
diff --git a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
index 89ead9d..111126f 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
@@ -52,7 +52,7 @@
@VisibleForTesting
static long sActiveUiSession = -1000;
@VisibleForTesting
- static boolean sToggleNeeded = true;
+ static boolean sApRowCollapsed;
public ContextualWifiSlice(Context context) {
super(context);
@@ -69,26 +69,26 @@
.getSlicesFeatureProvider().getUiSessionToken();
if (currentUiSession != sActiveUiSession) {
sActiveUiSession = currentUiSession;
- sToggleNeeded = !hasWorkingNetwork();
+ sApRowCollapsed = hasWorkingNetwork();
} else if (!mWifiManager.isWifiEnabled()) {
- sToggleNeeded = true;
+ sApRowCollapsed = false;
}
return super.getSlice();
}
static int getApRowCount() {
- return sToggleNeeded ? DEFAULT_EXPANDED_ROW_COUNT : COLLAPSED_ROW_COUNT;
+ return sApRowCollapsed ? COLLAPSED_ROW_COUNT : DEFAULT_EXPANDED_ROW_COUNT;
}
@Override
- protected boolean isToggleNeeded() {
- return sToggleNeeded;
+ protected boolean isApRowCollapsed() {
+ return sApRowCollapsed;
}
@Override
protected ListBuilder.RowBuilder getHeaderRow(AccessPoint accessPoint) {
final ListBuilder.RowBuilder builder = super.getHeaderRow(accessPoint);
- if (!sToggleNeeded) {
+ if (sApRowCollapsed) {
builder.setTitleItem(getLevelIcon(accessPoint), ListBuilder.ICON_IMAGE)
.setSubtitle(getSubtitle(accessPoint));
}
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index 8e2d7a6..fe1da73 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -107,7 +107,7 @@
final boolean isFirstApActive = apCount > 0 && apList.get(0).isActive();
handleNetworkCallback(worker, isFirstApActive);
- if (!isToggleNeeded()) {
+ if (isApRowCollapsed()) {
if (isFirstApActive) {
// refresh header subtext
listBuilder = getListBuilder(true /* isWifiEnabled */, apList.get(0));
@@ -142,8 +142,8 @@
}
}
- protected boolean isToggleNeeded() {
- return true;
+ protected boolean isApRowCollapsed() {
+ return false;
}
protected ListBuilder.RowBuilder getHeaderRow(AccessPoint accessPoint) {
@@ -160,15 +160,14 @@
}
private ListBuilder getListBuilder(boolean isWifiEnabled, AccessPoint accessPoint) {
+ final PendingIntent toggleAction = getBroadcastIntent(mContext);
+ final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
+ null /* actionTitle */, isWifiEnabled);
final ListBuilder builder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
.setAccentColor(COLOR_NOT_TINTED)
.setKeywords(getKeywords())
- .addRow(getHeaderRow(accessPoint));
- if (isToggleNeeded()) {
- final PendingIntent toggleAction = getBroadcastIntent(mContext);
- builder.addAction(SliceAction.createToggle(toggleAction, null /* actionTitle */,
- isWifiEnabled));
- }
+ .addRow(getHeaderRow(accessPoint))
+ .addAction(toggleSliceAction);
return builder;
}
diff --git a/tests/robotests/src/com/android/settings/development/EnableVerboseVendorLoggingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/EnableVerboseVendorLoggingPreferenceControllerTest.java
new file mode 100644
index 0000000..6145939
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/EnableVerboseVendorLoggingPreferenceControllerTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.dumpstate.V1_1.IDumpstateDevice;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.lang.reflect.Field;
+
+@RunWith(RobolectricTestRunner.class)
+public final class EnableVerboseVendorLoggingPreferenceControllerTest {
+ @Mock
+ private SwitchPreference mPreference;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ IDumpstateDevice mIDumpstateDevice;
+
+ private Context mContext;
+ private EnableVerboseVendorLoggingPreferenceController mController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mController = spy(new EnableVerboseVendorLoggingPreferenceController(mContext));
+ doReturn(mIDumpstateDevice).when(mController).getDumpstateDeviceService();
+
+ // mock with Dumpstate HAL v1.1
+ Field f = EnableVerboseVendorLoggingPreferenceController.class
+ .getDeclaredField("mDumpstateHalVersion");
+ f.setAccessible(true);
+ f.setInt(mController, 1 /* DUMPSTATE_HAL_VERSION_1_1 */);
+
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+ .thenReturn(mPreference);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void onPreferenceChange_settingEnable_enableVendorLoggingShouldBeOn() throws Exception {
+ doReturn(true).when(mIDumpstateDevice).getVerboseLoggingEnabled();
+
+ mController.onPreferenceChange(mPreference, true /* new value */);
+
+ final boolean enabled = mController.getVerboseLoggingEnabled();
+ assertTrue(enabled);
+ }
+
+ @Test
+ public void onPreferenceChange_settingDisable_enableVendorLoggingShouldBeOff()
+ throws Exception {
+ doReturn(false).when(mIDumpstateDevice).getVerboseLoggingEnabled();
+
+ mController.onPreferenceChange(mPreference, false /* new value */);
+
+ final boolean enabled = mController.getVerboseLoggingEnabled();
+ assertFalse(enabled);
+ }
+
+ @Test
+ public void updateState_settingDisabled_preferenceShouldNotBeChecked() throws Exception {
+ doReturn(false).when(mIDumpstateDevice).getVerboseLoggingEnabled();
+
+ mController.setVerboseLoggingEnabled(false);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(false);
+ }
+
+ @Test
+ public void updateState_settingEnabled_preferenceShouldBeChecked() throws Exception {
+ doReturn(true).when(mIDumpstateDevice).getVerboseLoggingEnabled();
+
+ mController.setVerboseLoggingEnabled(true);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(true);
+ }
+
+ @Test
+ public void onDeveloperOptionDisabled_shouldDisablePreference() throws Exception {
+ doReturn(false).when(mIDumpstateDevice).getVerboseLoggingEnabled();
+
+ mController.onDeveloperOptionsSwitchDisabled();
+
+ final boolean enabled = mController.getVerboseLoggingEnabled();
+ assertFalse(enabled);
+ verify(mPreference).setChecked(false);
+ verify(mPreference).setEnabled(false);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/development/EnhancedConnectivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/EnhancedConnectivityPreferenceControllerTest.java
new file mode 100644
index 0000000..6fd6f55
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/EnhancedConnectivityPreferenceControllerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class EnhancedConnectivityPreferenceControllerTest {
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private SwitchPreference mPreference;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+
+ private EnhancedConnectivityPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new EnhancedConnectivityPreferenceController(mContext);
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+ .thenReturn(mPreference);
+ mController.displayPreference(mPreferenceScreen);
+ }
+
+ @Test
+ public void onPreferenceChanged_enhanceConnectivity_shouldBeOn() {
+ mController.onPreferenceChange(mPreference, true /* new value */);
+
+ assertThat(isSettingEnabled()).isTrue();
+ }
+
+ @Test
+ public void onPreferenceChanged_enhanceConnectivity_shouldBeOff() {
+ mController.onPreferenceChange(mPreference, false /* new value */);
+
+ assertThat(isSettingEnabled()).isFalse();
+ }
+
+ @Test
+ public void updateState_preferenceShouldBeChecked() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.ENHANCED_CONNECTIVITY_ENABLED, 1 /* enabled */);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(true);
+ }
+
+ @Test
+ public void updateState_preferenceShouldNotBeChecked() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.ENHANCED_CONNECTIVITY_ENABLED, 0 /* disabled */);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(false);
+ }
+
+ @Test
+ public void onDeveloperOptionsDisabled_shouldDisablePreference() {
+ mController.onDeveloperOptionsDisabled();
+
+ verify(mPreference).setEnabled(false);
+ verify(mPreference).setChecked(false);
+
+ assertThat(isSettingEnabled()).isFalse();
+ }
+
+ private boolean isSettingEnabled() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
+ EnhancedConnectivityPreferenceController.ENHANCED_CONNECTIVITY_OFF
+ /* default off */)
+ == EnhancedConnectivityPreferenceController.ENHANCED_CONNECTIVITY_ON;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/development/WirelessDebuggingEnablerTest.java b/tests/robotests/src/com/android/settings/development/WirelessDebuggingEnablerTest.java
new file mode 100644
index 0000000..7f4f530
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/WirelessDebuggingEnablerTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.provider.Settings.Global;
+
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.settings.SettingsActivity;
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settings.widget.SwitchBar;
+import com.android.settings.widget.SwitchBarController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowUtils.class)
+public class WirelessDebuggingEnablerTest {
+
+ @Mock
+ private SettingsActivity mActivity;
+ @Mock
+ private SwitchBar mSwitchBar;
+ @Mock
+ private WirelessDebuggingEnabler.OnEnabledListener mListener;
+
+ private WirelessDebuggingEnabler mWirelessDebuggingEnabler;
+ private Context mContext;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+ mWirelessDebuggingEnabler = spy(new WirelessDebuggingEnabler(
+ mContext, new SwitchBarController(mSwitchBar), mListener, mLifecycle));
+ }
+
+ @Test
+ public void onCreation_shouldShowSwitchBar() {
+ verify(mSwitchBar).show();
+ }
+
+ @Test
+ public void teardownSwitchController_shouldHideSwitchBar() {
+ mWirelessDebuggingEnabler.teardownSwitchController();
+
+ verify(mSwitchBar).hide();
+ }
+
+ @Test
+ public void adbWifiEnabled_switchBarShouldBeChecked() {
+ // Set to disabled first otherwise we won't get the onChange() event
+ Global.putInt(mContext.getContentResolver(),
+ Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
+ mWirelessDebuggingEnabler.onResume();
+
+ verify(mSwitchBar).setChecked(false);
+ verify(mListener).onEnabled(false);
+
+ Global.putInt(mContext.getContentResolver(),
+ Global.ADB_WIFI_ENABLED, 1 /* setting enabled */);
+ final ContentObserver observer =
+ ReflectionHelpers.getField(mWirelessDebuggingEnabler, "mSettingsObserver");
+ observer.onChange(true, Global.getUriFor(Global.ADB_WIFI_ENABLED));
+
+ verify(mSwitchBar).setChecked(true);
+ // Should also get a callback
+ verify(mListener).onEnabled(true);
+ }
+
+ @Test
+ public void adbWifiEnabled_switchBarShouldNotBeChecked() {
+ Global.putInt(mContext.getContentResolver(),
+ Global.ADB_WIFI_ENABLED, 1 /* setting enabled */);
+ mWirelessDebuggingEnabler.onResume();
+
+ verify(mSwitchBar).setChecked(true);
+ verify(mListener).onEnabled(true);
+
+ Global.putInt(mContext.getContentResolver(),
+ Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
+ final ContentObserver observer =
+ ReflectionHelpers.getField(mWirelessDebuggingEnabler, "mSettingsObserver");
+ observer.onChange(true, Global.getUriFor(Global.ADB_WIFI_ENABLED));
+
+ verify(mSwitchBar).setChecked(false);
+ // Should also get a callback
+ verify(mListener).onEnabled(false);
+ }
+
+ @Test
+ public void onSwitchToggled_true_shouldSetAdbWifiEnabledTrue() {
+ Global.putInt(mContext.getContentResolver(),
+ Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
+ mWirelessDebuggingEnabler.onResume();
+
+ verify(mSwitchBar).setChecked(false);
+ verify(mListener).onEnabled(false);
+
+ mWirelessDebuggingEnabler.onSwitchToggled(true);
+
+ verify(mSwitchBar).setChecked(true);
+ verify(mListener).onEnabled(true);
+ assertThat(Global.getInt(mContext.getContentResolver(),
+ Global.ADB_WIFI_ENABLED, -1)).isEqualTo(1);
+ // Should also get a callback
+ }
+
+ @Test
+ public void onSwitchToggled_false_shouldSetAdbWifiEnabledFalse() {
+ Global.putInt(mContext.getContentResolver(),
+ Global.ADB_WIFI_ENABLED, 1 /* setting disabled */);
+ mWirelessDebuggingEnabler.onResume();
+
+ verify(mSwitchBar).setChecked(true);
+ verify(mListener).onEnabled(true);
+
+ mWirelessDebuggingEnabler.onSwitchToggled(false);
+
+ verify(mSwitchBar).setChecked(false);
+ verify(mListener).onEnabled(false);
+ assertThat(Global.getInt(mContext.getContentResolver(),
+ Global.ADB_WIFI_ENABLED, -1)).isEqualTo(0);
+ // Should also get a callback
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/development/WirelessDebuggingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/WirelessDebuggingPreferenceControllerTest.java
new file mode 100644
index 0000000..f755ec4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/WirelessDebuggingPreferenceControllerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 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.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.debug.IAdbManager;
+import android.os.RemoteException;
+import android.provider.Settings.Global;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settings.widget.MasterSwitchPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowUtils.class)
+public class WirelessDebuggingPreferenceControllerTest {
+
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private MasterSwitchPreference mPreference;
+ @Mock
+ private IAdbManager mAdbManager;
+ @Mock
+ private DevelopmentSettingsDashboardFragment mFragment;
+
+ private WirelessDebuggingPreferenceController mController;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+ private Context mContext;
+ private ContentResolver mContentResolver;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mContentResolver = mContext.getContentResolver();
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+ mController = spy(new WirelessDebuggingPreferenceController(mContext, mLifecycle));
+ ReflectionHelpers.setField(mController, "mAdbManager", mAdbManager);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void isAvailable_isAdbWifiSupported_yes_shouldBeTrue() throws RemoteException {
+ when(mAdbManager.isAdbWifiSupported()).thenReturn(true);
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_isAdbWifiSupported_shouldBeFalse() throws RemoteException {
+ when(mAdbManager.isAdbWifiSupported()).thenReturn(false);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void updateState_adbWifiEnabled_preferenceShouldBeChecked() {
+ Global.putInt(mContentResolver,
+ Global.ADB_WIFI_ENABLED, 1 /* setting enabled */);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(true);
+ }
+
+ @Test
+ public void updateState_adbWifiDisabled_preferenceShouldNotBeChecked() {
+ Global.putInt(mContentResolver,
+ Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(false);
+ }
+
+ @Test
+ public void onPreferenceChange_turnOn_adbWifiEnabledTrue() {
+ mController.onPreferenceChange(null, true);
+
+ assertThat(Global.getInt(mContentResolver, Global.ADB_WIFI_ENABLED, -1)).isEqualTo(1);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverAppPreferenceControllerTest.java
index 522dc8a..372daf0 100644
--- a/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverAppPreferenceControllerTest.java
@@ -56,8 +56,9 @@
public class GraphicsDriverAppPreferenceControllerTest {
private static final int DEFAULT = 0;
- private static final int GAME_DRIVER = 1;
- private static final int SYSTEM = 2;
+ private static final int PRERELEASE_DRIVER = 1;
+ private static final int GAME_DRIVER = 2;
+ private static final int SYSTEM = 3;
private static final String TEST_APP_NAME = "testApp";
private static final String TEST_PKG_NAME = "testPkg";
@@ -116,7 +117,7 @@
}
@Test
- public void getAvailability_gameDriverOff_conditionallyUnavailable() {
+ public void getAvailability_graphicsDriverOff_conditionallyUnavailable() {
loadDefaultConfig();
Settings.Global.putInt(mResolver, Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_OFF);
@@ -163,7 +164,7 @@
}
@Test
- public void updateState_gameDriverOff_notVisible() {
+ public void updateState_graphicsDriverOff_notVisible() {
Settings.Global.putInt(mResolver, Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_OFF);
loadDefaultConfig();
@@ -213,6 +214,8 @@
assertThat(preference.getDialogTitle()).isEqualTo(mDialogTitle);
assertThat(preference.getEntries()).isEqualTo(mValueList);
assertThat(preference.getEntryValues()).isEqualTo(mValueList);
+ assertThat(preference.getEntry()).isEqualTo(mValueList[PRERELEASE_DRIVER]);
+ assertThat(preference.getValue()).isEqualTo(mValueList[PRERELEASE_DRIVER]);
assertThat(preference.getSummary()).isEqualTo(mPreferencePrereleaseDriver);
}
@@ -249,6 +252,23 @@
}
@Test
+ public void onPreferenceChange_selectPRERELEASE_DRIVER_shouldUpdateAttrAndSettingsGlobal() {
+ loadDefaultConfig();
+ final ListPreference preference =
+ mController.createListPreference(mContext, TEST_PKG_NAME, TEST_APP_NAME);
+ mController.onPreferenceChange(preference, mValueList[PRERELEASE_DRIVER]);
+
+ assertThat(preference.getEntry()).isEqualTo(mValueList[PRERELEASE_DRIVER]);
+ assertThat(preference.getValue()).isEqualTo(mValueList[PRERELEASE_DRIVER]);
+ assertThat(preference.getSummary()).isEqualTo(mValueList[PRERELEASE_DRIVER]);
+ assertThat(Settings.Global.getString(mResolver,
+ Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS))
+ .isEqualTo(TEST_PKG_NAME);
+ assertThat(Settings.Global.getString(mResolver, Settings.Global.GAME_DRIVER_OPT_OUT_APPS))
+ .isEqualTo("");
+ }
+
+ @Test
public void onPreferenceChange_selectGAME_DRIVER_shouldUpdateAttributesAndSettingsGlobal() {
loadDefaultConfig();
final ListPreference preference =
@@ -306,6 +326,8 @@
Settings.Global.putString(mResolver, Settings.Global.GAME_DRIVER_OPT_OUT_APPS, optOut);
mController = new GraphicsDriverAppPreferenceController(mContext, "testKey");
+ mController.mEntryList = mContext.getResources().getStringArray(
+ R.array.graphics_driver_app_preference_values);
mGroup = spy(new PreferenceCategory(mContext));
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
when(mGroup.getContext()).thenReturn(mContext);
diff --git a/tests/robotests/src/com/android/settings/display/AdaptiveSleepPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AdaptiveSleepPreferenceControllerTest.java
index 295eac5..964fce3 100644
--- a/tests/robotests/src/com/android/settings/display/AdaptiveSleepPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AdaptiveSleepPreferenceControllerTest.java
@@ -16,17 +16,22 @@
package com.android.settings.display;
-import static android.provider.Settings.System.ADAPTIVE_SLEEP;
+import static android.provider.Settings.Secure.ADAPTIVE_SLEEP;
+
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.provider.Settings;
@@ -76,6 +81,14 @@
}
@Test
+ public void isControllerAvailable_ServiceUnavailable_returnUnsupported() {
+ doReturn(null).when(mPackageManager).resolveService(isA(Intent.class), anyInt());
+
+ assertThat(AdaptiveSleepPreferenceController.isControllerAvailable(mContext)).isEqualTo(
+ UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
public void onPreferenceChange_turnOn_returnOn() {
mController.onPreferenceChange(null, true);
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/CardDatabaseHelperTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/CardDatabaseHelperTest.java
index 7c76999..c908225 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/CardDatabaseHelperTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/CardDatabaseHelperTest.java
@@ -18,13 +18,10 @@
import static com.google.common.truth.Truth.assertThat;
-import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
-import com.android.settings.intelligence.ContextualCardProto;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -32,9 +29,6 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import java.util.ArrayList;
-import java.util.List;
-
@RunWith(RobolectricTestRunner.class)
public class CardDatabaseHelperTest {
@@ -46,7 +40,7 @@
public void setUp() {
mContext = RuntimeEnvironment.application;
mCardDatabaseHelper = CardDatabaseHelper.getInstance(mContext);
- mDatabase = mCardDatabaseHelper.getWritableDatabase();
+ mDatabase = mCardDatabaseHelper.getReadableDatabase();
}
@After
@@ -69,44 +63,10 @@
CardDatabaseHelper.CardColumns.CATEGORY,
CardDatabaseHelper.CardColumns.PACKAGE_NAME,
CardDatabaseHelper.CardColumns.APP_VERSION,
- CardDatabaseHelper.CardColumns.CARD_DISMISSED,
+ CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP,
};
assertThat(columnNames).isEqualTo(expectedNames);
cursor.close();
}
-
- @Test
- public void getContextualCards_shouldSortByScore() {
- insertFakeCard(mDatabase, "card1", 1, "uri1");
- insertFakeCard(mDatabase, "card2", 0, "uri2");
- insertFakeCard(mDatabase, "card3", 10, "uri3");
- // Should sort as 3,1,2
- try (final Cursor cursor = CardDatabaseHelper.getInstance(mContext).getContextualCards()) {
- assertThat(cursor.getCount()).isEqualTo(3);
- final List<ContextualCard> cards = new ArrayList<>();
- for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
- cards.add(new ContextualCard(cursor));
- }
- assertThat(cards.get(0).getName()).isEqualTo("card3");
- assertThat(cards.get(1).getName()).isEqualTo("card1");
- assertThat(cards.get(2).getName()).isEqualTo("card2");
- }
- }
-
- private static void insertFakeCard(SQLiteDatabase db, String name, double score, String uri) {
- final ContentValues value = new ContentValues();
- value.put(CardDatabaseHelper.CardColumns.NAME, name);
- value.put(CardDatabaseHelper.CardColumns.SCORE, score);
- value.put(CardDatabaseHelper.CardColumns.SLICE_URI, uri);
-
- value.put(CardDatabaseHelper.CardColumns.TYPE, ContextualCard.CardType.SLICE);
- value.put(CardDatabaseHelper.CardColumns.CATEGORY,
- ContextualCardProto.ContextualCard.Category.DEFAULT.getNumber());
- value.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME,
- RuntimeEnvironment.application.getPackageName());
- value.put(CardDatabaseHelper.CardColumns.APP_VERSION, 1);
-
- db.insert(CardDatabaseHelper.CARD_TABLE, null, value);
- }
}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java
index e380636..029dde8 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java
@@ -28,10 +28,14 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import android.annotation.Nullable;
import android.app.PendingIntent;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.util.ArraySet;
@@ -46,6 +50,7 @@
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.applications.AppInfoBase;
+import com.android.settings.intelligence.ContextualCardProto;
import org.junit.After;
import org.junit.Before;
@@ -54,6 +59,8 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
@RunWith(RobolectricTestRunner.class)
@@ -62,6 +69,8 @@
private Context mContext;
private ContextualCardFeatureProviderImpl mImpl;
private SharedPreferences mSharedPreferences;
+ private CardDatabaseHelper mCardDatabaseHelper;
+ private SQLiteDatabase mDatabase;
@Before
public void setUp() {
@@ -70,14 +79,56 @@
mSharedPreferences = mContext.getSharedPreferences(PREFS, MODE_PRIVATE);
// Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+ mCardDatabaseHelper = CardDatabaseHelper.getInstance(mContext);
+ mDatabase = mCardDatabaseHelper.getWritableDatabase();
}
@After
public void tearDown() {
+ CardDatabaseHelper.getInstance(mContext).close();
+ CardDatabaseHelper.sCardDatabaseHelper = null;
removeInteractedPackageFromSharedPreference();
}
@Test
+ public void getContextualCards_shouldSortByScore() {
+ insertFakeCard(mDatabase, "card1", 1, "uri1", 1000L);
+ insertFakeCard(mDatabase, "card2", 0, "uri2", 1000L);
+ insertFakeCard(mDatabase, "card3", 10, "uri3", 1000L);
+ // Should sort as 3,1,2
+ try (Cursor cursor = mImpl.getContextualCards()) {
+ assertThat(cursor.getCount()).isEqualTo(3);
+ final List<ContextualCard> cards = new ArrayList<>();
+ for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ cards.add(new ContextualCard(cursor));
+ }
+ assertThat(cards.get(0).getName()).isEqualTo("card3");
+ assertThat(cards.get(1).getName()).isEqualTo("card1");
+ assertThat(cards.get(2).getName()).isEqualTo("card2");
+ }
+ }
+
+ @Test
+ public void resetDismissedTime_durationExpired_shouldResetToNull() {
+ insertFakeCard(mDatabase, "card1", 1, "uri1", 100L);
+ final long threshold = 1000L;
+
+ final int rowsUpdated = mImpl.resetDismissedTime(threshold);
+
+ assertThat(rowsUpdated).isEqualTo(1);
+ }
+
+ @Test
+ public void resetDismissedTime_durationNotExpired_shouldNotUpdate() {
+ insertFakeCard(mDatabase, "card1", 1, "uri1", 1111L);
+ final long threshold = 1000L;
+
+ final int rowsUpdated = mImpl.resetDismissedTime(threshold);
+
+ assertThat(rowsUpdated).isEqualTo(0);
+ }
+
+ @Test
public void logNotificationPackage_isContextualNotificationChannel_shouldLogPackage() {
final String packageName = "com.android.test.app";
final Slice slice = buildSlice(CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI, packageName);
@@ -101,6 +152,28 @@
assertThat(interactedPackages.contains(packageName)).isFalse();
}
+ private static void insertFakeCard(
+ SQLiteDatabase db, String name, double score, String uri, @Nullable Long time) {
+ final ContentValues value = new ContentValues();
+ value.put(CardDatabaseHelper.CardColumns.NAME, name);
+ value.put(CardDatabaseHelper.CardColumns.SCORE, score);
+ value.put(CardDatabaseHelper.CardColumns.SLICE_URI, uri);
+
+ value.put(CardDatabaseHelper.CardColumns.TYPE, ContextualCard.CardType.SLICE);
+ value.put(CardDatabaseHelper.CardColumns.CATEGORY,
+ ContextualCardProto.ContextualCard.Category.DEFAULT.getNumber());
+ value.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME,
+ RuntimeEnvironment.application.getPackageName());
+ value.put(CardDatabaseHelper.CardColumns.APP_VERSION, 1);
+ if (time == null) {
+ value.putNull(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP);
+ } else {
+ value.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP, time);
+ }
+
+ db.insert(CardDatabaseHelper.CARD_TABLE, null, value);
+ }
+
private Slice buildSlice(Uri sliceUri, String packageName) {
final Bundle args = new Bundle();
args.putString(AppInfoBase.ARG_PACKAGE_NAME, packageName);
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java
index 51d2523..91d372e 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java
@@ -38,10 +38,13 @@
import com.android.settings.homepage.contextualcards.CardContentProvider;
import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
import com.android.settings.homepage.contextualcards.ContextualCard;
+import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
+import com.android.settings.homepage.contextualcards.ContextualCardFeatureProviderImpl;
import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog;
import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
import com.android.settings.testutils.FakeFeatureFactory;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -78,10 +81,18 @@
mResolver = mContext.getContentResolver();
mController = spy(new SliceContextualCardController(mContext));
mFeatureFactory = FakeFeatureFactory.setupForTest();
+ final ContextualCardFeatureProvider cardFeatureProvider =
+ new ContextualCardFeatureProviderImpl(mContext);
+ mFeatureFactory.mContextualCardFeatureProvider = cardFeatureProvider;
+ }
+
+ @After
+ public void tearDown() {
+ CardDatabaseHelper.getInstance(mContext).close();
}
@Test
- public void onDismissed_cardShouldBeMarkedAsDismissed() {
+ public void onDismissed_cardShouldBeMarkedAsDismissedWithTimestamp() {
final Uri providerUri = CardContentProvider.REFRESH_CARD_URI;
mResolver.insert(providerUri, generateOneRow());
doNothing().when(mController).showFeedbackDialog(any(ContextualCard.class));
@@ -89,15 +100,15 @@
final ContextualCard card = getTestSliceCard();
mController.onDismissed(card);
- final String[] columns = {CardDatabaseHelper.CardColumns.CARD_DISMISSED};
+ final String[] columns = {CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP};
final String selection = CardDatabaseHelper.CardColumns.NAME + "=?";
final String[] selectionArgs = {TEST_CARD_NAME};
final Cursor cr = mResolver.query(providerUri, columns, selection, selectionArgs, null);
cr.moveToFirst();
- final int qryDismissed = cr.getInt(0);
+ final long dismissedTimestamp = cr.getLong(0);
cr.close();
- assertThat(qryDismissed).isEqualTo(1);
+ assertThat(dismissedTimestamp).isNotEqualTo(0L);
verify(mFeatureFactory.metricsFeatureProvider).action(any(),
eq(SettingsEnums.ACTION_CONTEXTUAL_CARD_DISMISS), any(String.class));
}
@@ -172,7 +183,7 @@
values.put(CardDatabaseHelper.CardColumns.CATEGORY, 2);
values.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME, "com.android.settings");
values.put(CardDatabaseHelper.CardColumns.APP_VERSION, 10001);
- values.put(CardDatabaseHelper.CardColumns.CARD_DISMISSED, 0);
+ values.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP, 0L);
return values;
}
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index 220209c..b6c4fb7 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -66,7 +66,6 @@
public final UserFeatureProvider userFeatureProvider;
public final AssistGestureFeatureProvider assistGestureFeatureProvider;
public final AccountFeatureProvider mAccountFeatureProvider;
- public final ContextualCardFeatureProvider mContextualCardFeatureProvider;
public final BluetoothFeatureProvider mBluetoothFeatureProvider;
public final AwareFeatureProvider mAwareFeatureProvider;
public final FaceFeatureProvider mFaceFeatureProvider;
@@ -74,6 +73,7 @@
public PanelFeatureProvider panelFeatureProvider;
public SlicesFeatureProvider slicesFeatureProvider;
public SearchFeatureProvider searchFeatureProvider;
+ public ContextualCardFeatureProvider mContextualCardFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
index 6593ec3..54b7c2d 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
@@ -87,8 +87,7 @@
final Slice wifiSlice = mWifiSlice.getSlice();
- assertTitleAndIcon(wifiSlice);
- assertNoToggle(wifiSlice);
+ assertWifiHeader(wifiSlice);
assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(COLLAPSED_ROW_COUNT);
}
@@ -98,61 +97,32 @@
final Slice wifiSlice = mWifiSlice.getSlice();
- assertTitleAndIcon(wifiSlice);
- assertToggle(wifiSlice);
+ assertWifiHeader(wifiSlice);
assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(DEFAULT_EXPANDED_ROW_COUNT);
}
@Test
public void getWifiSlice_previousExpanded_hasActiveConnection_shouldExpandSlice() {
mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
- mWifiSlice.sToggleNeeded = true;
+ mWifiSlice.sApRowCollapsed = false;
connectToWifi(makeValidatedNetworkCapabilities());
final Slice wifiSlice = mWifiSlice.getSlice();
- assertTitleAndIcon(wifiSlice);
- assertToggle(wifiSlice);
+ assertWifiHeader(wifiSlice);
assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(DEFAULT_EXPANDED_ROW_COUNT);
}
@Test
- public void getWifiSlice_previousExpanded_disableWifi_shouldHaveToggle() {
- mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
- mWifiSlice.sToggleNeeded = true;
- connectToWifi(makeValidatedNetworkCapabilities());
-
- mWifiManager.setWifiEnabled(false);
- final Slice wifiSlice = mWifiSlice.getSlice();
-
- assertTitleAndIcon(wifiSlice);
- assertToggle(wifiSlice);
- }
-
- @Test
- public void getWifiSlice_previousCollapsed_disableWifi_shouldHaveToggle() {
- mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
- mWifiSlice.sToggleNeeded = false;
- connectToWifi(makeValidatedNetworkCapabilities());
-
- mWifiManager.setWifiEnabled(false);
- final Slice wifiSlice = mWifiSlice.getSlice();
-
- assertTitleAndIcon(wifiSlice);
- assertToggle(wifiSlice);
- }
-
- @Test
public void getWifiSlice_previousCollapsed_connectionLoss_shouldCollapseSlice() {
mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
- mWifiSlice.sToggleNeeded = false;
+ mWifiSlice.sApRowCollapsed = true;
connectToWifi(makeValidatedNetworkCapabilities());
mWifiManager.disconnect();
final Slice wifiSlice = mWifiSlice.getSlice();
- assertTitleAndIcon(wifiSlice);
- assertNoToggle(wifiSlice);
+ assertWifiHeader(wifiSlice);
assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(COLLAPSED_ROW_COUNT);
}
@@ -181,7 +151,7 @@
return nc;
}
- private void assertTitleAndIcon(Slice slice) {
+ private void assertWifiHeader(Slice slice) {
final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings));
@@ -189,17 +159,8 @@
final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
R.drawable.ic_settings_wireless);
assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
- }
- private void assertToggle(Slice slice) {
- final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
final List<SliceAction> toggles = metadata.getToggles();
assertThat(toggles).hasSize(1);
}
-
- private void assertNoToggle(Slice slice) {
- final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
- final List<SliceAction> toggles = metadata.getToggles();
- assertThat(toggles).isEmpty();
- }
}