Merge "Set ClearDefaultsPreference as unselectable for talkback"
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/values/config.xml b/res/values/config.xml
index 2373b25..64d9ab7 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -201,6 +201,9 @@
<!-- Whether or not TopLevelSettings should force rounded icon for injected tiles -->
<bool name="config_force_rounded_icon_TopLevelSettings">true</bool>
+ <!-- Whether dismissal timestamp should be kept before deletion -->
+ <bool name="config_keep_contextual_card_dismissal_timestamp">false</bool>
+
<!-- Settings intelligence package name -->
<string name="config_settingsintelligence_package_name" translatable="false">
com.android.settings.intelligence
diff --git a/res/values/strings.xml b/res/values/strings.xml
index aa37f06..47b30ea 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5086,29 +5086,19 @@
<string name="daltonizer_mode_tritanomaly_summary">Tritanomaly</string>
<!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
- <plurals name="accessibilty_autoclick_preference_subtitle_extremely_short_delay">
- <item quantity="one">Extremely short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
- <item quantity="other">Extremely short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
- </plurals>
- <!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
- <plurals name="accessibilty_autoclick_preference_subtitle_very_short_delay">
- <item quantity="one">Very short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
- <item quantity="other">Very short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
- </plurals>
- <!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
<plurals name="accessibilty_autoclick_preference_subtitle_short_delay">
- <item quantity="one">Short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
- <item quantity="other">Short delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
+ <item quantity="one">Short (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
+ <item quantity="other">Short (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
+ </plurals>
+ <!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
+ <plurals name="accessibilty_autoclick_preference_subtitle_medium_delay">
+ <item quantity="one">Medium (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
+ <item quantity="other">Medium (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
</plurals>
<!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
<plurals name="accessibilty_autoclick_preference_subtitle_long_delay">
- <item quantity="one">Long delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
- <item quantity="other">Long delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
- </plurals>
- <!-- Subtitle for the accessibility preference to configure feature that performs click action soon after mouse/trackpad pointer stops moving, in case delay before click is extremely short. Placeholder will be set to the number of milliseconds to which the delay amounts. [CHAR LIMIT=NONE] -->
- <plurals name="accessibilty_autoclick_preference_subtitle_very_long_delay">
- <item quantity="one">Very long delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
- <item quantity="other">Very long delay (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
+ <item quantity="one">Long (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> second)</item>
+ <item quantity="other">Long (<xliff:g id="click_delay_label" example="200">%1$s</xliff:g> seconds)</item>
</plurals>
<!-- Summary for autoclick seekbar settings preference when user selected custom item. [CHAR LIMIT=35] -->
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 c5920f0..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"
@@ -269,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/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index de08df2..3883eba 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -30,6 +30,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources.Theme;
+import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.UserHandle;
@@ -491,7 +492,7 @@
@Override
public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
- taskDescription.setIcon(R.drawable.ic_launcher_settings);
+ taskDescription.setIcon(Icon.createWithResource(this, R.drawable.ic_launcher_settings));
super.setTaskDescription(taskDescription);
}
diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java
index 8da6fbb..76fb3ff 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtil.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtil.java
@@ -21,9 +21,11 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Resources;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.TypedValue;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.IntDef;
@@ -350,4 +352,32 @@
"Unsupported userShortcutType " + shortcutType);
}
}
+
+ /**
+ * Gets the width of the screen.
+ *
+ * @param context the current context.
+ * @return the width of the screen in terms of pixels.
+ */
+ public static int getScreenWidthPixels(Context context) {
+ final Resources resources = context.getResources();
+ final int screenWidthDp = resources.getConfiguration().screenWidthDp;
+
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp,
+ resources.getDisplayMetrics()));
+ }
+
+ /**
+ * Gets the height of the screen.
+ *
+ * @param context the current context.
+ * @return the height of the screen in terms of pixels.
+ */
+ public static int getScreenHeightPixels(Context context) {
+ final Resources resources = context.getResources();
+ final int screenHeightDp = resources.getConfiguration().screenHeightDp;
+
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
+ resources.getDisplayMetrics()));
+ }
}
diff --git a/src/com/android/settings/accessibility/AnimatedImagePreference.java b/src/com/android/settings/accessibility/AnimatedImagePreference.java
index 1609a3c..61e439f 100644
--- a/src/com/android/settings/accessibility/AnimatedImagePreference.java
+++ b/src/com/android/settings/accessibility/AnimatedImagePreference.java
@@ -34,6 +34,7 @@
public class AnimatedImagePreference extends Preference {
private Uri mImageUri;
+ private int mMaxHeight = -1;
AnimatedImagePreference(Context context) {
super(context);
@@ -45,20 +46,26 @@
super.onBindViewHolder(holder);
final ImageView imageView = holder.itemView.findViewById(R.id.animated_img);
- if (imageView != null && mImageUri != null) {
+ if (imageView == null) {
+ return;
+ }
+
+ if (mImageUri != null) {
imageView.setImageURI(mImageUri);
final Drawable drawable = imageView.getDrawable();
- if (drawable != null) {
- if (drawable instanceof AnimatedImageDrawable) {
- ((AnimatedImageDrawable) drawable).start();
- }
+ if (drawable instanceof AnimatedImageDrawable) {
+ ((AnimatedImageDrawable) drawable).start();
}
}
+
+ if (mMaxHeight > -1) {
+ imageView.setMaxWidth(mMaxHeight);
+ }
}
/**
- * Set image uri to display image in {@link ImageView}
+ * Sets image uri to display image in {@link ImageView}
*
* @param imageUri the Uri of an image
*/
@@ -68,4 +75,16 @@
notifyChanged();
}
}
+
+ /**
+ * Sets the maximum height of the view.
+ *
+ * @param maxHeight the maximum height of ImageView in terms of pixels.
+ */
+ public void setMaxHeight(int maxHeight) {
+ if (maxHeight != mMaxHeight) {
+ mMaxHeight = maxHeight;
+ notifyChanged();
+ }
+ }
}
diff --git a/src/com/android/settings/accessibility/HtmlTextPreference.java b/src/com/android/settings/accessibility/HtmlTextPreference.java
index fcf4725..4c528a3 100644
--- a/src/com/android/settings/accessibility/HtmlTextPreference.java
+++ b/src/com/android/settings/accessibility/HtmlTextPreference.java
@@ -23,9 +23,6 @@
import androidx.preference.PreferenceViewHolder;
-import java.util.List;
-import java.util.regex.Pattern;
-
/**
* A custom {@link android.widget.TextView} preference that shows html text with a custom tag
* filter.
@@ -35,7 +32,6 @@
private int mFlag = Html.FROM_HTML_MODE_COMPACT;
private Html.ImageGetter mImageGetter;
private Html.TagHandler mTagHandler;
- private List<String> mUnsupportedTagList;
HtmlTextPreference(Context context) {
super(context);
@@ -47,8 +43,8 @@
final TextView summaryView = holder.itemView.findViewById(android.R.id.summary);
if (summaryView != null && !TextUtils.isEmpty(getSummary())) {
- final String filteredText = getFilteredText(getSummary().toString());
- summaryView.setText(Html.fromHtml(filteredText, mFlag, mImageGetter, mTagHandler));
+ summaryView.setText(
+ Html.fromHtml(getSummary().toString(), mFlag, mImageGetter, mTagHandler));
}
}
@@ -87,39 +83,4 @@
notifyChanged();
}
}
-
- /**
- * Sets unsupported tag list, the text will be filtered though this list in advanced.
- *
- * @param unsupportedTagList the list of unsupported tags
- */
- public void setUnsupportedTagList(List<String> unsupportedTagList) {
- if (unsupportedTagList != null && !unsupportedTagList.equals(mUnsupportedTagList)) {
- mUnsupportedTagList = unsupportedTagList;
- notifyChanged();
- }
- }
-
- private String getFilteredText(String text) {
- if (mUnsupportedTagList == null) {
- return text;
- }
-
- int i = 1;
- for (String tag : mUnsupportedTagList) {
- if (!TextUtils.isEmpty(text)) {
- final String index = String.valueOf(i++);
- final String targetStart1 = "(?i)<" + tag + " ";
- final String targetStart2 = "(?i)<" + tag + ">";
- final String replacementStart1 = "<unsupportedtag" + index + " ";
- final String replacementStart2 = "<unsupportedtag" + index + ">";
- final String targetEnd = "(?i)</" + tag + ">";
- final String replacementEnd = "</unsupportedtag" + index + ">";
- text = Pattern.compile(targetStart1).matcher(text).replaceAll(replacementStart1);
- text = Pattern.compile(targetStart2).matcher(text).replaceAll(replacementStart2);
- text = Pattern.compile(targetEnd).matcher(text).replaceAll(replacementEnd);
- }
- }
- return text;
- }
}
diff --git a/src/com/android/settings/accessibility/ToggleAutoclickPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAutoclickPreferenceFragment.java
index 5e09b2a..6624457 100644
--- a/src/com/android/settings/accessibility/ToggleAutoclickPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAutoclickPreferenceFragment.java
@@ -64,12 +64,10 @@
* Resource ids from which autoclick preference summaries should be derived. The strings have
* placeholder for integer delay value.
*/
- private static final int[] mAutoclickPreferenceSummaries = {
- R.plurals.accessibilty_autoclick_preference_subtitle_extremely_short_delay,
- R.plurals.accessibilty_autoclick_preference_subtitle_very_short_delay,
+ private static final int[] AUTOCLICK_PREFERENCE_SUMMARIES = {
R.plurals.accessibilty_autoclick_preference_subtitle_short_delay,
- R.plurals.accessibilty_autoclick_preference_subtitle_long_delay,
- R.plurals.accessibilty_autoclick_preference_subtitle_very_long_delay
+ R.plurals.accessibilty_autoclick_preference_subtitle_medium_delay,
+ R.plurals.accessibilty_autoclick_preference_subtitle_long_delay
};
/**
@@ -86,7 +84,7 @@
// Only show integer when delay time is 1.
final String decimalFormat = (delaySecond == 1) ? "%.0f" : "%.1f";
- return resources.getQuantityString(mAutoclickPreferenceSummaries[summaryIndex],
+ return resources.getQuantityString(AUTOCLICK_PREFERENCE_SUMMARIES[summaryIndex],
quantity, String.format(decimalFormat, delaySecond));
}
@@ -98,10 +96,10 @@
return 0;
}
if (delay >= MAX_AUTOCLICK_DELAY_MS) {
- return mAutoclickPreferenceSummaries.length - 1;
+ return AUTOCLICK_PREFERENCE_SUMMARIES.length - 1;
}
int delayRange = MAX_AUTOCLICK_DELAY_MS - MIN_AUTOCLICK_DELAY_MS;
- int rangeSize = (delayRange) / (mAutoclickPreferenceSummaries.length - 1);
+ int rangeSize = (delayRange) / (AUTOCLICK_PREFERENCE_SUMMARIES.length - 1);
return (delay - MIN_AUTOCLICK_DELAY_MS) / rangeSize;
}
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 19a0f2b..838a758 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -16,6 +16,9 @@
package com.android.settings.accessibility;
+import static com.android.settings.accessibility.AccessibilityUtil.getScreenHeightPixels;
+import static com.android.settings.accessibility.AccessibilityUtil.getScreenWidthPixels;
+
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
@@ -80,7 +83,6 @@
protected CharSequence mPackageName;
protected Uri mImageUri;
protected CharSequence mHtmlDescription;
- private static final String ANCHOR_TAG = "a";
private static final String DRAWABLE_FOLDER = "drawable";
protected static final String KEY_USE_SERVICE_PREFERENCE = "use_service";
protected static final String KEY_GENERAL_CATEGORY = "general_categories";
@@ -94,9 +96,8 @@
private CheckBox mSoftwareTypeCheckBox;
private CheckBox mHardwareTypeCheckBox;
- // For html description of accessibility service, third party developer must follow the rule,
- // such as <img src="R.drawable.fileName"/>, a11y settings will get third party resources
- // by this.
+ // For html description of accessibility service, must follow the rule, such as
+ // <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
private static final String IMG_PREFIX = "R.drawable.";
private ImageView mImageGetterCacheView;
@@ -147,10 +148,12 @@
PreferenceScreen preferenceScreen = getPreferenceScreen();
if (mImageUri != null) {
+ final int screenHalfHeight = getScreenHeightPixels(getPrefContext()) / /* half */ 2;
final AnimatedImagePreference animatedImagePreference = new AnimatedImagePreference(
getPrefContext());
animatedImagePreference.setImageUri(mImageUri);
animatedImagePreference.setSelectable(false);
+ animatedImagePreference.setMaxHeight(screenHalfHeight);
preferenceScreen.addPreference(animatedImagePreference);
}
@@ -195,14 +198,9 @@
introductionCategory.setTitle(title);
preferenceScreen.addPreference(introductionCategory);
- // For accessibility service, avoid malicious links made by third party developer.
- final List<String> unsupportedTagList = new ArrayList<>();
- unsupportedTagList.add(ANCHOR_TAG);
-
final HtmlTextPreference htmlTextPreference = new HtmlTextPreference(getPrefContext());
htmlTextPreference.setSummary(mHtmlDescription);
htmlTextPreference.setImageGetter(mImageGetter);
- htmlTextPreference.setUnsupportedTagList(unsupportedTagList);
htmlTextPreference.setSelectable(false);
introductionCategory.addPreference(htmlTextPreference);
}
@@ -383,14 +381,24 @@
mImageGetterCacheView.setAdjustViewBounds(true);
mImageGetterCacheView.setImageURI(imageUri);
- final Drawable drawable = mImageGetterCacheView.getDrawable().mutate();
- if (drawable != null) {
- drawable.setBounds(/* left= */0, /* top= */0, drawable.getIntrinsicWidth(),
- drawable.getIntrinsicHeight());
+ if (mImageGetterCacheView.getDrawable() == null) {
+ return null;
}
+ final Drawable drawable =
+ mImageGetterCacheView.getDrawable().mutate().getConstantState().newDrawable();
mImageGetterCacheView.setImageURI(null);
- mImageGetterCacheView.setImageDrawable(null);
+ final int imageWidth = drawable.getIntrinsicWidth();
+ final int imageHeight = drawable.getIntrinsicHeight();
+ final int screenHalfHeight = getScreenHeightPixels(getPrefContext()) / /* half */ 2;
+ if ((imageWidth > getScreenWidthPixels(getPrefContext()))
+ || (imageHeight > screenHalfHeight)) {
+ return null;
+ }
+
+ drawable.setBounds(/* left= */0, /* top= */0, drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight());
+
return drawable;
}
diff --git a/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java
index cbb805f..34c67f1 100644
--- a/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/InstantAppDomainsPreferenceController.java
@@ -38,7 +38,8 @@
@Override
public int getAvailabilityStatus() {
- return AppUtils.isInstant(mParent.getPackageInfo().applicationInfo)
+ return mParent.getPackageInfo() != null
+ && AppUtils.isInstant(mParent.getPackageInfo().applicationInfo)
? AVAILABLE : DISABLED_FOR_USER;
}
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 07e2ecd..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));
@@ -469,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/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/homepage/contextualcards/CardContentProvider.java b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java
index 9627eb0..75ec651 100644
--- a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java
+++ b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java
@@ -26,12 +26,16 @@
import android.net.Uri;
import android.os.Build;
import android.os.StrictMode;
+import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
+import com.android.settings.R;
import com.android.settingslib.utils.ThreadUtils;
+import java.util.Map;
+
/**
* Provider stores and manages user interaction feedback for homepage contextual cards.
*/
@@ -40,10 +44,10 @@
public static final String CARD_AUTHORITY = "com.android.settings.homepage.CardContentProvider";
public static final Uri REFRESH_CARD_URI = new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority(CardContentProvider.CARD_AUTHORITY)
- .appendPath(CardDatabaseHelper.CARD_TABLE)
- .build();
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(CardContentProvider.CARD_AUTHORITY)
+ .appendPath(CardDatabaseHelper.CARD_TABLE)
+ .build();
public static final Uri DELETE_CARD_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
@@ -81,6 +85,9 @@
final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
int numInserted = 0;
final SQLiteDatabase database = mDBHelper.getWritableDatabase();
+ final boolean keepDismissalTimestampBeforeDeletion = getContext().getResources()
+ .getBoolean(R.bool.config_keep_contextual_card_dismissal_timestamp);
+ final Map<String, Long> dismissedTimeMap = new ArrayMap<>();
try {
maybeEnableStrictMode();
@@ -88,9 +95,42 @@
final String table = getTableFromMatch(uri);
database.beginTransaction();
- // Here deletion first is avoiding redundant insertion. According to cl/215350754
+ if (keepDismissalTimestampBeforeDeletion) {
+ // Query the existing db and get dismissal info.
+ final String[] columns = new String[]{CardDatabaseHelper.CardColumns.NAME,
+ CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP};
+ final String selection =
+ CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " IS NOT NULL";
+ try (Cursor cursor = database.query(table, columns, selection,
+ null/* selectionArgs */, null /* groupBy */,
+ null /* having */, null /* orderBy */)) {
+ // Save them to a Map
+ for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ final String cardName = cursor.getString(cursor.getColumnIndex(
+ CardDatabaseHelper.CardColumns.NAME));
+ final long timestamp = cursor.getLong(cursor.getColumnIndex(
+ CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP));
+ dismissedTimeMap.put(cardName, timestamp);
+ }
+ }
+ }
+
+ // Here delete data first to avoid redundant insertion. According to cl/215350754
database.delete(table, null /* whereClause */, null /* whereArgs */);
+
for (ContentValues value : values) {
+ if (keepDismissalTimestampBeforeDeletion) {
+ // Replace dismissedTimestamp in each value if there is an old one.
+ final String cardName =
+ value.get(CardDatabaseHelper.CardColumns.NAME).toString();
+ if (dismissedTimeMap.containsKey(cardName)) {
+ // Replace the value of dismissedTimestamp
+ value.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP,
+ dismissedTimeMap.get(cardName));
+ Log.d(TAG, "Replace dismissed time: " + cardName);
+ }
+ }
+
long ret = database.insert(table, null /* nullColumnHack */, value);
if (ret != -1L) {
numInserted++;
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/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index dc638a5..c7173bc 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -72,6 +72,8 @@
<!-- Whether or not extra preview panels should be used for screen zoom setting. -->
<bool name="config_enable_extra_screen_zoom_preview">false</bool>
+ <!-- Whether dismissal timestamp should be kept before deletion -->
+ <bool name="config_keep_contextual_card_dismissal_timestamp">true</bool>
<!-- List of a11y components on the device allowed to be enabled by Settings Slices -->
<string-array name="config_settings_slices_accessibility_components" translatable="false">
diff --git a/tests/robotests/src/com/android/settings/SettingsActivityTest.java b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
index feb48ab..ada4c0e 100644
--- a/tests/robotests/src/com/android/settings/SettingsActivityTest.java
+++ b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
@@ -20,7 +20,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -82,7 +82,7 @@
public void setTaskDescription_shouldUpdateIcon() {
mActivity.setTaskDescription(mTaskDescription);
- verify(mTaskDescription).setIcon(anyInt());
+ verify(mTaskDescription).setIcon(any());
}
@Test
diff --git a/tests/robotests/src/com/android/settings/accessibility/HtmlTextPreferenceTest.java b/tests/robotests/src/com/android/settings/accessibility/HtmlTextPreferenceTest.java
index 5ac5bad..63c7da1 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HtmlTextPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HtmlTextPreferenceTest.java
@@ -23,7 +23,6 @@
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.TextView;
import androidx.preference.PreferenceViewHolder;
@@ -36,9 +35,6 @@
import org.robolectric.RuntimeEnvironment;
import org.xml.sax.XMLReader;
-import java.util.ArrayList;
-import java.util.List;
-
/** Tests for {@link HtmlTextPreference} */
@RunWith(RobolectricTestRunner.class)
public final class HtmlTextPreferenceTest {
@@ -65,21 +61,13 @@
}
@Test
- public void testUnsupportedTagList_keepRealContentWithoutTag() {
- final List<String> testUnsupportedTagList = new ArrayList<>();
- testUnsupportedTagList.add("testTag");
+ public void testTagHandler() {
final String testStr = "<testTag>Real description</testTag>";
- final String expectedStr = "Real description";
- final String expectedTag = "unsupportedtag1";
- mHtmlTextPreference.setUnsupportedTagList(testUnsupportedTagList);
mHtmlTextPreference.setSummary(testStr);
mHtmlTextPreference.setTagHandler(mTagHandler);
mHtmlTextPreference.onBindViewHolder(mPreferenceViewHolder);
- final TextView summaryView = mPreferenceViewHolder.itemView.findViewById(
- android.R.id.summary);
- assertThat(summaryView.getText().toString()).isEqualTo(expectedStr);
- assertThat(mHandledTag).isEqualTo(expectedTag);
+ assertThat(mHandledTag).isEqualTo("testTag");
}
}
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/homepage/contextualcards/CardContentProviderTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java
index d13c97c..32af5d8 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java
@@ -38,6 +38,7 @@
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
@@ -86,6 +87,25 @@
}
@Test
+ @Config(qualifiers = "mcc999")
+ public void bulkInsert_keepDismissalTimestamp_shouldHaveTimestamp() {
+ mResolver.bulkInsert(mUri, generateTwoRowsWithDismissTimestamp());
+
+ mResolver.bulkInsert(mUri, generateTwoRows());
+
+ assertThat(queryDismissedTimestamp()).isEqualTo(10001L);
+ }
+
+ @Test
+ public void bulkInsert_notKeepDismissalTimestamp_shouldNotHaveTimestamp() {
+ mResolver.bulkInsert(mUri, generateTwoRowsWithDismissTimestamp());
+
+ mResolver.bulkInsert(mUri, generateTwoRows());
+
+ assertThat(queryDismissedTimestamp()).isEqualTo(0L);
+ }
+
+ @Test
public void cardData_query() {
mResolver.insert(mUri, generateOneRow());
final int count = getRowCount();
@@ -198,10 +218,40 @@
return twoRows;
}
+ private ContentValues[] generateTwoRowsWithDismissTimestamp() {
+ final ContentValues[] twoRows = new ContentValues[2];
+ twoRows[0] = generateOneRow();
+
+ final ContentValues values = new ContentValues();
+ values.put(CardDatabaseHelper.CardColumns.NAME, "toggle_airplane");
+ values.put(CardDatabaseHelper.CardColumns.TYPE, 1);
+ values.put(CardDatabaseHelper.CardColumns.SCORE, 0.95);
+ values.put(CardDatabaseHelper.CardColumns.SLICE_URI,
+ "content://com.android.settings.slices/action/toggle_airplane");
+ 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.DISMISSED_TIMESTAMP, 10001L);
+ twoRows[1] = values;
+
+ return twoRows;
+ }
+
private int getRowCount() {
final Cursor cr = mResolver.query(mUri, null, null, null);
final int count = cr.getCount();
cr.close();
return count;
}
+
+ private long queryDismissedTimestamp() {
+ final String[] columns = {CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP};
+ final String selection = CardDatabaseHelper.CardColumns.NAME + "=?";
+ final String[] selectionArgs = {"toggle_airplane"};
+ final Cursor cr = mResolver.query(mUri, columns, selection, selectionArgs, null);
+ cr.moveToFirst();
+ final long dismissedTimestamp = cr.getLong(0);
+ cr.close();
+ return dismissedTimestamp;
+ }
}
\ No newline at end of file