Merge "Update screen lock complexity permission to password complexity"
diff --git a/res/layout/homepage_condition_footer.xml b/res/layout/homepage_condition_footer.xml
index 56687fe..cc84f52 100644
--- a/res/layout/homepage_condition_footer.xml
+++ b/res/layout/homepage_condition_footer.xml
@@ -29,6 +29,7 @@
android:id="@+id/collapse_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@drawable/ic_expand_less"/>
+ android:src="@drawable/ic_expand_less"
+ android:tint="?android:attr/colorAccent"/>
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/homepage_condition_header.xml b/res/layout/homepage_condition_header.xml
index a2796ec..5c1b181 100644
--- a/res/layout/homepage_condition_header.xml
+++ b/res/layout/homepage_condition_header.xml
@@ -45,7 +45,8 @@
android:paddingTop="@dimen/homepage_condition_header_indicator_padding_top"
android:paddingStart="@dimen/homepage_condition_header_indicator_padding_start"
android:paddingEnd="@dimen/homepage_condition_header_indicator_padding_end"
- android:src="@*android:drawable/ic_expand_more"/>
+ android:src="@*android:drawable/ic_expand_more"
+ android:tint="?android:attr/colorAccent"/>
</LinearLayout>
diff --git a/res/layout/wifi_button_preference_widget.xml b/res/layout/wifi_button_preference_widget.xml
index 1ecb98c..0999d20 100644
--- a/res/layout/wifi_button_preference_widget.xml
+++ b/res/layout/wifi_button_preference_widget.xml
@@ -23,5 +23,4 @@
android:minHeight="@dimen/min_tap_target_size"
android:layout_gravity="center"
android:background="@null"
- android:visibility="gone"
- android:contentDescription="@string/wifi_add_network" />
+ android:visibility="gone"/>
diff --git a/res/layout/wifi_dialog.xml b/res/layout/wifi_dialog.xml
index 1ae3bf5..333296c 100644
--- a/res/layout/wifi_dialog.xml
+++ b/res/layout/wifi_dialog.xml
@@ -71,7 +71,7 @@
android:layout_centerVertical="true"
android:background="@null"
android:src="@drawable/ic_scan_24dp"
- android:contentDescription="@string/wifi_add_network" />
+ android:contentDescription="@string/wifi_dpp_scan_qr_code"/>
</RelativeLayout>
<LinearLayout android:id="@+id/ssid_too_long_warning"
@@ -307,7 +307,7 @@
android:layout_centerVertical="true"
android:background="@null"
android:src="@drawable/ic_scan_24dp"
- android:contentDescription="@string/wifi_add_network" />
+ android:contentDescription="@string/wifi_dpp_scan_qr_code"/>
</RelativeLayout>
</LinearLayout>
diff --git a/res/layout/wifi_dpp_fragment_header.xml b/res/layout/wifi_dpp_fragment_header.xml
index e8e71d1..364f360 100644
--- a/res/layout/wifi_dpp_fragment_header.xml
+++ b/res/layout/wifi_dpp_fragment_header.xml
@@ -40,27 +40,35 @@
android:src="@drawable/ic_devices_check_circle_green"
android:scaleType="fitCenter"/>
- <TextView
- android:id="@android:id/title"
- style="@style/TextAppearance.EntityHeaderTitle"
- android:layout_width="match_parent"
+ <!-- Add title_summary_container to group content for Talkback -->
+ <LinearLayout
+ android:id="@+id/title_summary_container"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:textAlignment="center"
- android:layout_marginTop="8dp"
- android:paddingStart="32dp"
- android:paddingEnd="32dp"/>
+ android:orientation="vertical"
+ android:focusable="true">
- <TextView
- android:id="@android:id/summary"
- style="@style/TextAppearance.EntityHeaderSummary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="false"
- android:gravity="center_horizontal"
- android:textAlignment="center"
- android:layout_marginTop="2dp"
- android:paddingStart="32dp"
- android:paddingEnd="32dp"/>
+ <TextView
+ android:id="@android:id/title"
+ style="@style/TextAppearance.EntityHeaderTitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textAlignment="center"
+ android:layout_marginTop="8dp"
+ android:paddingStart="32dp"
+ android:paddingEnd="32dp"/>
+ <TextView
+ android:id="@android:id/summary"
+ style="@style/TextAppearance.EntityHeaderSummary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:gravity="center_horizontal"
+ android:textAlignment="center"
+ android:layout_marginTop="2dp"
+ android:paddingStart="32dp"
+ android:paddingEnd="32dp"/>
+ </LinearLayout>
</LinearLayout>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index dc91f22..7e0ba10 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1365,4 +1365,17 @@
<item>"9"</item>
</string-array>
+ <!-- WiFi calling mode array -->
+ <string-array name="wifi_calling_mode_summaries" translatable="false">
+ <item>@string/wifi_calling_mode_wifi_preferred_summary</item>
+ <item>@string/wifi_calling_mode_cellular_preferred_summary</item>
+ <item>@string/wifi_calling_mode_wifi_only_summary</item>
+ </string-array>
+
+ <!-- WiFi calling mode array without wifi only mode -->
+ <string-array name="wifi_calling_mode_summaries_without_wifi_only" translatable="false">
+ <item>@string/wifi_calling_mode_wifi_preferred_summary</item>
+ <item>@string/wifi_calling_mode_cellular_preferred_summary</item>
+ </string-array>
+
</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index c7bf1c7..e099cf0 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -132,6 +132,11 @@
<attr name="aspectRatio" format="float" />
</declare-styleable>
+ <declare-styleable name="ListWithEntrySummaryPreference">
+ <!-- Summaries of entry -->
+ <attr name="entrySummaries" format="reference" />
+ </declare-styleable>
+
<!-- For UsageView -->
<declare-styleable name="UsageView">
<attr name="android:colorAccent" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index aeeb403..a36d8ab 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -279,6 +279,15 @@
<dimen name="password_requirement_textsize">14sp</dimen>
+ <!-- Visible vertical space we want to show below password edittext field when ime is shown.
+ The unit is sp as it is related to the text size of password requirement item. -->
+ <dimen name="visible_vertical_space_below_password">20sp</dimen>
+
+ <!-- Select dialog -->
+ <dimen name="select_dialog_padding_start">20dp</dimen>
+ <dimen name="select_dialog_item_margin_start">12dp</dimen>
+ <dimen name="select_dialog_summary_padding_bottom">8dp</dimen>
+
<!-- Padding between the donut and the storage summary. -->
<dimen name="storage_summary_padding_end">16dp</dimen>
<!-- Text size of the big number in the donut. -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f3fe1f0..b912c9d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2157,6 +2157,10 @@
<string name="wifi_dpp_sharing_wifi_with_this_device">Sharing Wi\u2011Fi with this device\u2026</string>
<!-- Hint for Wi-Fi DPP handshake running [CHAR LIMIT=NONE] -->
<string name="wifi_dpp_connecting">Connecting\u2026</string>
+ <!-- Title for the fragment to show that the QR code is for sharing Wi-Fi hotspot network [CHAR LIMIT=50] -->
+ <string name="wifi_dpp_share_hotspot">Share hotspot</string>
+ <!-- Hint for the user to share Wi-Fi hotspot network [CHAR LIMIT=NONE] -->
+ <string name="wifi_dpp_scan_qr_code_to_share_hotspot">Scan this QR code with another device to join hotspot \u201c<xliff:g id="ssid" example="OfficeWifi">%1$s</xliff:g>\u201d</string>
<!-- Label for the try again button [CHAR LIMIT=20]-->
<string name="retry">Retry</string>
<!-- Label for the check box to share a network with other users on the same device -->
@@ -2221,6 +2225,14 @@
<!-- Button label to connect to a Wi-Fi network -->
<string name="wifi_connect">Connect</string>
+ <!-- Turned on notification for Wi-Fi [CHAR LIMIT=40] -->
+ <string name="wifi_turned_on_message">Wi\u2011Fi turned on</string>
+ <!-- Connected to notification for Wi-Fi [CHAR LIMIT=NONE] -->
+ <string name="wifi_connected_to_message">@string/bluetooth_connected_summary</string>
+ <!-- Button label to connecting progress to a Wi-Fi network [CHAR LIMIT=20] -->
+ <string name="wifi_connecting">Connecting\u2026</string>
+ <!-- Button label to disconnect to a Wi-Fi network [CHAR LIMIT=NONE] -->
+ <string name="wifi_disconnect">@string/bluetooth_device_context_disconnect</string>
<!-- Failured notification for connect -->
<string name="wifi_failed_connect_message">Failed to connect to network</string>
<!-- Button label to delete a Wi-Fi network -->
@@ -2476,7 +2488,7 @@
<!-- Title of WFC preference item [CHAR LIMIT=30] -->
<string name="wifi_calling_mode_title">Calling preference</string>
<!-- Title of WFC preference selection dialog [CHAR LIMIT=30] -->
- <string name="wifi_calling_mode_dialog_title">Wi-Fi calling mode</string>
+ <string name="wifi_calling_mode_dialog_title">Calling preference</string>
<!-- Title of WFC roaming preference item [CHAR LIMIT=45] -->
<string name="wifi_calling_roaming_mode_title">Roaming preference</string>
<!-- Summary of WFC roaming preference item [CHAR LIMIT=NONE]-->
@@ -2484,9 +2496,9 @@
<!-- WFC mode dialog [CHAR LIMIT=45] -->
<string name="wifi_calling_roaming_mode_dialog_title">Roaming preference</string>
<string-array name="wifi_calling_mode_choices">
- <item>Wi-Fi preferred</item>
- <item>Mobile preferred</item>
- <item>Wi-Fi only</item>
+ <item>@*android:string/wfc_mode_wifi_preferred_summary</item>
+ <item>@*android:string/wfc_mode_cellular_preferred_summary</item>
+ <item>@*android:string/wfc_mode_wifi_only_summary</item>
</string-array>
<string-array name="wifi_calling_mode_choices_v2">
<item>Wi-Fi</item>
@@ -2499,8 +2511,8 @@
<item>"0"</item>
</string-array>
<string-array name="wifi_calling_mode_choices_without_wifi_only">
- <item>Wi-Fi preferred</item>
- <item>Mobile preferred</item>
+ <item>@*android:string/wfc_mode_wifi_preferred_summary</item>
+ <item>@*android:string/wfc_mode_cellular_preferred_summary</item>
</string-array>
<string-array name="wifi_calling_mode_choices_v2_without_wifi_only">
<item>Wi-Fi</item>
@@ -2510,6 +2522,14 @@
<item>"2"</item>
<item>"1"</item>
</string-array>
+
+ <!-- Summary of WFC preference item on the WFC preference selection dialog. [CHAR LIMIT=70]-->
+ <string name="wifi_calling_mode_wifi_preferred_summary">If Wi\u2011Fi is unavailable, use mobile network</string>
+ <!-- Summary of WFC preference item on the WFC preference selection dialog. [CHAR LIMIT=70]-->
+ <string name="wifi_calling_mode_cellular_preferred_summary">If mobile network is unavailable, use Wi\u2011Fi</string>
+ <!-- Summary of WFC preference item on the WFC preference selection dialog. [CHAR LIMIT=70]-->
+ <string name="wifi_calling_mode_wifi_only_summary">Call over Wi\u2011Fi. If Wi\u2011Fi is lost, call will end.</string>
+
<!-- Wi-Fi Calling settings. Text displayed when Wi-Fi Calling is off -->
<string name="wifi_calling_off_explanation">When Wi-Fi calling is on, your phone can route calls via Wi-Fi networks or your carrier\u2019s network, depending on your preference and which signal is stronger. Before turning on this feature, check with your carrier regarding fees and other details.<xliff:g id="additional_text" example="Learn More">%1$s</xliff:g></string>
<!-- Wi-Fi Calling settings. Additional text displayed when Wi-Fi Calling is off. Default empty. [CHAR LIMIT=NONE] -->
@@ -10666,9 +10686,6 @@
<!-- Title for the Volume dialog (settings panel) with all volume streams[CHAR LIMIT=50] -->
<string name="volume_connectivity_panel_title">Volume</string>
- <!-- Subtitle explaining that mobile data cannot be used while airplane mode is on [CHAR LIMIT=50] -->
- <string name="mobile_data_ap_mode_disabled">Unavailable during airplane mode</string>
-
<!-- UI debug setting: force desktop mode [CHAR LIMIT=50] -->
<string name="force_desktop_mode">Force desktop mode</string>
<!-- UI debug setting: force desktop mode summary [CHAR LIMIT=NONE] -->
diff --git a/res/xml/single_choice_list_item_2.xml b/res/xml/single_choice_list_item_2.xml
new file mode 100644
index 0000000..ca80f44
--- /dev/null
+++ b/res/xml/single_choice_list_item_2.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/select_dialog_padding_start"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:orientation="horizontal"
+ android:descendantFocusability="blocksDescendants">
+
+ <RadioButton
+ android:id="@+id/radio"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:clickable="false" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:layout_marginStart="@dimen/select_dialog_item_margin_start"
+ android:layout_gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@+id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/select_dialog_summary_padding_bottom"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10" />
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml
index 81a0453..57bee1f 100644
--- a/res/xml/sound_settings.xml
+++ b/res/xml/sound_settings.xml
@@ -41,7 +41,7 @@
settings:controller="com.android.settings.notification.MediaVolumePreferenceController"/>
<!-- Media output switcher -->
- <ListPreference
+ <Preference
android:key="media_output"
android:title="@string/media_output_title"
android:dialogTitle="@string/media_output_title"
diff --git a/res/xml/wifi_calling_settings.xml b/res/xml/wifi_calling_settings.xml
index 0adb1e8..0276bdb 100644
--- a/res/xml/wifi_calling_settings.xml
+++ b/res/xml/wifi_calling_settings.xml
@@ -15,24 +15,27 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="wifi_calling_settings"
android:title="@string/wifi_calling_settings_title">
- <ListPreference
+ <com.android.settings.wifi.calling.ListWithEntrySummaryPreference
android:key="wifi_calling_mode"
android:title="@string/wifi_calling_mode_title"
android:summary="@string/wifi_calling_mode_title"
android:entries="@array/wifi_calling_mode_choices"
android:entryValues="@array/wifi_calling_mode_values"
- android:dialogTitle="@string/wifi_calling_mode_dialog_title" />
+ android:dialogTitle="@string/wifi_calling_mode_dialog_title"
+ settings:entrySummaries="@array/wifi_calling_mode_summaries" />
- <ListPreference
+ <com.android.settings.wifi.calling.ListWithEntrySummaryPreference
android:key="wifi_calling_roaming_mode"
android:title="@string/wifi_calling_roaming_mode_title"
android:summary="@string/wifi_calling_roaming_mode_summary"
android:entries="@array/wifi_calling_mode_choices_v2"
android:entryValues="@array/wifi_calling_mode_values"
- android:dialogTitle="@string/wifi_calling_roaming_mode_dialog_title" />
+ android:dialogTitle="@string/wifi_calling_roaming_mode_dialog_title"
+ settings:entrySummaries="@array/wifi_calling_mode_summaries" />
<Preference
android:key="emergency_address_key"
diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml
index 6c5e3c4..81e60a2 100644
--- a/res/xml/wifi_tether_settings.xml
+++ b/res/xml/wifi_tether_settings.xml
@@ -23,7 +23,7 @@
settings:searchable="false"
settings:initialExpandedChildrenCount="3">
- <com.android.settings.widget.ValidatedEditTextPreference
+ <com.android.settings.wifi.tether.WifiTetherSsidPreference
android:key="wifi_tether_network_name"
android:title="@string/wifi_hotspot_name_title"
android:summary="@string/summary_placeholder"/>
diff --git a/src/com/android/settings/applications/defaultapps/DefaultEmergencyPicker.java b/src/com/android/settings/applications/defaultapps/DefaultEmergencyPicker.java
index f088967..32cc9a8 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultEmergencyPicker.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultEmergencyPicker.java
@@ -17,9 +17,7 @@
package com.android.settings.applications.defaultapps;
import android.app.role.RoleManager;
-import android.app.role.RoleManagerCallback;
import android.app.settings.SettingsEnums;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -27,7 +25,6 @@
import android.content.pm.ResolveInfo;
import android.os.AsyncTask;
import android.os.Process;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -100,18 +97,13 @@
final String previousValue = getDefaultKey();
if (!TextUtils.isEmpty(key) && !TextUtils.equals(key, previousValue)) {
- getContext().getSystemService(RoleManager.class)
- .addRoleHolderAsUser(
- RoleManager.ROLE_EMERGENCY, key, 0, Process.myUserHandle(),
- AsyncTask.THREAD_POOL_EXECUTOR, new RoleManagerCallback() {
- @Override
- public void onSuccess() {}
-
- @Override
- public void onFailure() {
- Log.e(TAG, "Failed to set emergency default app.");
- }
- });
+ getContext().getSystemService(RoleManager.class).addRoleHolderAsUser(
+ RoleManager.ROLE_EMERGENCY, key, 0, Process.myUserHandle(),
+ AsyncTask.THREAD_POOL_EXECUTOR, successful -> {
+ if (!successful) {
+ Log.e(TAG, "Failed to set emergency default app.");
+ }
+ });
return true;
}
return false;
diff --git a/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java b/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java
index 5423ce3..fe68d02 100644
--- a/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java
+++ b/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java
@@ -51,6 +51,9 @@
@VisibleForTesting
boolean isCardEligibleToDisplay(ContextualCard card) {
+ if (card.getRankingScore() < 0) {
+ return false;
+ }
if (card.isCustomCard()) {
return true;
}
diff --git a/src/com/android/settings/media/MediaDeviceUpdateWorker.java b/src/com/android/settings/media/MediaDeviceUpdateWorker.java
index 7416018..d1e55e4 100644
--- a/src/com/android/settings/media/MediaDeviceUpdateWorker.java
+++ b/src/com/android/settings/media/MediaDeviceUpdateWorker.java
@@ -70,7 +70,7 @@
@Override
public void close() {
-
+ mLocalMediaManager = null;
}
@Override
diff --git a/src/com/android/settings/media/MediaOutputSlice.java b/src/com/android/settings/media/MediaOutputSlice.java
index d52b441..232986c 100644
--- a/src/com/android/settings/media/MediaOutputSlice.java
+++ b/src/com/android/settings/media/MediaOutputSlice.java
@@ -20,13 +20,11 @@
import android.annotation.ColorInt;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.UserHandle;
-import android.util.IconDrawableFactory;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -58,37 +56,48 @@
private MediaDeviceUpdateWorker mWorker;
private String mPackageName;
- private IconDrawableFactory mIconDrawableFactory;
public MediaOutputSlice(Context context) {
mContext = context;
mPackageName = getUri().getQueryParameter(MEDIA_PACKAGE_NAME);
- mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
}
@VisibleForTesting
- void init(String packageName, MediaDeviceUpdateWorker worker, IconDrawableFactory factory) {
+ void init(String packageName, MediaDeviceUpdateWorker worker) {
mPackageName = packageName;
mWorker = worker;
- mIconDrawableFactory = factory;
}
@Override
public Slice getSlice() {
- final PackageManager pm = mContext.getPackageManager();
+ final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (!adapter.isEnabled()) {
+ Log.d(TAG, "getSlice() Bluetooth is off");
+ return null;
+ }
final List<MediaDevice> devices = getMediaDevices();
- final CharSequence title = Utils.getApplicationLabel(mContext, mPackageName);
- final CharSequence summary =
- mContext.getString(R.string.media_output_panel_summary_of_playing_device,
- getConnectedDeviceName());
-
- final Drawable drawable =
- Utils.getBadgedIcon(mIconDrawableFactory, pm, mPackageName, UserHandle.myUserId());
- final IconCompat icon = Utils.createIconWithDrawable(drawable);
-
@ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
- final SliceAction primarySliceAction = SliceAction.createDeeplink(getPrimaryAction(), icon,
+
+ final MediaDevice connectedDevice = getWorker().getCurrentConnectedMediaDevice();
+ final ListBuilder listBuilder = buildActiveDeviceHeader(color, connectedDevice);
+
+ for (MediaDevice device : devices) {
+ if (!TextUtils.equals(connectedDevice.getId(), device.getId())) {
+ listBuilder.addRow(getMediaDeviceRow(device));
+ }
+ }
+
+ return listBuilder.build();
+ }
+
+ private ListBuilder buildActiveDeviceHeader(@ColorInt int color, MediaDevice device) {
+ final String title = device.getName();
+ final IconCompat icon = IconCompat.createWithResource(mContext, device.getIcon());
+
+ final PendingIntent broadcastAction =
+ getBroadcastIntent(mContext, device.getId(), device.hashCode());
+ final SliceAction primarySliceAction = SliceAction.createDeeplink(broadcastAction, icon,
ListBuilder.ICON_IMAGE, title);
final ListBuilder listBuilder = new ListBuilder(mContext, MEDIA_OUTPUT_SLICE_URI,
@@ -97,14 +106,10 @@
.addRow(new ListBuilder.RowBuilder()
.setTitleItem(icon, ListBuilder.ICON_IMAGE)
.setTitle(title)
- .setSubtitle(summary)
+ .setSubtitle(device.getSummary())
.setPrimaryAction(primarySliceAction));
- for (MediaDevice device : devices) {
- listBuilder.addRow(getMediaDeviceRow(device));
- }
-
- return listBuilder.build();
+ return listBuilder;
}
private MediaDeviceUpdateWorker getWorker() {
@@ -120,18 +125,6 @@
return devices;
}
- private String getConnectedDeviceName() {
- final MediaDevice device = getWorker().getCurrentConnectedMediaDevice();
- return device != null ? device.getName() : "";
- }
-
- private PendingIntent getPrimaryAction() {
- final PackageManager pm = mContext.getPackageManager();
- final Intent launchIntent = pm.getLaunchIntentForPackage(mPackageName);
- final Intent intent = launchIntent;
- return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */);
- }
-
private ListBuilder.RowBuilder getMediaDeviceRow(MediaDevice device) {
final String title = device.getName();
final PendingIntent broadcastAction =
@@ -141,7 +134,8 @@
.setTitleItem(deviceIcon, ListBuilder.ICON_IMAGE)
.setPrimaryAction(SliceAction.create(broadcastAction, deviceIcon,
ListBuilder.ICON_IMAGE, title))
- .setTitle(title);
+ .setTitle(title)
+ .setSubtitle(device.getSummary());
return rowBuilder;
}
diff --git a/src/com/android/settings/network/telephony/MobileDataSlice.java b/src/com/android/settings/network/telephony/MobileDataSlice.java
index 5a5d6a2..6efd8c3 100644
--- a/src/com/android/settings/network/telephony/MobileDataSlice.java
+++ b/src/com/android/settings/network/telephony/MobileDataSlice.java
@@ -74,27 +74,8 @@
final IconCompat icon = IconCompat.createWithResource(mContext,
R.drawable.ic_network_cell);
final String title = mContext.getText(R.string.mobile_data_settings_title).toString();
- @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
-
- // Return a Slice without the mobile data toggle when airplane mode is on.
- if (isAirplaneModeEnabled()) {
- final CharSequence summary = mContext.getText(R.string.mobile_data_ap_mode_disabled);
- // Intent does nothing, but we have to pass an intent to the Row.
- final PendingIntent intent = PendingIntent.getActivity(mContext, 0 /* requestCode */,
- new Intent(), 0 /* flags */);
- final SliceAction deadAction =
- SliceAction.create(intent, icon, ListBuilder.ICON_IMAGE, title);
- final ListBuilder listBuilder = new ListBuilder(mContext, getUri(),
- ListBuilder.INFINITY)
- .setAccentColor(color)
- .addRow(new ListBuilder.RowBuilder()
- .setTitle(title)
- .setSubtitle(summary)
- .setPrimaryAction(deadAction));
- return listBuilder.build();
- }
-
final CharSequence summary = getSummary();
+ @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
final PendingIntent toggleAction = getBroadcastIntent(mContext);
final PendingIntent primaryAction = getPrimaryAction();
final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon,
@@ -120,6 +101,11 @@
@Override
public void onNotifyChange(Intent intent) {
+ // Don't make a change if we are in Airplane Mode.
+ if (isAirplaneModeEnabled()) {
+ return;
+ }
+
final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
isMobileDataEnabled());
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index b26b921..eec0fb8 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -38,7 +38,6 @@
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.sound.HandsFreeProfileOutputPreferenceController;
-import com.android.settings.sound.MediaOutputPreferenceController;
import com.android.settings.widget.PreferenceCategoryController;
import com.android.settings.widget.UpdatableListPreferenceDialogFragment;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -77,7 +76,6 @@
private RingtonePreference mRequestPreference;
private UpdatableListPreferenceDialogFragment mDialogFragment;
- private String mMediaOutputControllerKey;
private String mHfpOutputControllerKey;
@Override
@@ -132,8 +130,6 @@
final int metricsCategory;
if (mHfpOutputControllerKey.equals(preference.getKey())) {
metricsCategory = SettingsEnums.DIALOG_SWITCH_HFP_DEVICES;
- } else if (mMediaOutputControllerKey.equals(preference.getKey())) {
- metricsCategory = SettingsEnums.DIALOG_SWITCH_A2DP_DEVICES;
} else {
metricsCategory = Instrumentable.METRICS_CATEGORY_UNKNOWN;
}
@@ -186,9 +182,6 @@
volumeControllers.add(use(CallVolumePreferenceController.class));
volumeControllers.add(use(RemoteVolumePreferenceController.class));
- use(MediaOutputPreferenceController.class).setCallback(listPreference ->
- onPreferenceDataChanged(listPreference));
- mMediaOutputControllerKey = use(MediaOutputPreferenceController.class).getPreferenceKey();
use(HandsFreeProfileOutputPreferenceController.class).setCallback(listPreference ->
onPreferenceDataChanged(listPreference));
mHfpOutputControllerKey =
diff --git a/src/com/android/settings/panel/SettingsPanelActivity.java b/src/com/android/settings/panel/SettingsPanelActivity.java
index 3819c80..8aee382 100644
--- a/src/com/android/settings/panel/SettingsPanelActivity.java
+++ b/src/com/android/settings/panel/SettingsPanelActivity.java
@@ -16,13 +16,11 @@
package com.android.settings.panel;
-import static com.android.settingslib.media.MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT;
import static com.android.settingslib.media.MediaOutputSliceConstants.EXTRA_PACKAGE_NAME;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.os.Bundle;
-import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -75,15 +73,8 @@
return;
}
- final String mediaPackageName =
- callingIntent.getStringExtra(EXTRA_PACKAGE_NAME);
-
- if (TextUtils.equals(ACTION_MEDIA_OUTPUT, callingIntent.getAction())
- && TextUtils.isEmpty(mediaPackageName)) {
- Log.e(TAG, "Missing EXTRA_PACKAGE_NAME, closing Panel Activity");
- finish();
- return;
- }
+ // We will use it once media output switch panel support remote device.
+ final String mediaPackageName = callingIntent.getStringExtra(EXTRA_PACKAGE_NAME);
setContentView(R.layout.settings_panel);
diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
index f77dfca..0da0f21 100644
--- a/src/com/android/settings/sound/AudioSwitchPreferenceController.java
+++ b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
@@ -32,7 +32,6 @@
import android.media.MediaRouter.Callback;
import android.os.Handler;
import android.os.Looper;
-import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
@@ -40,7 +39,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.FeatureFlags;
@@ -63,15 +61,11 @@
/**
* Abstract class for audio switcher controller to notify subclass
* updating the current status of switcher entry. Subclasses must overwrite
- * {@link #setActiveBluetoothDevice(BluetoothDevice)} to set the
- * active device for corresponding profile.
*/
public abstract class AudioSwitchPreferenceController extends BasePreferenceController
- implements Preference.OnPreferenceChangeListener, BluetoothCallback,
- LifecycleObserver, OnStart, OnStop {
+ implements BluetoothCallback, LifecycleObserver, OnStart, OnStop {
private static final String TAG = "AudioSwitchPrefCtrl";
- private static final int INVALID_INDEX = -1;
protected final List<BluetoothDevice> mConnectedDevices;
protected final AudioManager mAudioManager;
@@ -129,35 +123,6 @@
}
@Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final String address = (String) newValue;
- if (!(preference instanceof ListPreference)) {
- return false;
- }
-
- final ListPreference listPreference = (ListPreference) preference;
- if (TextUtils.equals(address, mContext.getText(R.string.media_output_default_summary))) {
- // Switch to default device which address is device name
- mSelectedIndex = getDefaultDeviceIndex();
- setActiveBluetoothDevice(null);
- listPreference.setSummary(mContext.getText(R.string.media_output_default_summary));
- } else {
- // Switch to BT device which address is hardware address
- final int connectedDeviceIndex = getConnectedDeviceIndex(address);
- if (connectedDeviceIndex == INVALID_INDEX) {
- return false;
- }
- final BluetoothDevice btDevice = mConnectedDevices.get(connectedDeviceIndex);
- mSelectedIndex = connectedDeviceIndex;
- setActiveBluetoothDevice(btDevice);
- listPreference.setSummary(btDevice.getAliasName());
- }
- return true;
- }
-
- public abstract void setActiveBluetoothDevice(BluetoothDevice device);
-
- @Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(mPreferenceKey);
@@ -185,6 +150,12 @@
}
@Override
+ public void onBluetoothStateChanged(int bluetoothState) {
+ // To handle the case that Bluetooth on and no connected devices
+ updateState(mPreference);
+ }
+
+ @Override
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
updateState(mPreference);
}
@@ -236,21 +207,15 @@
}
/**
- * get A2dp connected device
+ * get A2dp devices on all states
+ * (STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED, STATE_DISCONNECTING)
*/
- protected List<BluetoothDevice> getConnectedA2dpDevices() {
- final List<BluetoothDevice> connectedDevices = new ArrayList<>();
+ protected List<BluetoothDevice> getConnectableA2dpDevices() {
final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
if (a2dpProfile == null) {
- return connectedDevices;
+ return new ArrayList<>();
}
- final List<BluetoothDevice> devices = a2dpProfile.getConnectedDevices();
- for (BluetoothDevice device : devices) {
- if (device.isConnected()) {
- connectedDevices.add(device);
- }
- }
- return connectedDevices;
+ return a2dpProfile.getConnectableDevices();
}
/**
@@ -277,6 +242,31 @@
}
/**
+ * get hearing aid profile devices on all states
+ * (STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED, STATE_DISCONNECTING)
+ * exclude other devices with same hiSyncId.
+ */
+ protected List<BluetoothDevice> getConnectableHearingAidDevices() {
+ final List<BluetoothDevice> connectedDevices = new ArrayList<>();
+ final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+ if (hapProfile == null) {
+ return connectedDevices;
+ }
+ final List<Long> devicesHiSyncIds = new ArrayList<>();
+ final List<BluetoothDevice> devices = hapProfile.getConnectableDevices();
+ for (BluetoothDevice device : devices) {
+ final long hiSyncId = hapProfile.getHiSyncId(device);
+ // device with same hiSyncId should not be shown in the UI.
+ // So do not add it into connectedDevices.
+ if (!devicesHiSyncIds.contains(hiSyncId)) {
+ devicesHiSyncIds.add(hiSyncId);
+ connectedDevices.add(device);
+ }
+ }
+ return connectedDevices;
+ }
+
+ /**
* Find active hearing aid device
*/
protected BluetoothDevice findActiveHearingAidDevice() {
@@ -306,52 +296,6 @@
*/
public abstract BluetoothDevice findActiveDevice();
- int getDefaultDeviceIndex() {
- // Default device is after all connected devices.
- return mConnectedDevices.size();
- }
-
- void setupPreferenceEntries(CharSequence[] mediaOutputs, CharSequence[] mediaValues,
- BluetoothDevice activeDevice) {
- // default to current device
- mSelectedIndex = getDefaultDeviceIndex();
- // default device is after all connected devices.
- mediaOutputs[mSelectedIndex] = mContext.getText(R.string.media_output_default_summary);
- // use default device name as address
- mediaValues[mSelectedIndex] = mContext.getText(R.string.media_output_default_summary);
- for (int i = 0, size = mConnectedDevices.size(); i < size; i++) {
- final BluetoothDevice btDevice = mConnectedDevices.get(i);
- mediaOutputs[i] = btDevice.getAliasName();
- mediaValues[i] = btDevice.getAddress();
- if (btDevice.equals(activeDevice)) {
- // select the active connected device.
- mSelectedIndex = i;
- }
- }
- }
-
- void setPreference(CharSequence[] mediaOutputs, CharSequence[] mediaValues,
- Preference preference) {
- final ListPreference listPreference = (ListPreference) preference;
- listPreference.setEntries(mediaOutputs);
- listPreference.setEntryValues(mediaValues);
- listPreference.setValueIndex(mSelectedIndex);
- listPreference.setSummary(mediaOutputs[mSelectedIndex]);
- mAudioSwitchPreferenceCallback.onPreferenceDataChanged(listPreference);
- }
-
- private int getConnectedDeviceIndex(String hardwareAddress) {
- if (mConnectedDevices != null) {
- for (int i = 0, size = mConnectedDevices.size(); i < size; i++) {
- final BluetoothDevice btDevice = mConnectedDevices.get(i);
- if (TextUtils.equals(btDevice.getAddress(), hardwareAddress)) {
- return i;
- }
- }
- }
- return INVALID_INDEX;
- }
-
private void register() {
mLocalBluetoothManager.getEventManager().registerCallback(this);
mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler);
diff --git a/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java b/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
index a02c0b2..9157477 100644
--- a/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
+++ b/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceController.java
@@ -20,7 +20,9 @@
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.text.TextUtils;
+import androidx.preference.ListPreference;
import androidx.preference.Preference;
import com.android.settings.R;
@@ -32,14 +34,56 @@
* This class allows switching between HFP-connected & HAP-connected BT devices
* while in on-call state.
*/
-public class HandsFreeProfileOutputPreferenceController extends
- AudioSwitchPreferenceController {
+public class HandsFreeProfileOutputPreferenceController extends AudioSwitchPreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private static final int INVALID_INDEX = -1;
public HandsFreeProfileOutputPreferenceController(Context context, String key) {
super(context, key);
}
@Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final String address = (String) newValue;
+ if (!(preference instanceof ListPreference)) {
+ return false;
+ }
+
+ final CharSequence defaultSummary = mContext.getText(R.string.media_output_default_summary);
+ final ListPreference listPreference = (ListPreference) preference;
+ if (TextUtils.equals(address, defaultSummary)) {
+ // Switch to default device which address is device name
+ mSelectedIndex = getDefaultDeviceIndex();
+ setActiveBluetoothDevice(null);
+ listPreference.setSummary(defaultSummary);
+ } else {
+ // Switch to BT device which address is hardware address
+ final int connectedDeviceIndex = getConnectedDeviceIndex(address);
+ if (connectedDeviceIndex == INVALID_INDEX) {
+ return false;
+ }
+ final BluetoothDevice btDevice = mConnectedDevices.get(connectedDeviceIndex);
+ mSelectedIndex = connectedDeviceIndex;
+ setActiveBluetoothDevice(btDevice);
+ listPreference.setSummary(btDevice.getAliasName());
+ }
+ return true;
+ }
+
+ private int getConnectedDeviceIndex(String hardwareAddress) {
+ if (mConnectedDevices != null) {
+ for (int i = 0, size = mConnectedDevices.size(); i < size; i++) {
+ final BluetoothDevice btDevice = mConnectedDevices.get(i);
+ if (TextUtils.equals(btDevice.getAddress(), hardwareAddress)) {
+ return i;
+ }
+ }
+ }
+ return INVALID_INDEX;
+ }
+
+ @Override
public void updateState(Preference preference) {
if (preference == null) {
// In case UI is not ready.
@@ -83,7 +127,41 @@
setPreference(mediaOutputs, mediaValues, preference);
}
- @Override
+ int getDefaultDeviceIndex() {
+ // Default device is after all connected devices.
+ return mConnectedDevices.size();
+ }
+
+ void setupPreferenceEntries(CharSequence[] mediaOutputs, CharSequence[] mediaValues,
+ BluetoothDevice activeDevice) {
+ // default to current device
+ mSelectedIndex = getDefaultDeviceIndex();
+ // default device is after all connected devices.
+ final CharSequence defaultSummary = mContext.getText(R.string.media_output_default_summary);
+ mediaOutputs[mSelectedIndex] = defaultSummary;
+ // use default device name as address
+ mediaValues[mSelectedIndex] = defaultSummary;
+ for (int i = 0, size = mConnectedDevices.size(); i < size; i++) {
+ final BluetoothDevice btDevice = mConnectedDevices.get(i);
+ mediaOutputs[i] = btDevice.getAliasName();
+ mediaValues[i] = btDevice.getAddress();
+ if (btDevice.equals(activeDevice)) {
+ // select the active connected device.
+ mSelectedIndex = i;
+ }
+ }
+ }
+
+ void setPreference(CharSequence[] mediaOutputs, CharSequence[] mediaValues,
+ Preference preference) {
+ final ListPreference listPreference = (ListPreference) preference;
+ listPreference.setEntries(mediaOutputs);
+ listPreference.setEntryValues(mediaValues);
+ listPreference.setValueIndex(mSelectedIndex);
+ listPreference.setSummary(mediaOutputs[mSelectedIndex]);
+ mAudioSwitchPreferenceCallback.onPreferenceDataChanged(listPreference);
+ }
+
public void setActiveBluetoothDevice(BluetoothDevice device) {
if (!Utils.isAudioModeOngoingCall(mContext)) {
return;
diff --git a/src/com/android/settings/sound/MediaOutputPreferenceController.java b/src/com/android/settings/sound/MediaOutputPreferenceController.java
index ce476ad..47810f7 100644
--- a/src/com/android/settings/sound/MediaOutputPreferenceController.java
+++ b/src/com/android/settings/sound/MediaOutputPreferenceController.java
@@ -16,13 +16,14 @@
package com.android.settings.sound;
-import static android.bluetooth.IBluetoothHearingAid.HI_SYNC_ID_INVALID;
import static android.media.AudioManager.STREAM_MUSIC;
import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.content.Intent;
import android.media.AudioManager;
+import android.text.TextUtils;
import androidx.preference.Preference;
@@ -30,12 +31,16 @@
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.HearingAidProfile;
+import com.android.settingslib.media.MediaOutputSliceConstants;
+
+import java.util.List;
/**
- * This class which allows switching between A2dp-connected & HAP-connected BT devices.
- * A few conditions will disable this switcher:
- * - No available BT device(s)
- * - Media stream captured by cast device
+ * This class allows launching MediaOutputSlice to switch output device.
+ * Preference would hide only when
+ * - Bluetooth = OFF
+ * - Bluetooth = ON and Connected Devices = 0 and Previously Connected = 0
+ * - Media stream captured by remote device
* - During a call.
*/
public class MediaOutputPreferenceController extends AudioSwitchPreferenceController {
@@ -66,40 +71,22 @@
return;
}
- mConnectedDevices.clear();
- // Otherwise, list all of the A2DP connected device and display the active device.
- if (mAudioManager.getMode() == AudioManager.MODE_NORMAL) {
- mConnectedDevices.addAll(getConnectedA2dpDevices());
- mConnectedDevices.addAll(getConnectedHearingAidDevices());
+ boolean deviceConnectable = false;
+ BluetoothDevice activeDevice = null;
+ // Show preference if there is connected or previously connected device
+ // Find active device and set its name as the preference's summary
+ List<BluetoothDevice> connectableA2dpDevices = getConnectableA2dpDevices();
+ List<BluetoothDevice> connectableHADevices = getConnectableHearingAidDevices();
+ if (mAudioManager.getMode() == AudioManager.MODE_NORMAL
+ && ((connectableA2dpDevices != null && !connectableA2dpDevices.isEmpty())
+ || (connectableHADevices != null && !connectableHADevices.isEmpty()))) {
+ deviceConnectable = true;
+ activeDevice = findActiveDevice();
}
-
- final int numDevices = mConnectedDevices.size();
- mPreference.setVisible((numDevices == 0) ? false : true);
- CharSequence[] mediaOutputs = new CharSequence[numDevices + 1];
- CharSequence[] mediaValues = new CharSequence[numDevices + 1];
-
- // Setup devices entries, select active connected device
- setupPreferenceEntries(mediaOutputs, mediaValues, findActiveDevice());
-
- // Display connected devices, default device and show the active device
- setPreference(mediaOutputs, mediaValues, preference);
- }
-
- @Override
- public void setActiveBluetoothDevice(BluetoothDevice device) {
- if (mAudioManager.getMode() != AudioManager.MODE_NORMAL) {
- return;
- }
- final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
- final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
- if (hapProfile != null && a2dpProfile != null && device == null) {
- hapProfile.setActiveDevice(null);
- a2dpProfile.setActiveDevice(null);
- } else if (hapProfile != null && hapProfile.getHiSyncId(device) != HI_SYNC_ID_INVALID) {
- hapProfile.setActiveDevice(device);
- } else if (a2dpProfile != null) {
- a2dpProfile.setActiveDevice(device);
- }
+ mPreference.setVisible(deviceConnectable);
+ mPreference.setSummary((activeDevice == null) ?
+ mContext.getText(R.string.media_output_default_summary) :
+ activeDevice.getAliasName());
}
@Override
@@ -112,4 +99,34 @@
}
return activeDevice;
}
+
+ /**
+ * Find active hearing aid device
+ */
+ @Override
+ protected BluetoothDevice findActiveHearingAidDevice() {
+ final HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+
+ if (hearingAidProfile != null) {
+ List<BluetoothDevice> activeDevices = hearingAidProfile.getActiveDevices();
+ for (BluetoothDevice btDevice : activeDevices) {
+ if (btDevice != null) {
+ return btDevice;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+ final Intent intent = new Intent()
+ .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/com/android/settings/wifi/ButtonPreference.java b/src/com/android/settings/wifi/ButtonPreference.java
index 9a0abf6..5169d7a 100644
--- a/src/com/android/settings/wifi/ButtonPreference.java
+++ b/src/com/android/settings/wifi/ButtonPreference.java
@@ -49,6 +49,7 @@
private ImageButton mImageButton;
private Drawable mButtonIcon;
private View.OnClickListener mClickListener;
+ private String mContentDescription;
// Used for dummy pref.
public ButtonPreference(Context context, AttributeSet attrs) {
@@ -57,6 +58,7 @@
mImageButton = null;
mButtonIcon = null;
mClickListener = null;
+ mContentDescription = null;
}
public ButtonPreference(Context context) {
@@ -83,6 +85,7 @@
if (mImageButton != null) {
mImageButton.setImageDrawable(mButtonIcon);
mImageButton.setOnClickListener(mClickListener);
+ mImageButton.setContentDescription(mContentDescription);
}
setButtonVisibility();
}
@@ -96,9 +99,9 @@
/**
* Sets the drawable to be displayed in button.
*/
- public ButtonPreference setButtonIcon(@DrawableRes int iconResId) {
+ public void setButtonIcon(@DrawableRes int iconResId) {
if (iconResId == 0) {
- return this;
+ return;
}
try {
@@ -107,17 +110,26 @@
} catch (Resources.NotFoundException exception) {
Log.e(TAG, "Resource does not exist: " + iconResId);
}
- return this;
}
/**
* Register a callback to be invoked when button is clicked.
*/
- public ButtonPreference setButtonOnClickListener(View.OnClickListener listener) {
+ public void setButtonOnClickListener(View.OnClickListener listener) {
if (listener != mClickListener) {
mClickListener = listener;
notifyChanged();
}
- return this;
+ }
+
+ /**
+ * A content description briefly describes the button and is primarily used for accessibility
+ * support to determine how a button should be presented to the user.
+ */
+ public void setButtonContentDescription(String contentDescription) {
+ if (contentDescription != mContentDescription) {
+ mContentDescription = contentDescription;
+ notifyChanged();
+ }
}
}
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 916d330..665e253 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -265,11 +265,7 @@
configureSecuritySpinner();
mConfigUi.setSubmitButton(res.getString(R.string.wifi_save));
} else {
- if (!mAccessPoint.isPasspointConfig()) {
- mConfigUi.setTitle(mAccessPoint.getSsid());
- } else {
- mConfigUi.setTitle(mAccessPoint.getConfigName());
- }
+ mConfigUi.setTitle(mAccessPoint.getTitle());
ViewGroup group = (ViewGroup) mView.findViewById(R.id.info);
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index c00fe24..9e5bfc8 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -247,6 +247,7 @@
getContext().startActivity(
WifiDppUtils.getEnrolleeQrCodeScannerIntent(/* ssid */ null));
});
+ mAddPreference.setButtonContentDescription(getString(R.string.wifi_dpp_scan_qr_code));
mStatusMessagePreference = (LinkablePreference) findPreference(PREF_KEY_STATUS_MESSAGE);
mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager());
@@ -483,7 +484,7 @@
if (preference instanceof LongPressAccessPointPreference) {
mSelectedAccessPoint =
((LongPressAccessPointPreference) preference).getAccessPoint();
- menu.setHeaderTitle(mSelectedAccessPoint.getSsid());
+ menu.setHeaderTitle(mSelectedAccessPoint.getTitle());
if (mSelectedAccessPoint.isConnectable()) {
menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
}
diff --git a/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreference.java b/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreference.java
new file mode 100644
index 0000000..a44fcbe
--- /dev/null
+++ b/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreference.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2018 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.calling;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.RadioButton;
+import android.widget.TextView;
+import androidx.appcompat.app.AlertDialog.Builder;
+import com.android.settings.CustomListPreference;
+import com.android.settings.R;
+
+/**
+ * ListPreference contain the entry summary.
+ */
+public class ListWithEntrySummaryPreference extends CustomListPreference {
+ private static final String LOG_TAG = "ListWithEntrySummaryPreference";
+ private final Context mContext;
+ private CharSequence[] mSummaries;
+
+ /**
+ * ListWithEntrySummaryPreference constructor.
+ *
+ * @param context The context of view.
+ * @param attrs The attributes of the XML tag that is inflating the linear layout.
+ */
+ public ListWithEntrySummaryPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+
+ TypedArray array = context.obtainStyledAttributes(attrs,
+ R.styleable.ListWithEntrySummaryPreference, 0, 0);
+ mSummaries = array.getTextArray(R.styleable.ListWithEntrySummaryPreference_entrySummaries);
+ array.recycle();
+ }
+
+ /**
+ * Sets the summaries of mode items to be shown in the mode select dialog.
+ *
+ * @param summariesResId The summaries of mode items.
+ */
+ public void setEntrySummaries(int summariesResId) {
+ mSummaries = getContext().getResources().getTextArray(summariesResId);
+ }
+
+ /**
+ * Sets the summaries of mode items to be shown in the mode select dialog.
+ *
+ * @param summaries The summaries of mode items.
+ */
+ public void setEntrySummaries(CharSequence[] summaries) {
+ mSummaries = summaries;
+ }
+
+ private CharSequence getEntrySummary(int index) {
+ if (mSummaries == null) {
+ Log.w(LOG_TAG, "getEntrySummary : mSummaries is null");
+ return "";
+ }
+ return mSummaries[index];
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(Builder builder,
+ DialogInterface.OnClickListener listener) {
+ ListAdapter la = (ListAdapter) new SelectorAdapter(mContext,
+ R.xml.single_choice_list_item_2, this);
+ builder.setSingleChoiceItems(la, findIndexOfValue(getValue()), listener);
+ super.onPrepareDialogBuilder(builder, listener);
+ }
+
+ private static class SelectorAdapter extends ArrayAdapter<CharSequence> {
+ private final Context mContext;
+ private ListWithEntrySummaryPreference mSelector;
+
+ /**
+ * SelectorAdapter constructor.
+ *
+ * @param context The current context.
+ * @param rowResourceId The resource id of the XML tag that is inflating the linear layout.
+ * @param listPreference The instance of ListWithEntrySummaryPreference.
+ */
+ public SelectorAdapter(Context context, int rowResourceId,
+ ListWithEntrySummaryPreference listPreference) {
+ super(context, rowResourceId, listPreference.getEntryValues());
+ mContext = context;
+ mSelector = listPreference;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View row = inflater.inflate(R.xml.single_choice_list_item_2, parent, false);
+
+ TextView title = (TextView) row.findViewById(R.id.title);
+ title.setText(mSelector.getEntries()[position]);
+
+ TextView summary = (TextView) row.findViewById(R.id.summary);
+ summary.setText(mSelector.getEntrySummary(position));
+
+ RadioButton rb = (RadioButton) row.findViewById(R.id.radio);
+ if (position == mSelector.findIndexOfValue(mSelector.getValue())) {
+ rb.setChecked(true);
+ }
+
+ return row;
+ }
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ final Parcelable superState = super.onSaveInstanceState();
+
+ final SavedState myState = new SavedState(superState);
+ myState.mEntries = getEntries();
+ myState.mEntryValues = getEntryValues();
+ myState.mSummaries = mSummaries;
+ return myState;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (state == null || !state.getClass().equals(SavedState.class)) {
+ // Didn't save state for us in onSaveInstanceState
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ SavedState myState = (SavedState) state;
+ super.onRestoreInstanceState(myState.getSuperState());
+ setEntries(myState.mEntries);
+ setEntryValues(myState.mEntryValues);
+ mSummaries = myState.mSummaries;
+ }
+
+ /**
+ * We save entries, entryValues and summaries into bundle.
+ * At onCreate of fragment, dialog will be restored if it was open. In this case,
+ * we need to restore entries, entryValues and summaries. Without those information,
+ * crash when entering multi window during wfc modes dialog shown.
+ */
+ private static class SavedState extends BaseSavedState {
+ private CharSequence[] mEntries;
+ private CharSequence[] mEntryValues;
+ private CharSequence[] mSummaries;
+
+ public SavedState(Parcel source) {
+ super(source);
+ mEntries = source.readCharSequenceArray();
+ mEntryValues = source.readCharSequenceArray();
+ mSummaries = source.readCharSequenceArray();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeCharSequenceArray(mEntries);
+ dest.writeCharSequenceArray(mEntryValues);
+ dest.writeCharSequenceArray(mSummaries);
+ }
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
index 6fe1795..4255f3c 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
@@ -82,8 +82,8 @@
//UI objects
private SwitchBar mSwitchBar;
private Switch mSwitch;
- private ListPreference mButtonWfcMode;
- private ListPreference mButtonWfcRoamingMode;
+ private ListWithEntrySummaryPreference mButtonWfcMode;
+ private ListWithEntrySummaryPreference mButtonWfcRoamingMode;
private Preference mUpdateAddress;
private TextView mEmptyView;
@@ -265,10 +265,11 @@
mTelephonyManager = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE))
.createForSubscriptionId(mSubId);
- mButtonWfcMode = (ListPreference) findPreference(BUTTON_WFC_MODE);
+ mButtonWfcMode = (ListWithEntrySummaryPreference) findPreference(BUTTON_WFC_MODE);
mButtonWfcMode.setOnPreferenceChangeListener(this);
- mButtonWfcRoamingMode = (ListPreference) findPreference(BUTTON_WFC_ROAMING_MODE);
+ mButtonWfcRoamingMode = (ListWithEntrySummaryPreference) findPreference(
+ BUTTON_WFC_ROAMING_MODE);
mButtonWfcRoamingMode.setOnPreferenceChangeListener(this);
mUpdateAddress = (Preference) findPreference(PREFERENCE_EMERGENCY_ADDRESS);
@@ -329,10 +330,14 @@
if (!isWifiOnlySupported) {
mButtonWfcMode.setEntries(R.array.wifi_calling_mode_choices_without_wifi_only);
mButtonWfcMode.setEntryValues(R.array.wifi_calling_mode_values_without_wifi_only);
+ mButtonWfcMode.setEntrySummaries(R.array.wifi_calling_mode_summaries_without_wifi_only);
+
mButtonWfcRoamingMode.setEntries(
R.array.wifi_calling_mode_choices_v2_without_wifi_only);
mButtonWfcRoamingMode.setEntryValues(
R.array.wifi_calling_mode_values_without_wifi_only);
+ mButtonWfcRoamingMode.setEntrySummaries(
+ R.array.wifi_calling_mode_summaries_without_wifi_only);
}
diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
index 01673e3..c2daa94 100644
--- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
@@ -21,16 +21,12 @@
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.KeyguardManager;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricPrompt;
-import android.os.CancellationSignal;
-import android.os.Looper;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkAddress;
@@ -327,7 +323,7 @@
mContext.getDrawable(R.drawable.ic_settings_widget_background));
iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- mEntityHeaderController.setLabel(mAccessPoint.getSsidStr());
+ mEntityHeaderController.setLabel(mAccessPoint.getTitle());
}
@Override
@@ -619,48 +615,16 @@
* Share the wifi network with QR code.
*/
private void shareNetwork() {
- final KeyguardManager keyguardManager = (KeyguardManager) mContext.getSystemService(
- Context.KEYGUARD_SERVICE);
- if (keyguardManager.isKeyguardSecure()) {
- // Show authentication screen to confirm credentials (pin, pattern or password) for
- // the current user of the device.
- final String title = mContext.getString(
- R.string.lockpassword_confirm_your_pattern_header);
- final String description = String.format(
- mContext.getString(R.string.wifi_sharing_message),
- mAccessPoint.getSsidStr());
+ final String title = mContext.getString(
+ R.string.lockpassword_confirm_your_pattern_header);
+ final String description = String.format(
+ mContext.getString(R.string.wifi_sharing_message),
+ mAccessPoint.getSsidStr());
- final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(mContext)
- .setTitle(title)
- .setDescription(description);
-
- if (keyguardManager.isDeviceSecure()) {
- builder.setDeviceCredentialAllowed(true);
- }
-
- final BiometricPrompt bp = builder.build();
- final Handler handler = new Handler(Looper.getMainLooper());
- bp.authenticate(new CancellationSignal(),
- runnable -> handler.post(runnable),
- mAuthenticationCallback);
- } else {
- launchWifiDppConfiguratorActivity();
- }
+ WifiDppUtils.showLockScreen(mContext, title, description,
+ () -> launchWifiDppConfiguratorActivity());
}
- private BiometricPrompt.AuthenticationCallback mAuthenticationCallback =
- new BiometricPrompt.AuthenticationCallback() {
- @Override
- public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
- launchWifiDppConfiguratorActivity();
- }
-
- @Override
- public void onAuthenticationError(int errorCode, CharSequence errString) {
- //Do nothing
- }
- };
-
/**
* Sign in to the captive portal found on this wifi network associated with this preference.
*/
diff --git a/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java b/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
index 9e731ba..3a9308e 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppAddDeviceFragment.java
@@ -29,6 +29,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
@@ -99,6 +100,7 @@
if (!isConfigurationChange) {
mLatestStatusCode = WifiDppUtils.EASY_CONNECT_EVENT_SUCCESS;
+ changeFocusAndAnnounceChange(mButtonRight, mTitle);
}
}
@@ -168,15 +170,17 @@
mButtonLeft.setVisibility(View.INVISIBLE);
}
- if (!isConfigurationChange) {
- mLatestStatusCode = code;
- }
-
if (isGoingInitiator()) {
mSummary.setText(R.string.wifi_dpp_sharing_wifi_with_this_device);
}
+
mProgressBar.setVisibility(isGoingInitiator() ? View.VISIBLE : View.INVISIBLE);
mButtonRight.setVisibility(isGoingInitiator() ? View.INVISIBLE : View.VISIBLE);
+
+ if (!isConfigurationChange) {
+ mLatestStatusCode = code;
+ changeFocusAndAnnounceChange(mButtonRight, mSummary);
+ }
}
private boolean hasRetryButton(int code) {
@@ -277,6 +281,7 @@
mButtonRight.setVisibility(View.INVISIBLE);
startWifiDppConfiguratorInitiator();
updateSummary();
+ mTitleSummaryContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
});
if (savedInstanceState != null) {
@@ -288,6 +293,8 @@
} else {
showErrorUi(mLatestStatusCode, /* isConfigurationChange */ true);
}
+ } else {
+ changeFocusAndAnnounceChange(mButtonRight, mTitleSummaryContainer);
}
}
@@ -354,4 +361,17 @@
mSummary.setText(getString(R.string.wifi_dpp_add_device_to_wifi, getSsid()));
}
}
+
+ /**
+ * This fragment will change UI display and text messages for events. To improve Talkback user
+ * experienience, using this method to focus on a right component and announce a changed text
+ * after an UI changing event.
+ *
+ * @param focusView The UI component which will be focused
+ * @param announceView The UI component's text will be talked
+ */
+ private void changeFocusAndAnnounceChange(View focusView, View announceView) {
+ focusView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ announceView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java b/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
index 72e845f..ddba933 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppChooseSavedWifiNetworkFragment.java
@@ -23,6 +23,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.Button;
import android.widget.ListView;
@@ -105,5 +106,11 @@
mButtonRight = view.findViewById(R.id.button_right);
mButtonRight.setVisibility(View.GONE);
+
+ if (savedInstanceState == null) {
+ // For Talkback to describe this fragment
+ mTitleSummaryContainer.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
}
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
index 0a2c09b..7308741 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
@@ -75,6 +75,7 @@
private static final String KEY_WIFI_PRESHARED_KEY = "key_wifi_preshared_key";
private static final String KEY_WIFI_HIDDEN_SSID = "key_wifi_hidden_ssid";
private static final String KEY_WIFI_NETWORK_ID = "key_wifi_network_id";
+ private static final String KEY_IS_HOTSPOT = "key_is_hotspot";
private FragmentManager mFragmentManager;
@@ -104,14 +105,15 @@
mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(qrCode);
- String security = savedInstanceState.getString(KEY_WIFI_SECURITY);
- String ssid = savedInstanceState.getString(KEY_WIFI_SSID);
- String preSharedKey = savedInstanceState.getString(KEY_WIFI_PRESHARED_KEY);
- boolean hiddenSsid = savedInstanceState.getBoolean(KEY_WIFI_HIDDEN_SSID);
- int networkId = savedInstanceState.getInt(KEY_WIFI_NETWORK_ID);
+ final String security = savedInstanceState.getString(KEY_WIFI_SECURITY);
+ final String ssid = savedInstanceState.getString(KEY_WIFI_SSID);
+ final String preSharedKey = savedInstanceState.getString(KEY_WIFI_PRESHARED_KEY);
+ final boolean hiddenSsid = savedInstanceState.getBoolean(KEY_WIFI_HIDDEN_SSID);
+ final int networkId = savedInstanceState.getInt(KEY_WIFI_NETWORK_ID);
+ final boolean isHotspot = savedInstanceState.getBoolean(KEY_IS_HOTSPOT);
mWifiNetworkConfig = WifiNetworkConfig.getValidConfigOrNull(security, ssid,
- preSharedKey, hiddenSsid, networkId);
+ preSharedKey, hiddenSsid, networkId, isHotspot);
} else {
handleIntent(getIntent());
}
@@ -361,6 +363,7 @@
outState.putString(KEY_WIFI_PRESHARED_KEY, mWifiNetworkConfig.getPreSharedKey());
outState.putBoolean(KEY_WIFI_HIDDEN_SSID, mWifiNetworkConfig.getHiddenSsid());
outState.putInt(KEY_WIFI_NETWORK_ID, mWifiNetworkConfig.getNetworkId());
+ outState.putBoolean(KEY_IS_HOTSPOT, mWifiNetworkConfig.isHotspot());
}
super.onSaveInstanceState(outState);
@@ -393,7 +396,8 @@
wifiConfiguration.getPrintableSsid(),
wifiConfiguration.preSharedKey,
/* hiddenSsid */ false,
- wifiConfiguration.networkId);
+ wifiConfiguration.networkId,
+ /* isHotspot */ false);
}
}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
index fab495d..eafbe68 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeBaseFragment.java
@@ -38,6 +38,7 @@
private ImageView mDevicesCheckCircleGreenHeaderIcon;
protected TextView mTitle;
protected TextView mSummary;
+ protected View mTitleSummaryContainer;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -48,6 +49,10 @@
view.findViewById(R.id.devices_check_circle_green_icon);
mTitle = view.findViewById(android.R.id.title);
mSummary = view.findViewById(android.R.id.summary);
+
+ // This is the LinearLayout which groups mTitle and mSummary for Talkback to announce the
+ // content in a way that reflects its natural groupings.
+ mTitleSummaryContainer = view.findViewById(R.id.title_summary_container);
}
protected void setHeaderIconImageResource(int resId) {
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
index daa41d9f..2cd5e23 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeGeneratorFragment.java
@@ -60,6 +60,14 @@
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ // setTitle for Talkback
+ final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
+ if (wifiNetworkConfig.isHotspot()) {
+ getActivity().setTitle(R.string.wifi_dpp_share_hotspot);
+ } else {
+ getActivity().setTitle(R.string.wifi_dpp_share_wifi);
+ }
+
setHasOptionsMenu(true);
final ActionBar actionBar = getActivity().getActionBar();
if (actionBar != null) {
@@ -86,7 +94,8 @@
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
MenuItem menuItem;
- if (wifiNetworkConfig.isSupportWifiDpp(getActivity())) {
+ if (!wifiNetworkConfig.isHotspot() &&
+ wifiNetworkConfig.isSupportWifiDpp(getActivity())) {
menuItem = menu.add(0, Menu.FIRST, 0, R.string.next_label);
menuItem.setIcon(R.drawable.ic_scan_24dp);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
@@ -127,9 +136,15 @@
setHeaderIconImageResource(R.drawable.ic_qrcode_24dp);
final WifiNetworkConfig wifiNetworkConfig = getWifiNetworkConfigFromHostActivity();
- mTitle.setText(R.string.wifi_dpp_share_wifi);
- mSummary.setText(getString(R.string.wifi_dpp_scan_qr_code_with_another_device,
- wifiNetworkConfig.getSsid()));
+ if (wifiNetworkConfig.isHotspot()) {
+ mTitle.setText(R.string.wifi_dpp_share_hotspot);
+ mSummary.setText(getString(R.string.wifi_dpp_scan_qr_code_to_share_hotspot,
+ wifiNetworkConfig.getSsid()));
+ } else {
+ mTitle.setText(R.string.wifi_dpp_share_wifi);
+ mSummary.setText(getString(R.string.wifi_dpp_scan_qr_code_with_another_device,
+ wifiNetworkConfig.getSsid()));
+ }
mQrCode = wifiNetworkConfig.getQrCode();
setQrCode();
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
index a19069b..4535599 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
@@ -41,6 +41,7 @@
import android.view.TextureView.SurfaceTextureListener;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -174,6 +175,13 @@
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+ // setTitle for Talkback
+ if (mIsConfiguratorMode) {
+ getActivity().setTitle(R.string.wifi_dpp_add_device_to_network);
+ } else {
+ getActivity().setTitle(R.string.wifi_dpp_scan_qr_code);
+ }
+
final ActionBar actionBar = getActivity().getActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
@@ -380,6 +388,7 @@
public void showErrorMessage(String message) {
mErrorMessage.setVisibility(View.VISIBLE);
mErrorMessage.setText(message);
+ mErrorMessage.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
mHandler.removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
mHandler.sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,
@@ -410,6 +419,8 @@
mProgressBar.setVisibility(View.VISIBLE);
startWifiDppEnrolleeInitiator((WifiQrCode)msg.obj);
updateEnrolleeSummary();
+ mSummary.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
break;
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index 4d8cca5..fe7af27 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -16,11 +16,16 @@
package com.android.settings.wifi.dpp;
+import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
+import android.hardware.biometrics.BiometricPrompt;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
@@ -70,6 +75,9 @@
/** The data corresponding to {@code WifiConfiguration} networkId */
public static final String EXTRA_WIFI_NETWORK_ID = "networkId";
+ /** The data to recognize if it's a Wi-Fi hotspot for configuration */
+ public static final String EXTRA_IS_HOTSPOT = "isHotspot";
+
/** Used by {@link android.provider.Settings#ACTION_PROCESS_WIFI_EASY_CONNECT_URI} to
* indicate test mode UI should be shown. Test UI does not make API calls. Value is a boolean.*/
public static final String EXTRA_TEST = "test";
@@ -142,24 +150,12 @@
return str.substring(begin, end+1);
}
- private static String getSecurityString(AccessPoint accessPoint) {
- switch(accessPoint.getSecurity()) {
- case AccessPoint.SECURITY_WEP:
- return WifiQrCode.SECURITY_WEP;
- case AccessPoint.SECURITY_PSK:
- return WifiQrCode.SECURITY_WPA_PSK;
- case AccessPoint.SECURITY_SAE:
- return WifiQrCode.SECURITY_SAE;
- default:
- return WifiQrCode.SECURITY_NO_PASSWORD;
- }
- }
-
static String getSecurityString(WifiConfiguration config) {
if (config.allowedKeyManagement.get(KeyMgmt.SAE)) {
return WifiQrCode.SECURITY_SAE;
}
- if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+ if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ||
+ config.allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
return WifiQrCode.SECURITY_WPA_PSK;
}
return (config.wepKeys[0] == null) ?
@@ -171,6 +167,9 @@
* security. It may return null if the security is not supported by QR code generator nor
* scanner.
*
+ * Do not use this method for Wi-Fi hotspot network, use
+ * {@code getHotspotConfiguratorIntentOrNull} instead.
+ *
* @param context The context to use for the content resolver
* @param wifiManager An instance of {@link WifiManager}
* @param accessPoint An instance of {@link AccessPoint}
@@ -187,15 +186,63 @@
return null;
}
- final WifiConfiguration wifiConfig = accessPoint.getConfig();
- final String ssid = removeFirstAndLastDoubleQuotes(wifiConfig.SSID);
- final String security = getSecurityString(accessPoint);
- String preSharedKey = wifiConfig.preSharedKey;
+ final WifiConfiguration wifiConfiguration = accessPoint.getConfig();
+ setConfiguratorIntentExtra(intent, wifiManager, wifiConfiguration);
+
+ if (wifiConfiguration.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
+ throw new IllegalArgumentException("Invalid network ID");
+ } else {
+ intent.putExtra(EXTRA_WIFI_NETWORK_ID, wifiConfiguration.networkId);
+ }
+
+ return intent;
+ }
+
+ /**
+ * Returns an intent to launch QR code generator for the Wi-Fi hotspot. It may return null if
+ * the security is not supported by QR code generator.
+ *
+ * @param context The context to use for the content resolver
+ * @param wifiManager An instance of {@link WifiManager}
+ * @param wifiConfiguration {@link WifiConfiguration} of the Wi-Fi hotspot
+ * @return Intent for launching QR code generator
+ */
+ public static Intent getHotspotConfiguratorIntentOrNull(Context context,
+ WifiManager wifiManager, WifiConfiguration wifiConfiguration) {
+ final Intent intent = new Intent(context, WifiDppConfiguratorActivity.class);
+ if (isSupportHotspotConfiguratorQrCodeGenerator(wifiConfiguration)) {
+ intent.setAction(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR);
+ } else {
+ return null;
+ }
+
+ setConfiguratorIntentExtra(intent, wifiManager, wifiConfiguration);
+
+ intent.putExtra(EXTRA_WIFI_NETWORK_ID, WifiConfiguration.INVALID_NETWORK_ID);
+ intent.putExtra(EXTRA_IS_HOTSPOT, true);
+
+ return intent;
+ }
+
+ /**
+ * Set all extra except {@code EXTRA_WIFI_NETWORK_ID} for the intent to
+ * launch configurator activity later.
+ *
+ * @param intent the target to set extra
+ * @param wifiManager an instance of {@code WifiManager}
+ * @param wifiConfiguration the Wi-Fi network for launching configurator activity
+ */
+ private static void setConfiguratorIntentExtra(Intent intent, WifiManager wifiManager,
+ WifiConfiguration wifiConfiguration) {
+ final String ssid = removeFirstAndLastDoubleQuotes(wifiConfiguration.SSID);
+ final String security = getSecurityString(wifiConfiguration);
+ String preSharedKey = wifiConfiguration.preSharedKey;
if (preSharedKey != null) {
// When the value of this key is read, the actual key is not returned, just a "*".
// Call privileged system API to obtain actual key.
- preSharedKey = removeFirstAndLastDoubleQuotes(getPresharedKey(wifiManager, wifiConfig));
+ preSharedKey = removeFirstAndLastDoubleQuotes(getPresharedKey(wifiManager,
+ wifiConfiguration));
}
if (!TextUtils.isEmpty(ssid)) {
@@ -207,13 +254,6 @@
if (!TextUtils.isEmpty(preSharedKey)) {
intent.putExtra(EXTRA_WIFI_PRE_SHARED_KEY, preSharedKey);
}
- if (wifiConfig.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
- throw new IllegalArgumentException("Invalid network ID");
- } else {
- intent.putExtra(EXTRA_WIFI_NETWORK_ID, wifiConfig.networkId);
- }
-
- return intent;
}
/**
@@ -228,6 +268,54 @@
isSupportConfiguratorQrCodeGenerator(accessPoint);
}
+ /**
+ * Shows authentication screen to confirm credentials (pin, pattern or password) for the current
+ * user of the device.
+ *
+ * @param context The {@code Context} used to get {@code KeyguardManager} service
+ * @param title The title on lock screen
+ * @param description The description on lock screen
+ * @param successRunnable The {@code Runnable} which will be executed if the user does not setup
+ * device security or if lock screen is unlocked
+ */
+ public static void showLockScreen(Context context, String title, String description,
+ Runnable successRunnable) {
+ final KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(
+ Context.KEYGUARD_SERVICE);
+
+ if (keyguardManager.isKeyguardSecure()) {
+ final BiometricPrompt.AuthenticationCallback authenticationCallback =
+ new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationSucceeded(
+ BiometricPrompt.AuthenticationResult result) {
+ successRunnable.run();
+ }
+
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ //Do nothing
+ }
+ };
+
+ final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context)
+ .setTitle(title)
+ .setDescription(description);
+
+ if (keyguardManager.isDeviceSecure()) {
+ builder.setDeviceCredentialAllowed(true);
+ }
+
+ final BiometricPrompt bp = builder.build();
+ final Handler handler = new Handler(Looper.getMainLooper());
+ bp.authenticate(new CancellationSignal(),
+ runnable -> handler.post(runnable),
+ authenticationCallback);
+ } else {
+ successRunnable.run();
+ }
+ }
+
private static boolean isSupportConfiguratorQrCodeScanner(Context context,
AccessPoint accessPoint) {
if (!isWifiDppEnabled(context)) {
@@ -254,4 +342,13 @@
return false;
}
+
+ private static boolean isSupportHotspotConfiguratorQrCodeGenerator(
+ WifiConfiguration wifiConfiguration) {
+ // QR code generator produces QR code with ZXing's Wi-Fi network config format,
+ // it supports PSK and WEP and non security
+ // KeyMgmt.NONE is for WEP or non security
+ return wifiConfiguration.allowedKeyManagement.get(KeyMgmt.WPA2_PSK) ||
+ wifiConfiguration.allowedKeyManagement.get(KeyMgmt.NONE);
+ }
}
diff --git a/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java b/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java
index a483073..fdc74d8 100644
--- a/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java
+++ b/src/com/android/settings/wifi/dpp/WifiNetworkConfig.java
@@ -30,7 +30,6 @@
import android.text.TextUtils;
import android.util.Log;
-import androidx.annotation.Keep;
import androidx.annotation.VisibleForTesting;
/**
@@ -52,15 +51,17 @@
private String mPreSharedKey;
private boolean mHiddenSsid;
private int mNetworkId;
+ private boolean mIsHotspot;
@VisibleForTesting
WifiNetworkConfig(String security, String ssid, String preSharedKey,
- boolean hiddenSsid, int networkId) {
+ boolean hiddenSsid, int networkId, boolean isHotspot) {
mSecurity = security;
mSsid = ssid;
mPreSharedKey = preSharedKey;
mHiddenSsid = hiddenSsid;
mNetworkId = networkId;
+ mIsHotspot = isHotspot;
}
public WifiNetworkConfig(WifiNetworkConfig config) {
@@ -69,6 +70,7 @@
mPreSharedKey = config.mPreSharedKey;
mHiddenSsid = config.mHiddenSsid;
mNetworkId = config.mNetworkId;
+ mIsHotspot = config.mIsHotspot;
}
/**
@@ -86,23 +88,26 @@
* android.settings.WIFI_DPP_CONFIGURATOR_QR_CODE_SCANNER
*/
public static WifiNetworkConfig getValidConfigOrNull(Intent intent) {
- String security = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_SECURITY);
- String ssid = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_SSID);
- String preSharedKey = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY);
- boolean hiddenSsid = intent.getBooleanExtra(WifiDppUtils.EXTRA_WIFI_HIDDEN_SSID, false);
- int networkId = intent.getIntExtra(WifiDppUtils.EXTRA_WIFI_NETWORK_ID,
+ final String security = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_SECURITY);
+ final String ssid = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_SSID);
+ final String preSharedKey = intent.getStringExtra(WifiDppUtils.EXTRA_WIFI_PRE_SHARED_KEY);
+ final boolean hiddenSsid = intent.getBooleanExtra(WifiDppUtils.EXTRA_WIFI_HIDDEN_SSID,
+ false);
+ final int networkId = intent.getIntExtra(WifiDppUtils.EXTRA_WIFI_NETWORK_ID,
WifiConfiguration.INVALID_NETWORK_ID);
+ final boolean isHotspot = intent.getBooleanExtra(WifiDppUtils.EXTRA_IS_HOTSPOT, false);
- return getValidConfigOrNull(security, ssid, preSharedKey, hiddenSsid, networkId);
+ return getValidConfigOrNull(security, ssid, preSharedKey, hiddenSsid, networkId, isHotspot);
}
public static WifiNetworkConfig getValidConfigOrNull(String security, String ssid,
- String preSharedKey, boolean hiddenSsid, int networkId) {
+ String preSharedKey, boolean hiddenSsid, int networkId, boolean isHotspot) {
if (!isValidConfig(security, ssid, preSharedKey, hiddenSsid)) {
return null;
}
- return new WifiNetworkConfig(security, ssid, preSharedKey, hiddenSsid, networkId);
+ return new WifiNetworkConfig(security, ssid, preSharedKey, hiddenSsid, networkId,
+ isHotspot);
}
public static boolean isValidConfig(WifiNetworkConfig config) {
@@ -174,31 +179,30 @@
return barcode;
}
- @Keep
public String getSecurity() {
return mSecurity;
}
- @Keep
public String getSsid() {
return mSsid;
}
- @Keep
public String getPreSharedKey() {
return mPreSharedKey;
}
- @Keep
public boolean getHiddenSsid() {
return mHiddenSsid;
}
- @Keep
public int getNetworkId() {
return mNetworkId;
}
+ public boolean isHotspot() {
+ return mIsHotspot;
+ }
+
public void connect(Context context, WifiManager.ActionListener listener) {
WifiConfiguration wifiConfiguration = getWifiConfigurationOrNull();
if (wifiConfiguration == null) {
diff --git a/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java b/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java
index ce92f90..4688d76 100644
--- a/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiNetworkListFragment.java
@@ -218,7 +218,7 @@
final WifiNetworkConfig networkConfig = WifiNetworkConfig.getValidConfigOrNull(
selectedAccessPoint.getSecurityString(/* concise */ true),
wifiConfig.getPrintableSsid(), wifiConfig.preSharedKey, /* hiddenSsid */ false,
- wifiConfig.networkId);
+ wifiConfig.networkId, /* isHotspot */ false);
if (mOnChooseNetworkListener != null) {
mOnChooseNetworkListener.onChooseNetwork(networkConfig);
}
@@ -232,7 +232,8 @@
/* ssid */ WifiNetworkConfig.FAKE_SSID,
/* preSharedKey */ WifiNetworkConfig.FAKE_PASSWORD,
/* hiddenSsid */ true,
- /* networkId */ WifiConfiguration.INVALID_NETWORK_ID));
+ /* networkId */ WifiConfiguration.INVALID_NETWORK_ID,
+ /* isHotspot*/ false));
}
} else {
return super.onPreferenceTreeClick(preference);
diff --git a/src/com/android/settings/wifi/dpp/WifiQrCode.java b/src/com/android/settings/wifi/dpp/WifiQrCode.java
index 10971cf..8296a62 100644
--- a/src/com/android/settings/wifi/dpp/WifiQrCode.java
+++ b/src/com/android/settings/wifi/dpp/WifiQrCode.java
@@ -133,7 +133,7 @@
password = removeBackSlash(password);
mWifiNetworkConfig = WifiNetworkConfig.getValidConfigOrNull(security, ssid, password,
- hiddenSsid, WifiConfiguration.INVALID_NETWORK_ID);
+ hiddenSsid, WifiConfiguration.INVALID_NETWORK_ID, /* isHotspot */ false);
if (mWifiNetworkConfig == null) {
throw new IllegalArgumentException("Invalid format");
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
index eb6a123..10f3e56 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
@@ -17,13 +17,18 @@
package com.android.settings.wifi.tether;
import android.content.Context;
+import android.content.Intent;
import android.net.wifi.WifiConfiguration;
+import android.util.Log;
+import android.view.View;
import androidx.annotation.VisibleForTesting;
import androidx.preference.EditTextPreference;
import androidx.preference.Preference;
+import com.android.settings.R;
import com.android.settings.widget.ValidatedEditTextPreference;
+import com.android.settings.wifi.dpp.WifiDppUtils;
public class WifiTetherSSIDPreferenceController extends WifiTetherBasePreferenceController
implements ValidatedEditTextPreference.Validator {
@@ -56,6 +61,23 @@
mSSID = DEFAULT_SSID;
}
((ValidatedEditTextPreference) mPreference).setValidator(this);
+
+ if (mWifiManager.isWifiApEnabled() && config != null) {
+ final Intent intent = WifiDppUtils.getHotspotConfiguratorIntentOrNull(mContext,
+ mWifiManager, config);
+
+ if (intent == null) {
+ Log.e(TAG, "Invalid security to share hotspot");
+ ((WifiTetherSsidPreference) mPreference).setButtonVisible(false);
+ } else {
+ ((WifiTetherSsidPreference) mPreference).setButtonOnClickListener(
+ view -> shareHotspotNetwork(intent));
+ ((WifiTetherSsidPreference) mPreference).setButtonVisible(true);
+ }
+ } else {
+ ((WifiTetherSsidPreference) mPreference).setButtonVisible(false);
+ }
+
updateSsidDisplay((EditTextPreference) mPreference);
}
@@ -80,4 +102,19 @@
preference.setText(mSSID);
preference.setSummary(mSSID);
}
+
+ private void shareHotspotNetwork(Intent intent) {
+ final String title = mContext.getString(
+ R.string.lockpassword_confirm_your_pattern_header);
+ final String description = String.format(
+ mContext.getString(R.string.wifi_sharing_message), mSSID);
+
+ WifiDppUtils.showLockScreen(mContext, title, description,
+ () -> mContext.startActivity(intent));
+ }
+
+ @VisibleForTesting
+ boolean isQrCodeButtonAvailable() {
+ return ((WifiTetherSsidPreference) mPreference).isQrCodeButtonAvailable();
+ }
}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSsidPreference.java b/src/com/android/settings/wifi/tether/WifiTetherSsidPreference.java
new file mode 100644
index 0000000..64014d9
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherSsidPreference.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.tether;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageButton;
+
+import androidx.annotation.DrawableRes;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.widget.ValidatedEditTextPreference;
+
+/**
+ * Support a QR code share button for {@code EditTextPreference} that supports input validation.
+ */
+public class WifiTetherSsidPreference extends ValidatedEditTextPreference {
+ private static final String TAG = "WifiTetherSsidPreference";
+
+ private ImageButton mImageButton;
+ private Drawable mButtonIcon;
+ private View.OnClickListener mClickListener;
+ private boolean mVisible;
+
+ public WifiTetherSsidPreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ initialize();
+ }
+
+ public WifiTetherSsidPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ initialize();
+ }
+
+ public WifiTetherSsidPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ initialize();
+ }
+
+ public WifiTetherSsidPreference(Context context) {
+ super(context);
+
+ initialize();
+ }
+
+ private void initialize() {
+ setWidgetLayoutResource(R.layout.wifi_button_preference_widget);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ if (mImageButton == null) {
+ mImageButton = (ImageButton) holder.findViewById(R.id.button_icon);
+
+ mImageButton.setContentDescription(
+ getContext().getString(R.string.wifi_dpp_share_hotspot));
+ setButtonIcon(R.drawable.ic_qrcode_24dp);
+ mImageButton.setImageDrawable(mButtonIcon);
+ }
+
+ if (mVisible) {
+ mImageButton.setOnClickListener(mClickListener);
+ mImageButton.setVisibility(View.VISIBLE);
+ } else {
+ mImageButton.setVisibility(View.GONE);
+ }
+ }
+
+ public void setButtonOnClickListener(View.OnClickListener listener) {
+ mClickListener = listener;
+ }
+
+ public void setButtonVisible(boolean visible) {
+ mVisible = visible;
+ }
+
+ private void setButtonIcon(@DrawableRes int iconResId) {
+ try {
+ mButtonIcon = getContext().getDrawable(iconResId);
+ } catch (Resources.NotFoundException exception) {
+ Log.e(TAG, "Resource does not exist: " + iconResId);
+ }
+ }
+
+ @VisibleForTesting
+ boolean isQrCodeButtonAvailable() {
+ return mVisible && mClickListener != null;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultEmergencyPickerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultEmergencyPickerTest.java
index 5d97f52..b7b046c 100644
--- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultEmergencyPickerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultEmergencyPickerTest.java
@@ -19,7 +19,6 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -27,15 +26,10 @@
import android.app.Activity;
import android.app.role.RoleManager;
-import android.app.role.RoleManagerCallback;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.AsyncTask;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
-import android.util.Log;
import org.junit.Before;
import org.junit.Test;
@@ -50,6 +44,7 @@
import java.util.Arrays;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
@RunWith(RobolectricTestRunner.class)
public class DefaultEmergencyPickerTest {
@@ -90,7 +85,7 @@
eq(0),
any(UserHandle.class),
any(Executor.class),
- any(RoleManagerCallback.class));
+ any(Consumer.class));
}
@Test
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
index 8b04ef3..e66774e 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
@@ -22,7 +22,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -87,6 +87,19 @@
}
@Test
+ public void isCardEligibleToDisplay_invalidRankingScore_returnFalse() {
+ final ContextualCard card = new ContextualCard.Builder()
+ .setName("test_card")
+ .setCardType(ContextualCard.CardType.SLICE)
+ .setSliceUri(CustomSliceRegistry.FLASHLIGHT_SLICE_URI)
+ .setRankingScore(-1)
+ .build();
+
+ assertThat(mEligibleCardChecker.isCardEligibleToDisplay(card))
+ .isFalse();
+ }
+
+ @Test
public void isCardEligibleToDisplay_nullSlice_returnFalse() {
doReturn(null).when(mEligibleCardChecker).bindSlice(Uri.parse(TEST_SLICE_URI));
@@ -108,7 +121,7 @@
public void getDisplayableCards_twoEligibleCards_shouldShowAll() {
final List<ContextualCard> cards = getContextualCardList().stream().limit(2)
.collect(Collectors.toList());
- doReturn(cards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
+ doReturn(cards).when(mContextualCardLoader).filterEligibleCards(anyList());
final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(cards);
@@ -118,7 +131,7 @@
@Test
public void getDisplayableCards_fiveEligibleCardsNoLarge_shouldShowDefaultCardCount() {
final List<ContextualCard> fiveCards = getContextualCardListWithNoLargeCard();
- doReturn(fiveCards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
+ doReturn(fiveCards).when(mContextualCardLoader).filterEligibleCards(anyList());
final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(
fiveCards);
@@ -136,7 +149,7 @@
.setSliceUri(Uri.parse(
"content://com.android.settings.test.slices/action/gesture_pick_up"))
.build());
- doReturn(cards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
+ doReturn(cards).when(mContextualCardLoader).filterEligibleCards(anyList());
final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(cards);
@@ -147,7 +160,7 @@
public void getDisplayableCards_threeEligibleCardsTwoLarge_shouldShowTwoCards() {
final List<ContextualCard> threeCards = getContextualCardList().stream().limit(3)
.collect(Collectors.toList());
- doReturn(threeCards).when(mContextualCardLoader).filterEligibleCards(any(List.class));
+ doReturn(threeCards).when(mContextualCardLoader).filterEligibleCards(anyList());
final List<ContextualCard> result = mContextualCardLoader.getDisplayableCards(
threeCards);
@@ -167,25 +180,26 @@
public void getDisplayableCards_refreshCardUri_shouldLogContextualCardDisplay() {
mContextualCardLoader.mNotifyUri = CardContentProvider.REFRESH_CARD_URI;
- mContextualCardLoader.getDisplayableCards(new ArrayList<ContextualCard>());
+ mContextualCardLoader.getDisplayableCards(new ArrayList<>());
verify(mFakeFeatureFactory.mContextualCardFeatureProvider).logContextualCardDisplay(
- any(List.class), any(List.class));
+ anyList(), anyList());
}
@Test
public void getDisplayableCards_deleteCardUri_shouldNotLogContextualCardDisplay() {
mContextualCardLoader.mNotifyUri = CardContentProvider.DELETE_CARD_URI;
- mContextualCardLoader.getDisplayableCards(new ArrayList<ContextualCard>());
+ mContextualCardLoader.getDisplayableCards(new ArrayList<>());
verify(mFakeFeatureFactory.mContextualCardFeatureProvider, never())
- .logContextualCardDisplay(any(List.class), any(List.class));
+ .logContextualCardDisplay(anyList(), anyList());
}
private ContextualCard getContextualCard(String sliceUri) {
return new ContextualCard.Builder()
.setName("test_card")
+ .setRankingScore(0.5)
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(Uri.parse(sliceUri))
.build();
diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
index daaba90..da0d85b 100644
--- a/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
+++ b/tests/robotests/src/com/android/settings/media/MediaOutputSliceTest.java
@@ -21,21 +21,15 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.util.IconDrawableFactory;
import androidx.slice.Slice;
import androidx.slice.SliceMetadata;
@@ -43,6 +37,7 @@
import androidx.slice.core.SliceAction;
import androidx.slice.widget.SliceLiveData;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
@@ -53,70 +48,64 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
public class MediaOutputSliceTest {
private static final String TEST_PACKAGE_NAME = "com.fake.android.music";
- private static final String TEST_LABEL = "Test app";
private static final String TEST_DEVICE_1_ID = "test_device_1_id";
+ private static final String TEST_DEVICE_1_NAME = "test_device_1_name";
+ private static final int TEST_DEVICE_1_ICON =
+ com.android.internal.R.drawable.ic_bt_headphones_a2dp;
@Mock
- private PackageManager mPackageManager;
- @Mock
- private ApplicationInfo mApplicationInfo;
- @Mock
- private ApplicationInfo mApplicationInfo2;
- @Mock
private LocalMediaManager mLocalMediaManager;
- @Mock
- private IconDrawableFactory mIconDrawableFactory;
- @Mock
- private Drawable mTestDrawable;
private final List<MediaDevice> mDevices = new ArrayList<>();
private Context mContext;
private MediaOutputSlice mMediaOutputSlice;
private MediaDeviceUpdateWorker mMediaDeviceUpdateWorker;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
- when(mContext.getPackageManager()).thenReturn(mPackageManager);
- when(mPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt()))
- .thenReturn(mApplicationInfo);
- when(mPackageManager.getApplicationInfoAsUser(eq(TEST_PACKAGE_NAME), anyInt(), anyInt()))
- .thenReturn(mApplicationInfo2);
- when(mApplicationInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL);
- when(mIconDrawableFactory.getBadgedIcon(mApplicationInfo2, UserHandle.myUserId()))
- .thenReturn(mTestDrawable);
- when(mTestDrawable.getIntrinsicWidth()).thenReturn(100);
- when(mTestDrawable.getIntrinsicHeight()).thenReturn(100);
-
// Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+ // Setup BluetoothAdapter
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mShadowBluetoothAdapter.setEnabled(true);
mMediaOutputSlice = new MediaOutputSlice(mContext);
mMediaDeviceUpdateWorker = new MediaDeviceUpdateWorker(mContext, MEDIA_OUTPUT_SLICE_URI);
mMediaDeviceUpdateWorker.setPackageName(TEST_PACKAGE_NAME);
mMediaDeviceUpdateWorker.onDeviceListUpdate(mDevices);
mMediaDeviceUpdateWorker.mLocalMediaManager = mLocalMediaManager;
- mMediaOutputSlice.init(TEST_PACKAGE_NAME, mMediaDeviceUpdateWorker, mIconDrawableFactory);
+ mMediaOutputSlice.init(TEST_PACKAGE_NAME, mMediaDeviceUpdateWorker);
}
@Test
- public void getSlice_shouldHaveAppTitle() {
+ public void getSlice_shouldHaveActiveDeviceName() {
+ mDevices.clear();
+ final MediaDevice device = mock(MediaDevice.class);
+ when(device.getName()).thenReturn(TEST_DEVICE_1_NAME);
+ when(device.getIcon()).thenReturn(TEST_DEVICE_1_ICON);
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(device);
+
final Slice mediaSlice = mMediaOutputSlice.getSlice();
final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
final SliceAction primaryAction = metadata.getPrimaryAction();
- assertThat(primaryAction.getTitle().toString()).isEqualTo(TEST_LABEL);
+ assertThat(primaryAction.getTitle().toString()).isEqualTo(TEST_DEVICE_1_NAME);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
index 31daa37..c497cf8 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
@@ -24,7 +24,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
@@ -163,27 +162,4 @@
assertThat(isMobileDataEnabled).isEqualTo(seed);
}
-
- @Test
- public void airplaneModeEnabled_slicePrimaryActionIsEmpty() {
- doReturn(true).when(mMobileDataSlice).isAirplaneModeEnabled();
- doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(SUB_ID);
- final Slice mobileData = mMobileDataSlice.getSlice();
-
- final SliceMetadata metadata = SliceMetadata.from(mContext, mobileData);
- assertThat(metadata.getTitle())
- .isEqualTo(mContext.getString(R.string.mobile_data_settings_title));
-
- assertThat(metadata.getSubtitle())
- .isEqualTo(mContext.getString(R.string.mobile_data_ap_mode_disabled));
-
- final List<SliceAction> toggles = metadata.getToggles();
- assertThat(toggles).hasSize(0);
-
- final SliceAction primaryAction = metadata.getPrimaryAction();
- final PendingIntent pendingIntent = primaryAction.getAction();
- final Intent actionIntent = pendingIntent.getIntent();
-
- assertThat(actionIntent.getAction()).isNull();
- }
}
diff --git a/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java b/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
index a51b7b0..1d5c3c2 100644
--- a/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
+++ b/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
@@ -76,15 +76,16 @@
}
@Test
- public void startMediaOutputSlice_withoutPackageName_bundleShouldNotHaveValue() {
+ public void startMediaOutputSlice_withoutPackageName_bundleShouldHaveValue() {
final Intent intent = new Intent()
.setAction("com.android.settings.panel.action.MEDIA_OUTPUT");
final SettingsPanelActivity activity =
Robolectric.buildActivity(SettingsPanelActivity.class, intent).create().get();
- assertThat(activity.mBundle.containsKey(KEY_MEDIA_PACKAGE_NAME)).isFalse();
- assertThat(activity.mBundle.containsKey(KEY_PANEL_TYPE_ARGUMENT)).isFalse();
+ assertThat(activity.mBundle.containsKey(KEY_MEDIA_PACKAGE_NAME)).isTrue();
+ assertThat(activity.mBundle.getString(KEY_PANEL_TYPE_ARGUMENT))
+ .isEqualTo("com.android.settings.panel.action.MEDIA_OUTPUT");
}
@Test
diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
index 54a79f4..5b7c863 100644
--- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
@@ -238,63 +238,6 @@
verify(mLocalBluetoothManager).setForegroundActivity(null);
}
- @Test
- public void onPreferenceChange_toThisDevice_shouldSetDefaultSummary() {
- mController.mConnectedDevices.clear();
- mController.mConnectedDevices.add(mBluetoothDevice);
-
- mController.onPreferenceChange(mPreference,
- mContext.getText(R.string.media_output_default_summary));
-
- assertThat(mPreference.getSummary()).isEqualTo(
- mContext.getText(R.string.media_output_default_summary));
- }
-
- /**
- * One Bluetooth devices are available, and select the device.
- * Preference summary should be device name.
- */
- @Test
- public void onPreferenceChange_toBtDevice_shouldSetBtDeviceName() {
- mController.mConnectedDevices.clear();
- mController.mConnectedDevices.add(mBluetoothDevice);
-
- mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1);
-
- assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
- }
-
- /**
- * More than one Bluetooth devices are available, and select second device.
- * Preference summary should be second device name.
- */
- @Test
- public void onPreferenceChange_toBtDevices_shouldSetSecondBtDeviceName() {
- ShadowBluetoothDevice shadowBluetoothDevice;
- BluetoothDevice secondBluetoothDevice;
- secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2);
- shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice);
- shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2);
- mController.mConnectedDevices.clear();
- mController.mConnectedDevices.add(mBluetoothDevice);
- mController.mConnectedDevices.add(secondBluetoothDevice);
-
- mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_2);
-
- assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_2);
- }
-
- /**
- * mConnectedDevices is empty.
- * onPreferenceChange should return false.
- */
- @Test
- public void onPreferenceChange_connectedDeviceIsNull_shouldReturnFalse() {
- mController.mConnectedDevices.clear();
-
- assertThat(mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1)).isFalse();
- }
-
/**
* Audio stream output to bluetooth sco headset which is the subset of all sco device.
* isStreamFromOutputDevice should return true.
@@ -416,39 +359,6 @@
}
/**
- * One A2dp device is connected.
- * getConnectedA2dpDevices should add this device to list.
- */
- @Test
- public void getConnectedA2dpDevices_oneConnectedA2dpDevice_shouldAddDeviceToList() {
- mEmptyDevices.clear();
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
-
- mEmptyDevices.addAll(mController.getConnectedA2dpDevices());
-
- assertThat(mEmptyDevices).containsExactly(mBluetoothDevice);
- }
-
- /**
- * More than one A2dp devices are connected.
- * getConnectedA2dpDevices should add all devices to list.
- */
- @Test
- public void getConnectedA2dpDevices_moreThanOneConnectedA2dpDevice_shouldAddDeviceToList() {
- mEmptyDevices.clear();
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
-
- mEmptyDevices.addAll(mController.getConnectedA2dpDevices());
-
- assertThat(mEmptyDevices).containsExactly(mBluetoothDevice, mLeftBluetoothHapDevice);
- }
-
- /**
* One hands free profile device is connected.
* getConnectedA2dpDevices should add this device to list.
*/
@@ -488,10 +398,6 @@
}
@Override
- public void setActiveBluetoothDevice(BluetoothDevice device) {
- }
-
- @Override
public BluetoothDevice findActiveDevice() {
return null;
}
diff --git a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
index 4010145..0eada60 100644
--- a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
@@ -57,6 +57,7 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowBluetoothDevice;
@@ -107,7 +108,7 @@
private BluetoothDevice mLeftBluetoothHapDevice;
private BluetoothDevice mRightBluetoothHapDevice;
private LocalBluetoothManager mLocalBluetoothManager;
- private AudioSwitchPreferenceController mController;
+ private HandsFreeProfileOutputPreferenceController mController;
private List<BluetoothDevice> mProfileConnectedDevices;
private List<BluetoothDevice> mHearingAidActiveDevices;
@@ -478,4 +479,61 @@
assertThat(mController.findActiveDevice()).isNull();
}
+
+ /**
+ * One Bluetooth devices are available, and select the device.
+ * Preference summary should be device name.
+ */
+ @Test
+ public void onPreferenceChange_toBtDevice_shouldSetBtDeviceName() {
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+
+ mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1);
+
+ assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
+
+ /**
+ * More than one Bluetooth devices are available, and select second device.
+ * Preference summary should be second device name.
+ */
+ @Test
+ public void onPreferenceChange_toBtDevices_shouldSetSecondBtDeviceName() {
+ ShadowBluetoothDevice shadowBluetoothDevice;
+ BluetoothDevice secondBluetoothDevice;
+ secondBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2);
+ shadowBluetoothDevice = Shadows.shadowOf(secondBluetoothDevice);
+ shadowBluetoothDevice.setName(TEST_DEVICE_NAME_2);
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+ mController.mConnectedDevices.add(secondBluetoothDevice);
+
+ mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_2);
+
+ assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_2);
+ }
+
+ /**
+ * mConnectedDevices is empty.
+ * onPreferenceChange should return false.
+ */
+ @Test
+ public void onPreferenceChange_connectedDeviceIsNull_shouldReturnFalse() {
+ mController.mConnectedDevices.clear();
+
+ assertThat(mController.onPreferenceChange(mPreference, TEST_DEVICE_ADDRESS_1)).isFalse();
+ }
+
+ @Test
+ public void onPreferenceChange_toThisDevice_shouldSetDefaultSummary() {
+ mController.mConnectedDevices.clear();
+ mController.mConnectedDevices.add(mBluetoothDevice);
+
+ mController.onPreferenceChange(mPreference,
+ mContext.getText(R.string.media_output_default_summary));
+
+ assertThat(mPreference.getSummary()).isEqualTo(
+ mContext.getText(R.string.media_output_default_summary));
+ }
}
diff --git a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
index 8c7faef..7fcd3d2 100644
--- a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
@@ -17,16 +17,15 @@
package com.android.settings.sound;
import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+import static android.media.AudioSystem.DEVICE_OUT_EARPIECE;
import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,9 +33,10 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
+import android.content.Intent;
import android.media.AudioManager;
-import androidx.preference.ListPreference;
+import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
@@ -49,11 +49,13 @@
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.media.MediaOutputSliceConstants;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -80,8 +82,6 @@
private static final String TEST_DEVICE_ADDRESS_2 = "00:B2:B2:B2:B2:B2";
private static final String TEST_DEVICE_ADDRESS_3 = "00:C3:C3:C3:C3:C3";
private static final String TEST_DEVICE_ADDRESS_4 = "00:D4:D4:D4:D4:D4";
- private final static long HISYNCID1 = 10;
- private final static long HISYNCID2 = 11;
@Mock
private LocalBluetoothManager mLocalManager;
@@ -98,7 +98,7 @@
private Context mContext;
private PreferenceScreen mScreen;
- private ListPreference mPreference;
+ private Preference mPreference;
private AudioManager mAudioManager;
private ShadowAudioManager mShadowAudioManager;
private BluetoothManager mBluetoothManager;
@@ -108,8 +108,8 @@
private BluetoothDevice mLeftBluetoothHapDevice;
private BluetoothDevice mRightBluetoothHapDevice;
private LocalBluetoothManager mLocalBluetoothManager;
- private AudioSwitchPreferenceController mController;
- private List<BluetoothDevice> mProfileConnectedDevices;
+ private MediaOutputPreferenceController mController;
+ private List<BluetoothDevice> mProfileConnectableDevices;
private List<BluetoothDevice> mHearingAidActiveDevices;
@Before
@@ -149,8 +149,8 @@
mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
mScreen = spy(new PreferenceScreen(mContext, null));
- mPreference = new ListPreference(mContext);
- mProfileConnectedDevices = new ArrayList<>();
+ mPreference = new Preference(mContext);
+ mProfileConnectableDevices = new ArrayList<>();
mHearingAidActiveDevices = new ArrayList<>(2);
when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
@@ -166,61 +166,140 @@
ShadowBluetoothUtils.reset();
}
+
/**
- * In normal mode, bluetooth device with HisyncId.
- * HearingAidProfile should set active device to this device.
+ * A2DP Bluetooth device(s) are not connected nor previously connected
+ * Preference should be invisible
*/
@Test
- public void setActiveBluetoothDevice_btDeviceWithHisyncId_shouldSetBtDeviceActive() {
+ public void updateState_withoutConnectableBtDevice_preferenceInvisible() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_EARPIECE);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
+ mProfileConnectableDevices.clear();
+ when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ mPreference.setVisible(true);
- mController.setActiveBluetoothDevice(mLeftBluetoothHapDevice);
-
- verify(mHearingAidProfile).setActiveDevice(mLeftBluetoothHapDevice);
- verify(mA2dpProfile, never()).setActiveDevice(mLeftBluetoothHapDevice);
+ assertThat(mPreference.isVisible()).isTrue();
+ mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isFalse();
}
/**
- * In normal mode, bluetooth device without HisyncId.
- * A2dpProfile should set active device to this device.
+ * A2DP Bluetooth device(s) are connectable, no matter active or inactive
+ * Preference should be visible
*/
@Test
- public void setActiveBluetoothDevice_btDeviceWithoutHisyncId_shouldSetBtDeviceActive() {
+ public void updateState_withConnectableBtDevice_preferenceVisible() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mProfileConnectableDevices.clear();
+ mProfileConnectableDevices.add(mBluetoothDevice);
+ when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ assertThat(mPreference.isVisible()).isFalse();
- mController.setActiveBluetoothDevice(mBluetoothDevice);
+ // Without Active Bluetooth Device
+ mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isTrue();
- verify(mA2dpProfile).setActiveDevice(mBluetoothDevice);
- verify(mHearingAidProfile, never()).setActiveDevice(mBluetoothDevice);
+ // With Active Bluetooth Device
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
+ mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isTrue();
}
/**
- * In normal mode, set active device to "this device".
- * A2dpProfile should set to null.
- * HearingAidProfile should set to null.
+ * A2DP Bluetooth device(s) are connectable, but no device is set as activated
+ * Preference summary should be "This device"
*/
@Test
- public void setActiveBluetoothDevice_setNull_shouldSetNullToBothProfiles() {
+ public void updateState_withConnectableBtDevice_withoutActiveBtDevice_setDefaultSummary() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_EARPIECE);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mProfileConnectableDevices.clear();
+ mProfileConnectableDevices.add(mBluetoothDevice);
+ mProfileConnectableDevices.add(mSecondBluetoothDevice);
+ when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(null);
- mController.setActiveBluetoothDevice(null);
-
- verify(mA2dpProfile).setActiveDevice(null);
- verify(mHearingAidProfile).setActiveDevice(null);
+ assertThat(mPreference.getSummary()).isNull();
+ mController.updateState(mPreference);
+ assertThat(mPreference.getSummary()).isEqualTo(
+ mContext.getText(R.string.media_output_default_summary));
}
/**
- * During a call
- * A2dpProfile should not set active device.
+ * A2DP Bluetooth device(s) are connected and active
+ * Preference summary should be device's name
*/
@Test
- public void setActiveBluetoothDevice_duringACall_shouldNotSetActiveDeviceToA2dpProfile() {
- mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ public void updateState_withActiveBtDevice_setActivatedDeviceName() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mProfileConnectableDevices.clear();
+ mProfileConnectableDevices.add(mBluetoothDevice);
+ mProfileConnectableDevices.add(mSecondBluetoothDevice);
+ when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
- mController.setActiveBluetoothDevice(mBluetoothDevice);
+ assertThat(mPreference.getSummary()).isNull();
+ mController.updateState(mPreference);
+ assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
- verify(mA2dpProfile, times(0)).setActiveDevice(any(BluetoothDevice.class));
+
+ /**
+ * Hearing Aid device(s) are connectable, no matter active or inactive
+ * Preference should be visible
+ */
+ @Test
+ public void updateState_withConnectableHADevice_preferenceVisible() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectableDevices()).thenReturn(mHearingAidActiveDevices);
+ assertThat(mPreference.isVisible()).isFalse();
+
+ // Without Active Hearing Aid Device
+ mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isTrue();
+
+ // With Active Hearing Aid Device
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+ mController.updateState(mPreference);
+ assertThat(mPreference.isVisible()).isTrue();
+ }
+
+ /**
+ * Hearing Aid device(s) are connected and active
+ * Preference summary should be device's name
+ */
+ @Test
+ public void updateState_withActiveHADevice_setActivatedDeviceName() {
+ mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mHearingAidActiveDevices.clear();
+ mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
+ when(mHearingAidProfile.getConnectableDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
+
+ assertThat(mPreference.getSummary()).isNull();
+ mController.updateState(mPreference);
+ assertThat(mPreference.getSummary()).isEqualTo(TEST_HAP_DEVICE_NAME_1);
+
+ }
+
+ @Test
+ public void click_launch_outputSwitcherSlice() {
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ mController.handlePreferenceTreeClick(mPreference);
+ verify(mContext, never()).startActivity(intentCaptor.capture());
+
+ mPreference.setKey(TEST_KEY);
+ mController.handlePreferenceTreeClick(mPreference);
+ verify(mContext).startActivity(intentCaptor.capture());
+ assertThat(intentCaptor.getValue().getAction())
+ .isEqualTo(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT);
}
/**
@@ -254,24 +333,6 @@
}
/**
- * No available A2dp BT devices:
- * Preference should be invisible
- * Preference summary should be "This device"
- */
- @Test
- public void updateState_noAvailableA2dpBtDevices_shouldDisableAndSetDefaultSummary() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- List<BluetoothDevice> emptyDeviceList = new ArrayList<>();
- when(mA2dpProfile.getConnectedDevices()).thenReturn(emptyDeviceList);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isFalse();
- String defaultString = mContext.getString(R.string.media_output_default_summary);
- assertThat(mPreference.getSummary()).isEqualTo(defaultString);
- }
-
- /**
* Media stream is captured by something else (cast device):
* Preference should be invisible
* Preference summary should be "unavailable"
@@ -287,235 +348,6 @@
assertThat(mPreference.getSummary()).isEqualTo(defaultString);
}
- /**
- * One A2DP Bluetooth device is available and active.
- * Preference should be visible
- * Preference summary should be the activated device name
- */
- @Test
- public void updateState_oneA2dpBtDeviceAreAvailable_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_1);
- }
-
- /**
- * More than one A2DP Bluetooth devices are available, and second device is active.
- * Preference should be visible
- * Preference summary should be the activated device name
- */
- @Test
- public void updateState_moreThanOneA2DpBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- mProfileConnectedDevices.add(mSecondBluetoothDevice);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mA2dpProfile.getActiveDevice()).thenReturn(mSecondBluetoothDevice);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(TEST_DEVICE_NAME_2);
- }
-
- /**
- * A2DP Bluetooth device(s) are available, but wired headset is plugged in and activated
- * Preference should be visible
- * Preference summary should be "This device"
- */
- @Test
- public void updateState_a2dpDevicesAvailableWiredHeadsetIsActivated_shouldSetDefaultSummary() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mA2dpProfile.getActiveDevice()).thenReturn(null);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(
- mContext.getString(R.string.media_output_default_summary));
- }
-
-
- /**
- * A2DP Bluetooth device(s) are available, but current device speaker is activated
- * Preference should be visible
- * Preference summary should be "This device"
- */
- @Test
- public void updateState_a2dpDevicesAvailableCurrentDeviceActivated_shouldSetDefaultSummary() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mA2dpProfile.getActiveDevice()).thenReturn(null);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(
- mContext.getString(R.string.media_output_default_summary));
- }
-
- /**
- * One hearing aid profile Bluetooth device is available and active.
- * Preference should be visible
- * Preference summary should be the activated device name
- */
- @Test
- public void updateState_oneHapBtDeviceAreAvailable_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- mHearingAidActiveDevices.clear();
- mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
- }
-
- /**
- * More than one hearing aid profile Bluetooth devices are available, and second
- * device is active.
- * Preference should be visible
- * Preference summary should be the activated device name
- */
- @Test
- public void updateState_moreThanOneHapBtDevicesAreAvailable_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- mProfileConnectedDevices.add(mRightBluetoothHapDevice);
- mHearingAidActiveDevices.clear();
- mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
- when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
- }
-
- /**
- * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
- * profile devices with same HisyncId are active. Both of HAP device are active,
- * "left" side HAP device is added first.
- * Preference should be visible
- * Preference summary should be the activated device name
- * ConnectedDevice should not contain second HAP device with same HisyncId
- */
- @Test
- public void updateState_hapBtDeviceWithSameId_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- //with same HisyncId, first one will remain in UI.
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- mProfileConnectedDevices.add(mRightBluetoothHapDevice);
- mHearingAidActiveDevices.clear();
- mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
- mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
- when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(mLeftBluetoothHapDevice.getName());
- assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isTrue();
- assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isFalse();
- }
-
- /**
- * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
- * profile devices with same HisyncId. Both of HAP device are active,
- * "right" side HAP device is added first.
- * Preference should be visible
- * Preference summary should be the activated device name
- * ConnectedDevice should not contain second HAP device with same HisyncId
- */
- @Test
- public void updateState_hapBtDeviceWithSameIdButDifferentOrder_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- //with same HisyncId, first one will remain in UI.
- mProfileConnectedDevices.add(mRightBluetoothHapDevice);
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- mHearingAidActiveDevices.clear();
- mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
- mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
- when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID1);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
- assertThat(mController.mConnectedDevices.contains(mRightBluetoothHapDevice)).isTrue();
- assertThat(mController.mConnectedDevices.contains(mLeftBluetoothHapDevice)).isFalse();
- }
-
- /**
- * Both hearing aid profile and A2dp Bluetooth devices are available, and two hearing aid
- * profile devices with different HisyncId. One of HAP device is active.
- * Preference should be visible
- * Preference summary should be the activated device name
- * ConnectedDevice should contain both HAP device with different HisyncId
- */
- @Test
- public void updateState_hapBtDeviceWithDifferentId_shouldSetActivatedDeviceName() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
- mProfileConnectedDevices.clear();
- mProfileConnectedDevices.add(mBluetoothDevice);
- mProfileConnectedDevices.add(mLeftBluetoothHapDevice);
- mProfileConnectedDevices.add(mRightBluetoothHapDevice);
- mHearingAidActiveDevices.clear();
- mHearingAidActiveDevices.add(null);
- mHearingAidActiveDevices.add(mRightBluetoothHapDevice);
- when(mHearingAidProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
- when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
- when(mHearingAidProfile.getHiSyncId(mLeftBluetoothHapDevice)).thenReturn(HISYNCID1);
- when(mHearingAidProfile.getHiSyncId(mRightBluetoothHapDevice)).thenReturn(HISYNCID2);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isTrue();
- assertThat(mPreference.getSummary()).isEqualTo(mRightBluetoothHapDevice.getName());
- assertThat(mController.mConnectedDevices).containsExactly(mBluetoothDevice,
- mLeftBluetoothHapDevice, mRightBluetoothHapDevice);
- }
-
@Test
public void findActiveDevice_onlyA2dpDeviceActive_returnA2dpDevice() {
when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreferenceTest.java b/tests/robotests/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreferenceTest.java
new file mode 100644
index 0000000..cc2622f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreferenceTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 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.calling;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Parcelable;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.io.IOException;
+
+@RunWith(RobolectricTestRunner.class)
+public class ListWithEntrySummaryPreferenceTest {
+
+ private Context mContext;
+ private ListWithEntrySummaryPreference mPreference;
+
+ private CharSequence[] mDefaultEntries =
+ {"default_entry1", "default_entry2", "default_entry3"};
+ private CharSequence[] mDefaultEntryValues = {"0", "1", "2"};
+ private CharSequence[] mDefaultEntrySummaries =
+ {"default_summary1", "default_summary2", "default_summary3"};
+
+ private CharSequence[] mCustomEntries = {"custom_entry1", "custom_entry2"};
+ private CharSequence[] mCustomEntryValues = {"0", "1"};
+ private CharSequence[] mCustomEntrySummaries = {"custom_summary1", "custom_summary2"};
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mPreference = new ListWithEntrySummaryPreference(mContext, null);
+ mPreference.setEntries(mDefaultEntries);
+ mPreference.setEntryValues(mDefaultEntryValues);
+ mPreference.setEntrySummaries(mDefaultEntrySummaries);
+ }
+
+ @Test
+ public void initialize_defaultEntries_shouldDisplayDefalutEntries() {
+ AlertDialog dialog = showDialog(mPreference);
+ ListAdapter adapter = dialog.getListView().getAdapter();
+
+ int len = mDefaultEntries.length;
+ assertThat(adapter.getCount()).isEqualTo(len);
+ for (int i = 0; i < len; i++) {
+ TextView title = adapter.getView(i, null, null).findViewById(R.id.title);
+ TextView summary = adapter.getView(i, null, null).findViewById(R.id.summary);
+ assertThat(title.getText()).isEqualTo(mDefaultEntries[i]);
+ assertThat(summary.getText()).isEqualTo(mDefaultEntrySummaries[i]);
+ }
+ }
+
+ @Test
+ public void setEntries_customEntries_shouldUpdateEntries() {
+ mPreference.setEntries(mCustomEntries);
+ mPreference.setEntryValues(mCustomEntryValues);
+ mPreference.setEntrySummaries(mCustomEntrySummaries);
+
+ AlertDialog dialog = showDialog(mPreference);
+ ListAdapter adapter = dialog.getListView().getAdapter();
+
+ int len = mCustomEntries.length;
+ assertThat(adapter.getCount()).isEqualTo(len);
+ for (int i = 0; i < len; i++) {
+ TextView title = adapter.getView(i, null, null).findViewById(R.id.title);
+ TextView summary = adapter.getView(i, null, null).findViewById(R.id.summary);
+ assertThat(title.getText()).isEqualTo(mCustomEntries[i]);
+ assertThat(summary.getText()).isEqualTo(mCustomEntrySummaries[i]);
+ }
+ }
+
+ @Test
+ public void onSaveAndRestoreInstanceState_resumePreference_shouldNotChangeEntries() {
+ setEntries_customEntries_shouldUpdateEntries();
+
+ final Parcelable parcelable = mPreference.onSaveInstanceState();
+ ListWithEntrySummaryPreference preference
+ = new ListWithEntrySummaryPreference(mContext, null);
+ preference.setEntries(mDefaultEntries);
+ preference.setEntryValues(mDefaultEntryValues);
+ preference.setEntrySummaries(mDefaultEntrySummaries);
+ preference.onRestoreInstanceState(parcelable);
+
+ AlertDialog dialog = showDialog(preference);
+ ListAdapter adapter = dialog.getListView().getAdapter();
+
+ int len = mCustomEntries.length;
+ assertThat(adapter.getCount()).isEqualTo(len);
+ for (int i = 0; i < len; i++) {
+ TextView title = adapter.getView(i, null, null).findViewById(R.id.title);
+ TextView summary = adapter.getView(i, null, null).findViewById(R.id.summary);
+ assertThat(title.getText()).isEqualTo(mCustomEntries[i]);
+ assertThat(summary.getText()).isEqualTo(mCustomEntrySummaries[i]);
+ }
+ }
+
+ private AlertDialog showDialog(ListWithEntrySummaryPreference preference) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ preference.onPrepareDialogBuilder(builder, null);
+ return builder.show();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
index 640c426..e640dee 100644
--- a/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/details/WifiDetailPreferenceControllerTest.java
@@ -384,9 +384,9 @@
}
@Test
- public void entityHeader_shouldHaveLabelSetToSsid() {
- String label = "ssid";
- when(mockAccessPoint.getSsidStr()).thenReturn(label);
+ public void entityHeader_shouldHaveLabelSetToTitle() {
+ String label = "title";
+ when(mockAccessPoint.getTitle()).thenReturn(label);
displayAndResume();
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
index cb58778..3d15197 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiManager;
import androidx.preference.PreferenceScreen;
@@ -56,12 +57,12 @@
private PreferenceScreen mScreen;
private WifiTetherSSIDPreferenceController mController;
- private ValidatedEditTextPreference mPreference;
+ private WifiTetherSsidPreference mPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mPreference = new ValidatedEditTextPreference(RuntimeEnvironment.application);
+ mPreference = new WifiTetherSsidPreference(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
@@ -121,4 +122,28 @@
assertThat(mController.getSSID()).isEqualTo(config.SSID);
assertThat(mPreference.getSummary()).isEqualTo(config.SSID);
}
+
+ @Test
+ public void displayPreference_wifiApDisabled_shouldHideQrCodeIcon() {
+ when(mWifiManager.isWifiApEnabled()).thenReturn(false);
+ final WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "test_1234";
+ config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
+ when(mWifiManager.getWifiApConfiguration()).thenReturn(config);
+
+ mController.displayPreference(mScreen);
+ assertThat(mController.isQrCodeButtonAvailable()).isEqualTo(false);
+ }
+
+ @Test
+ public void displayPreference_wifiApEnabled_shouldShowQrCodeIcon() {
+ when(mWifiManager.isWifiApEnabled()).thenReturn(true);
+ final WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "test_1234";
+ config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
+ when(mWifiManager.getWifiApConfiguration()).thenReturn(config);
+
+ mController.displayPreference(mScreen);
+ assertThat(mController.isQrCodeButtonAvailable()).isEqualTo(true);
+ }
}
diff --git a/tests/unit/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java b/tests/unit/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
index 447e2b4..653e55e 100644
--- a/tests/unit/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
+++ b/tests/unit/src/com/android/settings/homepage/contextualcards/ContextualCardLoaderTest.java
@@ -86,6 +86,7 @@
private ContextualCard getContextualCard(Uri sliceUri) {
return new ContextualCard.Builder()
.setName("test_card")
+ .setRankingScore(0.5f)
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(sliceUri)
.build();
diff --git a/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java b/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
index 405ed4a..38211b3 100644
--- a/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
+++ b/tests/unit/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivityTest.java
@@ -147,7 +147,7 @@
@Test
public void rotateScreen_shouldGetCorrectWifiNetworkConfig() {
final WifiNetworkConfig wifiNetworkConfig = new WifiNetworkConfig("WPA", "WifiSsid",
- "password", /* hiddenSsid */ false, /* networkId */ 0);
+ "password", /* hiddenSsid */ false, /* networkId */ 0, /* isHotspot */ true);
final Intent intent = new Intent(Settings.ACTION_PROCESS_WIFI_EASY_CONNECT_URI);
intent.setData(Uri.parse(VALID_WIFI_DPP_QR_CODE));
@@ -173,5 +173,6 @@
assertThat(restoredWifiNetworkConfig.getPreSharedKey()).isEqualTo("password");
assertThat(restoredWifiNetworkConfig.getHiddenSsid()).isFalse();
assertThat(restoredWifiNetworkConfig.getNetworkId()).isEqualTo(0);
+ assertThat(restoredWifiNetworkConfig.isHotspot()).isTrue();
}
}