Merge "Support the managed profile deep links for large screen" into tm-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ae87edb..71c07a0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4474,6 +4474,16 @@
</intent-filter>
</receiver>
+ <activity
+ android:name="com.android.settings.bluetooth.QrCodeScanModeActivity"
+ android:permission="android.permission.BLUETOOTH_CONNECT"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.settings.BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
<!-- This is the longest AndroidManifest.xml ever. -->
</application>
</manifest>
diff --git a/res/drawable/ic_qr_code_scanner.xml b/res/drawable/ic_qr_code_scanner.xml
new file mode 100644
index 0000000..f6f63c5
--- /dev/null
+++ b/res/drawable/ic_qr_code_scanner.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp"
+ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M2,7V2H7V4H4V7ZM2,22V17H4V20H7V22ZM17,22V20H20V17H22V22ZM20,7V4H17V2H22V7ZM17.5,17.5H19V19H17.5ZM17.5,14.5H19V16H17.5ZM16,16H17.5V17.5H16ZM14.5,17.5H16V19H14.5ZM13,16H14.5V17.5H13ZM16,13H17.5V14.5H16ZM14.5,14.5H16V16H14.5ZM13,13H14.5V14.5H13ZM19,5V11H13V5ZM11,13V19H5V13ZM11,5V11H5V5ZM9.5,17.5V14.5H6.5V17.5ZM9.5,9.5V6.5H6.5V9.5ZM17.5,9.5V6.5H14.5V9.5Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/layout/qrcode_scan_mode_activity.xml b/res/layout/qrcode_scan_mode_activity.xml
new file mode 100644
index 0000000..f0a182b
--- /dev/null
+++ b/res/layout/qrcode_scan_mode_activity.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</LinearLayout>
diff --git a/res/layout/qrcode_scanner_fragment.xml b/res/layout/qrcode_scanner_fragment.xml
new file mode 100644
index 0000000..2c543f2
--- /dev/null
+++ b/res/layout/qrcode_scanner_fragment.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/sud_layout_icon_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="3"
+ android:layout_marginBottom="35dp">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:gravity="center"
+ android:orientation="vertical">
+ <ImageView
+ android:id="@+id/sud_layout_icon"
+ android:src="@drawable/ic_qr_code_scanner"
+ android:tint="?androidprv:attr/colorAccentPrimaryVariant"
+ android:layout_width="@dimen/qrcode_icon_size"
+ android:layout_height="@dimen/qrcode_icon_size"
+ android:contentDescription="@null"/>
+
+ <TextView
+ android:id="@+id/sud_layout_title"
+ style="@style/QrCodeScanner"
+ android:textSize="24sp"
+ android:text="@string/bluetooth_find_broadcast_button_scan"
+ android:gravity="center"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="19dp"/>
+
+ <TextView
+ android:id="@+id/sud_layout_subtitle"
+ style="@style/QrCodeScanner"
+ android:text="@string/bt_le_audio_scan_qr_code_scanner"
+ android:gravity="center"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="7"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:gravity="center"
+ android:clipChildren="true">
+ <TextureView
+ android:id="@+id/preview_view"
+ android:layout_marginStart="@dimen/qrcode_preview_margin"
+ android:layout_marginEnd="@dimen/qrcode_preview_margin"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qrcode_preview_size"/>
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/error_message"
+ style="@style/TextAppearance.ErrorText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginStart="?attr/sudMarginStart"
+ android:layout_marginEnd="?attr/sudMarginEnd"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:visibility="invisible"/>
+
+ </LinearLayout>
+
+
+</LinearLayout>
+
diff --git a/res/layout/wifi_calling_settings_preferences.xml b/res/layout/wifi_calling_settings_preferences.xml
index 9a6cbe6..bad90ad 100644
--- a/res/layout/wifi_calling_settings_preferences.xml
+++ b/res/layout/wifi_calling_settings_preferences.xml
@@ -21,12 +21,6 @@
android:layout_height="match_parent"
android:orientation="vertical">
- <com.android.settings.widget.SettingsMainSwitchBar
- android:id="@+id/switch_bar"
- android:title="@string/wifi_calling_main_switch_title"
- android:layout_height="wrap_content"
- android:layout_width="match_parent" />
-
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1582d21..3d616a8 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -485,4 +485,9 @@
<!-- Sims/Data mobile/Calls/SMS select dialog-->
<dimen name="sims_select_margin_bottom">24dp</dimen>
<dimen name="sims_select_margin_top">8dp</dimen>
+
+ <!-- QR code picture size -->
+ <dimen name="qrcode_preview_margin">40dp</dimen>
+ <dimen name="qrcode_preview_radius">30dp</dimen>
+ <dimen name="qrcode_icon_size">27dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9ab0cc2..1d685ef 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1297,7 +1297,10 @@
<string name="security_advanced_settings_no_work_profile_settings_summary">Encryption, credentials, and more</string>
<!-- Search keywords for the "More security settings" section in security settings. [CHAR_LIMIT=NONE] -->
<string name="security_advanced_settings_keywords">security, more security settings, more settings, advanced security settings</string>
-
+ <!-- Title for the section that has additional privacy settings. [CHAR LIMIT=60] -->
+ <string name="privacy_advanced_settings">More privacy settings</string>
+ <!-- Title for the section that has additional privacy settings. [CHAR LIMIT=60] -->
+ <string name="privacy_advanced_settings_summary">Autofill, activity controls, and more</string>
<!-- Text shown when "Add fingerprint" button is disabled -->
<string name="fingerprint_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
@@ -8973,6 +8976,9 @@
<!-- Configure Notifications: Work profile section header [CHAR LIMIT=30] -->
<string name="profile_section_header">Work notifications</string>
+ <!-- Configure Notifications: Work profile section header [CHAR LIMIT=30] -->
+ <string name="profile_section_header_for_advanced_privacy">Work profile</string>
+
<!-- Configure Notifications: section header for prioritizer settings [CHAR LIMIT=80] -->
<string name="smart_notifications_title">Adaptive notifications</string>
@@ -13649,7 +13655,9 @@
<string name="default_active_sim_mobile_data">mobile data</string>
<!-- Provider Model: Message to describe "Wi-Fi scan always available feature" when Wi-Fi is off and Wi-Fi
scanning is on. To mark a link to bring the user to "scanning settings" screen. [CHAR LIMIT=NONE]-->
- <string name="wifi_scan_notify_message">To improve device experience, apps and services can still scan for Wi\u2011Fi networks at any time, even when Wi\u2011Fi is off. This can be used, for example, to improve location-based features and services. You can change this in Wi\u2011Fi scanning settings. <annotation id="link">Change</annotation></string>
+ <string name="wifi_scan_notify_message">To improve device experience, apps and services can still scan for Wi\u2011Fi networks at any time, even when Wi\u2011Fi is off. This can be used, for example, to improve location-based features and services. You can change this in Wi\u2011Fi scanning settings.</string>
+ <!-- Provider Model: Link text to bring the user to "scanning settings" screen. [CHAR LIMIT=NONE]-->
+ <string name="wifi_scan_change">Change</string>
<!-- Summary text separator for preferences including a short description
(eg. "Connected / 5G"). [CHAR LIMIT=50] -->
@@ -14163,4 +14171,11 @@
<string name="find_broadcast_password_dialog_connection_error">Can\u2019t connect. Try again.</string>
<!-- The error message of enter password dialog in bluetooth find broadcast page [CHAR LIMIT=none] -->
<string name="find_broadcast_password_dialog_password_error">Wrong password</string>
+
+
+ <!-- [CHAR LIMIT=NONE] Le audio QR code scanner sub-title -->
+ <string name="bt_le_audio_scan_qr_code_scanner">To start listening, center the QR code below</string>
+ <!-- [CHAR LIMIT=NONE] Hint for QR code process failure -->
+ <string name="bt_le_audio_qr_code_is_not_valid_format">QR code isn\u0027t a valid format</string>
+
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7a87993..f147ce9 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -962,4 +962,11 @@
<item name="android:minWidth">0dp</item>
<item name="android:textAllCaps">false</item>
</style>
+
+ <style name="QrCodeScanner">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textDirection">locale</item>
+ </style>
</resources>
diff --git a/res/xml/privacy_advanced_settings.xml b/res/xml/privacy_advanced_settings.xml
new file mode 100644
index 0000000..9f465d4
--- /dev/null
+++ b/res/xml/privacy_advanced_settings.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:key="privacy_dashboard_page"
+ android:title="@string/privacy_advanced_settings">
+
+ <!-- Work Policy info -->
+ <Preference
+ android:key="work_policy_info"
+ android:title="@string/work_policy_privacy_settings"
+ android:summary="@string/work_policy_privacy_settings_summary"
+ settings:controller="com.android.settings.privacy.WorkPolicyInfoPreferenceController"/>
+
+ <!-- Connected work and personal apps -->
+ <Preference
+ android:key="interact_across_profiles_privacy"
+ android:title="@string/interact_across_profiles_title"
+ android:fragment="com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesSettings"
+ settings:searchable="false"
+ settings:controller="com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesController" />
+
+ <!-- Accessibility usage -->
+ <Preference
+ android:key="privacy_accessibility_usage"
+ android:title="@string/accessibility_usage_title"
+ settings:controller="com.android.settings.privacy.AccessibilityUsagePreferenceController">
+ <intent android:action="android.intent.action.REVIEW_ACCESSIBILITY_SERVICES"/>
+ </Preference>
+
+ <!-- On lock screen notifications -->
+ <com.android.settings.RestrictedListPreference
+ android:key="privacy_lock_screen_notifications"
+ android:title="@string/lock_screen_notifs_title"
+ android:summary="@string/summary_placeholder"
+ settings:searchable="false"/>
+
+ <!-- Privacy Service -->
+ <PreferenceCategory
+ android:key="privacy_services"
+ android:layout="@layout/preference_category_no_label"/>
+
+ <PreferenceCategory
+ android:key="dashboard_tile_placeholder"/>
+
+ <!-- Work profile settings are at the bottom with high order value to avoid users thinking that
+ any of the above settings (including dynamic) are specific to the work profile. -->
+ <PreferenceCategory
+ android:key="privacy_work_profile_notifications_category"
+ android:title="@string/profile_section_header_for_advanced_privacy"
+ android:order="998">
+
+ <com.android.settings.RestrictedListPreference
+ android:key="privacy_lock_screen_work_profile_notifications"
+ android:title="@string/locked_work_profile_notification_title"
+ android:summary="@string/summary_placeholder"
+ android:order="999"
+ settings:searchable="false"/>
+ </PreferenceCategory>
+
+ <!-- Content Capture -->
+
+ <!-- NOTE: content capture has a different preference, depending whether or not the
+ ContentCaptureService implementations defines a custom settings activitiy on its manifest.
+ Hence, we show both here, but the controller itself will decide if it's available or not.
+ -->
+
+ <SwitchPreference
+ android:key="content_capture"
+ android:title="@string/content_capture"
+ android:summary="@string/content_capture_summary"
+ settings:controller="com.android.settings.privacy.EnableContentCapturePreferenceController"/>
+
+ <com.android.settingslib.PrimarySwitchPreference
+ android:key="content_capture_custom_settings"
+ android:title="@string/content_capture"
+ android:summary="@string/content_capture_summary"
+ settings:controller="com.android.settings.privacy.EnableContentCaptureWithServiceSettingsPreferenceController"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/wifi_calling_settings.xml b/res/xml/wifi_calling_settings.xml
index 902ff1a..c45f702 100644
--- a/res/xml/wifi_calling_settings.xml
+++ b/res/xml/wifi_calling_settings.xml
@@ -19,6 +19,10 @@
android:key="wifi_calling_settings"
android:title="@string/wifi_calling_settings_title">
+ <com.android.settings.widget.SettingsMainSwitchPreference
+ android:key="wifi_calling_switch_bar"
+ android:title="@string/wifi_calling_main_switch_title" />
+
<com.android.settings.wifi.calling.ListWithEntrySummaryPreference
android:key="wifi_calling_mode"
isPreferenceVisible="false"
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 9f191f6..ee0743a 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -16,6 +16,8 @@
package com.android.settings;
+import static android.provider.Settings.ACTION_PRIVACY_SETTINGS;
+
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -214,7 +216,8 @@
/** Redirects to SafetyCenter if enabled. */
@VisibleForTesting
public void handleSafetyCenterRedirection() {
- if (SafetyCenterManagerWrapper.get().isEnabled(this)) {
+ if (ACTION_PRIVACY_SETTINGS.equals(getIntent().getAction())
+ && SafetyCenterManagerWrapper.get().isEnabled(this)) {
try {
startActivity(new Intent(Intent.ACTION_SAFETY_CENTER));
finish();
diff --git a/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java
index 930fbe4..6ead390 100644
--- a/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java
@@ -27,8 +27,11 @@
import com.android.settings.R;
import com.android.settingslib.Utils;
+import com.android.settingslib.widget.LayoutPreference;
import com.google.android.setupdesign.GlifPreferenceLayout;
+import com.google.android.setupdesign.util.LayoutStyler;
+
/**
* A {@link androidx.preference.PreferenceFragmentCompat} that displays the settings page related
@@ -47,6 +50,8 @@
icon.setTintList(Utils.getColorAttr(getContext(), android.R.attr.colorPrimary));
AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title,
/* description= */ null, icon);
+
+ updateResetButtonPadding();
}
@Override
@@ -66,4 +71,14 @@
// Hides help center in action bar and footer bar in SuW
return 0;
}
+
+ /**
+ * Updates the padding of the reset button to meet for SetupWizard style.
+ */
+ private void updateResetButtonPadding() {
+ final LayoutPreference resetPreference = (LayoutPreference) findPreference(RESET_KEY);
+ final ViewGroup parentView =
+ (ViewGroup) resetPreference.findViewById(R.id.reset_button).getParent();
+ LayoutStyler.applyPartnerCustomizationLayoutPaddingStyle(parentView);
+ }
}
diff --git a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
index f9a1113..0af8aa1 100644
--- a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
@@ -49,6 +49,8 @@
if (mTopIntroPreference != null) {
mTopIntroPreference.setVisible(false);
}
+
+ mToggleServiceSwitchPreference.applyPartnerCustomizationPaddingStyle();
}
@Override
diff --git a/src/com/android/settings/bluetooth/BluetoothBroadcastSourcePreference.java b/src/com/android/settings/bluetooth/BluetoothBroadcastSourcePreference.java
index 17b604c..733a4a9 100644
--- a/src/com/android/settings/bluetooth/BluetoothBroadcastSourcePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothBroadcastSourcePreference.java
@@ -16,7 +16,9 @@
package com.android.settings.bluetooth;
+import android.bluetooth.BluetoothLeAudioContentMetadata;
import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothLeBroadcastSubgroup;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -43,16 +45,15 @@
private static final int RESOURCE_ID_ICON = R.drawable.settings_input_antenna;
private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
+ private BluetoothLeBroadcastReceiveState mBluetoothLeBroadcastReceiveState;
private ImageView mFrictionImageView;
private String mTitle;
private boolean mStatus;
private boolean mIsEncrypted;
- BluetoothBroadcastSourcePreference(@NonNull Context context,
- @NonNull BluetoothLeBroadcastMetadata source) {
+ BluetoothBroadcastSourcePreference(@NonNull Context context) {
super(context);
initUi();
- updateMetadataAndRefreshUi(source, false);
}
@Override
@@ -68,7 +69,7 @@
private void initUi() {
setLayoutResource(R.layout.preference_access_point);
setWidgetLayoutResource(R.layout.access_point_friction_widget);
-
+ mTitle = getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO);
mStatus = false;
final Drawable drawable = getContext().getDrawable(RESOURCE_ID_ICON);
if (drawable != null) {
@@ -105,9 +106,20 @@
*/
public void updateMetadataAndRefreshUi(BluetoothLeBroadcastMetadata source, boolean status) {
mBluetoothLeBroadcastMetadata = source;
- mTitle = getBroadcastMetadataProgramInfo();
+ mTitle = getProgramInfo();
mIsEncrypted = mBluetoothLeBroadcastMetadata.isEncrypted();
- mStatus = status;
+ mStatus = status || mBluetoothLeBroadcastReceiveState != null;
+
+ refresh();
+ }
+
+ /**
+ * Updates the title and status from BluetoothLeBroadcastReceiveState.
+ */
+ public void updateReceiveStateAndRefreshUi(BluetoothLeBroadcastReceiveState receiveState) {
+ mBluetoothLeBroadcastReceiveState = receiveState;
+ mTitle = getProgramInfo();
+ mStatus = true;
refresh();
}
@@ -124,7 +136,17 @@
updateStatusButton();
}
- private String getBroadcastMetadataProgramInfo() {
+ private String getProgramInfo() {
+ if (mBluetoothLeBroadcastReceiveState != null) {
+ List<BluetoothLeAudioContentMetadata> bluetoothLeAudioContentMetadata =
+ mBluetoothLeBroadcastReceiveState.getSubgroupMetadata();
+ if (!bluetoothLeAudioContentMetadata.isEmpty()) {
+ return bluetoothLeAudioContentMetadata.stream()
+ .map(i -> i.getProgramInfo())
+ .findFirst().orElse(
+ getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO));
+ }
+ }
if (mBluetoothLeBroadcastMetadata == null) {
return getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO);
}
@@ -138,4 +160,24 @@
.filter(i -> !TextUtils.isEmpty(i))
.findFirst().orElse(getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO));
}
+
+ /**
+ * Whether the broadcast source is encrypted or not.
+ * @return If true, the broadcast source needs the broadcast code. If false, the broadcast
+ * source does not need the broadcast code.
+ */
+ public boolean isEncrypted() {
+ return mIsEncrypted;
+ }
+
+ /**
+ * Clear the BluetoothLeBroadcastReceiveState and reset the state when the user clicks the
+ * "leave broadcast" button.
+ */
+ public void clearReceiveState() {
+ mBluetoothLeBroadcastReceiveState = null;
+ mTitle = getProgramInfo();
+ mStatus = false;
+ refresh();
+ }
}
diff --git a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
index 07a3156..13388b3 100644
--- a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
@@ -86,9 +86,7 @@
@Override
public void onSearchStarted(int reason) {
Log.d(TAG, "onSearchStarted: " + reason);
-
- getActivity().runOnUiThread(
- () -> cacheRemoveAllPrefs(mBroadcastSourceListCategory));
+ getActivity().runOnUiThread(() -> handleSearchStarted());
}
@Override
@@ -109,7 +107,8 @@
@Override
public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {
Log.d(TAG, "onSourceFound:");
- getActivity().runOnUiThread(() -> updateListCategory(source, false));
+ getActivity().runOnUiThread(
+ () -> updateListCategoryFromBroadcastMetadata(source, false));
}
@Override
@@ -119,7 +118,7 @@
Log.w(TAG, "onSourceAdded: mSelectedPreference == null!");
return;
}
- getActivity().runOnUiThread(() -> updateListCategory(
+ getActivity().runOnUiThread(() -> updateListCategoryFromBroadcastMetadata(
mSelectedPreference.getBluetoothLeBroadcastMetadata(), true));
}
@@ -144,6 +143,7 @@
public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId,
int reason) {
Log.d(TAG, "onSourceRemoved:");
+ getActivity().runOnUiThread(() -> handleSourceRemoved());
}
@Override
@@ -215,6 +215,8 @@
//check assistant status. Start searching...
if (mLeBroadcastAssistant != null && !mLeBroadcastAssistant.isSearchInProgress()) {
mLeBroadcastAssistant.startSearchingForSources(getScanFilter());
+ } else {
+ addConnectedSourcePreference();
}
}
@@ -310,11 +312,13 @@
return Collections.emptyList();
}
- private void updateListCategory(BluetoothLeBroadcastMetadata source, boolean isConnected) {
+ private void updateListCategoryFromBroadcastMetadata(BluetoothLeBroadcastMetadata source,
+ boolean isConnected) {
BluetoothBroadcastSourcePreference item = mBroadcastSourceListCategory.findPreference(
Integer.toString(source.getBroadcastId()));
if (item == null) {
- item = createBluetoothBroadcastSourcePreference(source);
+ item = createBluetoothBroadcastSourcePreference();
+ item.setKey(Integer.toString(source.getBroadcastId()));
mBroadcastSourceListCategory.addPreference(item);
}
item.updateMetadataAndRefreshUi(source, isConnected);
@@ -326,13 +330,36 @@
}
}
- private BluetoothBroadcastSourcePreference createBluetoothBroadcastSourcePreference(
- BluetoothLeBroadcastMetadata source) {
+ private void updateListCategoryFromBroadcastReceiveState(
+ BluetoothLeBroadcastReceiveState receiveState) {
+ BluetoothBroadcastSourcePreference item = mBroadcastSourceListCategory.findPreference(
+ Integer.toString(receiveState.getBroadcastId()));
+ if (item == null) {
+ item = createBluetoothBroadcastSourcePreference();
+ item.setKey(Integer.toString(receiveState.getBroadcastId()));
+ mBroadcastSourceListCategory.addPreference(item);
+ }
+ item.updateReceiveStateAndRefreshUi(receiveState);
+ item.setOrder(0);
+
+ setSourceId(receiveState.getSourceId());
+ mSelectedPreference = item;
+
+ //refresh the header
+ if (mBluetoothFindBroadcastsHeaderController != null) {
+ mBluetoothFindBroadcastsHeaderController.refreshUi();
+ }
+ }
+
+ private BluetoothBroadcastSourcePreference createBluetoothBroadcastSourcePreference() {
BluetoothBroadcastSourcePreference pref = new BluetoothBroadcastSourcePreference(
- getContext(), source);
- pref.setKey(Integer.toString(source.getBroadcastId()));
+ getContext());
pref.setOnPreferenceClickListener(preference -> {
- if (source.isEncrypted()) {
+ if (pref.getBluetoothLeBroadcastMetadata() == null) {
+ Log.d(TAG, "BluetoothLeBroadcastMetadata is null, do nothing.");
+ return false;
+ }
+ if (pref.isEncrypted()) {
launchBroadcastCodeDialog(pref);
} else {
addSource(pref);
@@ -383,6 +410,10 @@
.setPositiveButton(R.string.bluetooth_connect_access_dialog_positive,
(d, w) -> {
Log.d(TAG, "setPositiveButton: clicked");
+ if (pref.getBluetoothLeBroadcastMetadata() == null) {
+ Log.d(TAG, "BluetoothLeBroadcastMetadata is null, do nothing.");
+ return;
+ }
addBroadcastCodeIntoPreference(pref, editText.getText().toString());
addSource(pref);
})
@@ -392,6 +423,30 @@
alertDialog.show();
}
+ private void handleSearchStarted() {
+ cacheRemoveAllPrefs(mBroadcastSourceListCategory);
+ addConnectedSourcePreference();
+ }
+
+ private void handleSourceRemoved() {
+ if (mSelectedPreference != null) {
+ if (mSelectedPreference.getBluetoothLeBroadcastMetadata() == null) {
+ mBroadcastSourceListCategory.removePreference(mSelectedPreference);
+ } else {
+ mSelectedPreference.clearReceiveState();
+ }
+ }
+ mSelectedPreference = null;
+ }
+
+ private void addConnectedSourcePreference() {
+ List<BluetoothLeBroadcastReceiveState> receiveStateList =
+ mLeBroadcastAssistant.getAllSources(mCachedDevice.getDevice());
+ if (!receiveStateList.isEmpty()) {
+ updateListCategoryFromBroadcastReceiveState(receiveStateList.get(0));
+ }
+ }
+
public int getSourceId() {
return mSourceId;
}
diff --git a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java
index 1527f21..6aaa1b8 100644
--- a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java
+++ b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java
@@ -33,7 +33,6 @@
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.qrcode.QrCodeScanModeActivity;
import com.android.settingslib.widget.LayoutPreference;
/**
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeActivity.java b/src/com/android/settings/bluetooth/QrCodeScanModeActivity.java
new file mode 100644
index 0000000..5c5b61f
--- /dev/null
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeActivity.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright (C) 2022 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.bluetooth;
+
+import static com.android.settingslib.bluetooth.BluetoothBroadcastUtils.EXTRA_BLUETOOTH_DEVICE_SINK;
+import static com.android.settingslib.bluetooth.BluetoothBroadcastUtils.EXTRA_BLUETOOTH_SINK_IS_GROUP;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.fragment.app.FragmentTransaction;
+
+import com.android.settingslib.R;
+import com.android.settingslib.bluetooth.BluetoothBroadcastUtils;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+
+//TODO (b/232365943): Add test case for tthe QrCode UI.
+public class QrCodeScanModeActivity extends QrCodeScanModeBaseActivity {
+ private static final boolean DEBUG = BluetoothUtils.D;
+ private static final String TAG = "QrCodeScanModeActivity";
+
+ private boolean mIsGroupOp;
+ private BluetoothDevice mSink;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void handleIntent(Intent intent) {
+ String action = intent != null ? intent.getAction() : null;
+ if (DEBUG) {
+ Log.d(TAG, "handleIntent(), action = " + action);
+ }
+
+ if (action == null) {
+ finish();
+ return;
+ }
+
+ switch (action) {
+ case BluetoothBroadcastUtils.ACTION_BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER:
+ showQrCodeScannerFragment(intent);
+ break;
+ default:
+ if (DEBUG) {
+ Log.e(TAG, "Launch with an invalid action");
+ }
+ finish();
+ }
+ }
+
+ protected void showQrCodeScannerFragment(Intent intent) {
+ if (intent == null) {
+ if (DEBUG) {
+ Log.d(TAG, "intent is null, can not get bluetooth information from intent.");
+ }
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "showQrCodeScannerFragment");
+ }
+
+ mSink = intent.getParcelableExtra(EXTRA_BLUETOOTH_DEVICE_SINK);
+ mIsGroupOp = intent.getBooleanExtra(EXTRA_BLUETOOTH_SINK_IS_GROUP, false);
+ if (DEBUG) {
+ Log.d(TAG, "get extra from intent");
+ }
+
+ QrCodeScanModeFragment fragment =
+ (QrCodeScanModeFragment) mFragmentManager.findFragmentByTag(
+ BluetoothBroadcastUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
+
+ if (fragment == null) {
+ fragment = new QrCodeScanModeFragment(mIsGroupOp, mSink);
+ } else {
+ if (fragment.isVisible()) {
+ return;
+ }
+
+ // When the fragment in back stack but not on top of the stack, we can simply pop
+ // stack because current fragment transactions are arranged in an order
+ mFragmentManager.popBackStackImmediate();
+ return;
+ }
+ final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
+
+ fragmentTransaction.replace(R.id.fragment_container, fragment,
+ BluetoothBroadcastUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
+ fragmentTransaction.commit();
+ }
+}
+
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeBaseActivity.java b/src/com/android/settings/bluetooth/QrCodeScanModeBaseActivity.java
new file mode 100644
index 0000000..af8a6e9
--- /dev/null
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeBaseActivity.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2022 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.bluetooth;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.fragment.app.FragmentManager;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.ObservableActivity;
+
+public abstract class QrCodeScanModeBaseActivity extends ObservableActivity {
+
+ protected FragmentManager mFragmentManager;
+
+ protected abstract void handleIntent(Intent intent);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setTheme(R.style.SudThemeGlifV3_DayNight);
+
+ setContentView(R.layout.qrcode_scan_mode_activity);
+ mFragmentManager = getSupportFragmentManager();
+
+ if (savedInstanceState == null) {
+ handleIntent(getIntent());
+ }
+ }
+}
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeController.java b/src/com/android/settings/bluetooth/QrCodeScanModeController.java
new file mode 100644
index 0000000..4504b4b
--- /dev/null
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeController.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2022 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.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastMetadata;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+
+public class QrCodeScanModeController {
+
+ private static final boolean DEBUG = BluetoothUtils.D;
+ private static final String TAG = "QrCodeScanModeController";
+
+ private LocalBluetoothLeBroadcastMetadata mLocalBroadcastMetadata;
+ private LocalBluetoothLeBroadcastAssistant mLocalBroadcastAssistant;
+ private LocalBluetoothManager mLocalBluetoothManager;
+ private LocalBluetoothProfileManager mProfileManager;
+
+ public QrCodeScanModeController(Context context) {
+ if (DEBUG) {
+ Log.d(TAG, "QrCodeScanModeController constructor.");
+ }
+ mLocalBluetoothManager = Utils.getLocalBtManager(context);
+ mProfileManager = mLocalBluetoothManager.getProfileManager();
+ mLocalBroadcastMetadata = new LocalBluetoothLeBroadcastMetadata();
+ CachedBluetoothDeviceManager cachedDeviceManager = new CachedBluetoothDeviceManager(context,
+ mLocalBluetoothManager);
+ mLocalBroadcastAssistant = new LocalBluetoothLeBroadcastAssistant(context,
+ cachedDeviceManager, mProfileManager);
+ }
+
+ private BluetoothLeBroadcastMetadata convertToBroadcastMetadata(String qrCodeString) {
+ return mLocalBroadcastMetadata.convertToBroadcastMetadata(qrCodeString);
+ }
+
+ public void addSource(BluetoothDevice sink, String sourceMetadata,
+ boolean isGroupOp) {
+ mLocalBroadcastAssistant.addSource(sink,
+ convertToBroadcastMetadata(sourceMetadata), isGroupOp);
+ }
+}
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java
new file mode 100644
index 0000000..dcf89ca
--- /dev/null
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java
@@ -0,0 +1,243 @@
+/**
+ * Copyright (C) 2022 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.bluetooth;
+
+import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.util.Size;
+import android.view.LayoutInflater;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.TextView;
+
+import com.android.settings.core.InstrumentedFragment;
+import com.android.settingslib.R;
+import com.android.settingslib.bluetooth.BluetoothBroadcastUtils;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.core.lifecycle.ObservableFragment;
+import com.android.settingslib.qrcode.QrCamera;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+
+public class QrCodeScanModeFragment extends InstrumentedFragment implements
+ TextureView.SurfaceTextureListener,
+ QrCamera.ScannerCallback {
+ private static final boolean DEBUG = BluetoothUtils.D;
+ private static final String TAG = "QrCodeScanModeFragment";
+
+ /** Message sent to hide error message */
+ private static final int MESSAGE_HIDE_ERROR_MESSAGE = 1;
+ /** Message sent to show error message */
+ private static final int MESSAGE_SHOW_ERROR_MESSAGE = 2;
+ /** Message sent to broadcast QR code */
+ private static final int MESSAGE_SCAN_BROADCAST_SUCCESS = 3;
+
+ private static final long SHOW_ERROR_MESSAGE_INTERVAL = 10000;
+ private static final long SHOW_SUCCESS_SQUARE_INTERVAL = 1000;
+
+ private boolean mIsGroupOp;
+ private int mCornerRadius;
+ private BluetoothDevice mSink;
+ private String mBroadcastMetadata;
+ private Context mContext;
+ private QrCamera mCamera;
+ private QrCodeScanModeController mController;
+ private TextureView mTextureView;
+ private TextView mSummary;
+ private TextView mErrorMessage;
+
+ public QrCodeScanModeFragment(boolean isGroupOp, BluetoothDevice sink) {
+ mIsGroupOp = isGroupOp;
+ mSink = sink;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mContext = getContext();
+ mController = new QrCodeScanModeController(mContext);
+ }
+
+ @Override
+ public final View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.qrcode_scanner_fragment, container,
+ /* attachToRoot */ false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ mTextureView = view.findViewById(R.id.preview_view);
+ mCornerRadius = mContext.getResources().getDimensionPixelSize(
+ R.dimen.qrcode_preview_radius);
+ mTextureView.setSurfaceTextureListener(this);
+ mTextureView.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0,0, view.getWidth(), view.getHeight(), mCornerRadius);
+ }
+ });
+ mTextureView.setClipToOutline(true);
+ mErrorMessage = view.findViewById(R.id.error_message);
+ }
+
+ private void initCamera(SurfaceTexture surface) {
+ // Check if the camera has already created.
+ if (mCamera == null) {
+ mCamera = new QrCamera(mContext, this);
+ mCamera.start(surface);
+ }
+ }
+
+ private void destroyCamera() {
+ if (mCamera != null) {
+ mCamera.stop();
+ mCamera = null;
+ }
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
+ initCamera(surface);
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width,
+ int height) {
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
+ destroyCamera();
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
+ }
+
+ @Override
+ public void handleSuccessfulResult(String qrCode) {
+ if (DEBUG) {
+ Log.d(TAG, "handleSuccessfulResult(), get the qr code string.");
+ }
+ mBroadcastMetadata = qrCode;
+ handleBtLeAudioScanner();
+ }
+
+ @Override
+ public void handleCameraFailure() {
+ destroyCamera();
+ }
+
+ @Override
+ public Size getViewSize() {
+ return new Size(mTextureView.getWidth(), mTextureView.getHeight());
+ }
+
+ @Override
+ public Rect getFramePosition(Size previewSize, int cameraOrientation) {
+ return new Rect(0, 0, previewSize.getHeight(), previewSize.getHeight());
+ }
+
+ @Override
+ public void setTransform(Matrix transform) {
+ mTextureView.setTransform(transform);
+ }
+
+ @Override
+ public boolean isValid(String qrCode) {
+ if (qrCode.startsWith(BluetoothBroadcastUtils.SCHEME_BT_BROADCAST_METADATA)) {
+ return true;
+ } else {
+ showErrorMessage(R.string.bt_le_audio_qr_code_is_not_valid_format);
+ return false;
+ }
+ }
+
+ protected boolean isDecodeTaskAlive() {
+ return mCamera != null && mCamera.isDecodeTaskAlive();
+ }
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_HIDE_ERROR_MESSAGE:
+ mErrorMessage.setVisibility(View.INVISIBLE);
+ break;
+
+ case MESSAGE_SHOW_ERROR_MESSAGE:
+ final String errorMessage = (String) msg.obj;
+
+ mErrorMessage.setVisibility(View.VISIBLE);
+ mErrorMessage.setText(errorMessage);
+ mErrorMessage.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+
+ // Cancel any pending messages to hide error view and requeue the message so
+ // user has time to see error
+ removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
+ sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,
+ SHOW_ERROR_MESSAGE_INTERVAL);
+ break;
+
+ case MESSAGE_SCAN_BROADCAST_SUCCESS:
+ mController.addSource(mSink, mBroadcastMetadata, mIsGroupOp);
+ updateSummary();
+ mSummary.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ break;
+ default:
+ }
+ }
+ };
+
+ private void showErrorMessage(@StringRes int messageResId) {
+ final Message message = mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE,
+ getString(messageResId));
+ message.sendToTarget();
+ }
+
+ private void handleBtLeAudioScanner() {
+ Message message = mHandler.obtainMessage(MESSAGE_SCAN_BROADCAST_SUCCESS);
+ mHandler.sendMessageDelayed(message, SHOW_SUCCESS_SQUARE_INTERVAL);
+ }
+
+ private void updateSummary() {
+ mSummary.setText(getString(R.string.bt_le_audio_scan_qr_code_scanner,
+ null /* broadcast_name*/));;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.LE_AUDIO_BROADCAST_SCAN_QR_CODE;
+ }
+}
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java
index 57988c5..1473dd1 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java
@@ -84,10 +84,19 @@
}
final StorageEntry changedStorageEntry = new StorageEntry(getContext(), volumeInfo);
- switch (volumeInfo.getState()) {
+ final int volumeState = volumeInfo.getState();
+ switch (volumeState) {
+ case VolumeInfo.STATE_REMOVED:
+ case VolumeInfo.STATE_BAD_REMOVAL:
+ // Remove removed storage from list and don't show it on spinner.
+ if (!mStorageEntries.remove(changedStorageEntry)) {
+ break;
+ }
case VolumeInfo.STATE_MOUNTED:
case VolumeInfo.STATE_MOUNTED_READ_ONLY:
case VolumeInfo.STATE_UNMOUNTABLE:
+ case VolumeInfo.STATE_UNMOUNTED:
+ case VolumeInfo.STATE_EJECTING:
// Add mounted or unmountable storage in the list and show it on spinner.
// Unmountable storages are the storages which has a problem format and android
// is not able to mount it automatically.
@@ -95,25 +104,15 @@
mStorageEntries.removeIf(storageEntry -> {
return storageEntry.equals(changedStorageEntry);
});
- mStorageEntries.add(changedStorageEntry);
+ if (volumeState != VolumeInfo.STATE_REMOVED
+ && volumeState != VolumeInfo.STATE_BAD_REMOVAL) {
+ mStorageEntries.add(changedStorageEntry);
+ }
if (changedStorageEntry.equals(mSelectedStorageEntry)) {
mSelectedStorageEntry = changedStorageEntry;
}
refreshUi();
break;
- case VolumeInfo.STATE_REMOVED:
- case VolumeInfo.STATE_UNMOUNTED:
- case VolumeInfo.STATE_BAD_REMOVAL:
- case VolumeInfo.STATE_EJECTING:
- // Remove removed storage from list and don't show it on spinner.
- if (mStorageEntries.remove(changedStorageEntry)) {
- if (changedStorageEntry.equals(mSelectedStorageEntry)) {
- mSelectedStorageEntry =
- StorageEntry.getDefaultInternalStorageEntry(getContext());
- }
- refreshUi();
- }
- break;
default:
// Do nothing.
}
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index 77d4072..a4809c9 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -115,10 +115,19 @@
}
final StorageEntry changedStorageEntry = new StorageEntry(getContext(), volumeInfo);
- switch (volumeInfo.getState()) {
+ final int volumeState = volumeInfo.getState();
+ switch (volumeState) {
+ case VolumeInfo.STATE_REMOVED:
+ case VolumeInfo.STATE_BAD_REMOVAL:
+ // Remove removed storage from list and don't show it on spinner.
+ if (!mStorageEntries.remove(changedStorageEntry)) {
+ break;
+ }
case VolumeInfo.STATE_MOUNTED:
case VolumeInfo.STATE_MOUNTED_READ_ONLY:
case VolumeInfo.STATE_UNMOUNTABLE:
+ case VolumeInfo.STATE_UNMOUNTED:
+ case VolumeInfo.STATE_EJECTING:
// Add mounted or unmountable storage in the list and show it on spinner.
// Unmountable storages are the storages which has a problem format and android
// is not able to mount it automatically.
@@ -126,25 +135,15 @@
mStorageEntries.removeIf(storageEntry -> {
return storageEntry.equals(changedStorageEntry);
});
- mStorageEntries.add(changedStorageEntry);
+ if (volumeState != VolumeInfo.STATE_REMOVED
+ && volumeState != VolumeInfo.STATE_BAD_REMOVAL) {
+ mStorageEntries.add(changedStorageEntry);
+ }
if (changedStorageEntry.equals(mSelectedStorageEntry)) {
mSelectedStorageEntry = changedStorageEntry;
}
refreshUi();
break;
- case VolumeInfo.STATE_REMOVED:
- case VolumeInfo.STATE_UNMOUNTED:
- case VolumeInfo.STATE_BAD_REMOVAL:
- case VolumeInfo.STATE_EJECTING:
- // Remove removed storage from list and don't show it on spinner.
- if (mStorageEntries.remove(changedStorageEntry)) {
- if (changedStorageEntry.equals(mSelectedStorageEntry)) {
- mSelectedStorageEntry =
- StorageEntry.getDefaultInternalStorageEntry(getContext());
- }
- refreshUi();
- }
- break;
default:
// Do nothing.
}
diff --git a/src/com/android/settings/deviceinfo/VolumeOptionMenuController.java b/src/com/android/settings/deviceinfo/VolumeOptionMenuController.java
index 42a3a16..4b87e42 100644
--- a/src/com/android/settings/deviceinfo/VolumeOptionMenuController.java
+++ b/src/com/android/settings/deviceinfo/VolumeOptionMenuController.java
@@ -26,6 +26,7 @@
import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -53,6 +54,8 @@
public class VolumeOptionMenuController implements LifecycleObserver, OnCreateOptionsMenu,
OnPrepareOptionsMenu, OnOptionsItemSelected {
+ private static final String TAG = "VolumeOptionMenuController";
+
@VisibleForTesting
MenuItem mRename;
@VisibleForTesting
@@ -103,6 +106,17 @@
mFree = menu.findItem(R.id.storage_free);
mForget = menu.findItem(R.id.storage_forget);
+ updateOptionsMenu();
+ }
+
+ private void updateOptionsMenu() {
+ if (mRename == null || mMount == null || mUnmount == null || mFormat == null
+ || mFormatAsPortable == null || mFormatAsInternal == null || mMigrate == null
+ || mFree == null || mForget == null) {
+ Log.d(TAG, "Menu items are not available");
+ return;
+ }
+
mRename.setVisible(false);
mMount.setVisible(false);
mUnmount.setVisible(false);
@@ -252,5 +266,7 @@
public void setSelectedStorageEntry(StorageEntry storageEntry) {
mStorageEntry = storageEntry;
+
+ updateOptionsMenu();
}
}
diff --git a/src/com/android/settings/localepicker/AppLocalePickerActivity.java b/src/com/android/settings/localepicker/AppLocalePickerActivity.java
index 791a4e8..377a076 100644
--- a/src/com/android/settings/localepicker/AppLocalePickerActivity.java
+++ b/src/com/android/settings/localepicker/AppLocalePickerActivity.java
@@ -118,6 +118,7 @@
/** Sets the app's locale to the supplied language tag */
private void setAppDefaultLocale(String languageTag) {
+ Log.d(TAG, "setAppDefaultLocale: " + languageTag);
LocaleManager localeManager = mContextAsUser.getSystemService(LocaleManager.class);
if (localeManager == null) {
Log.w(TAG, "LocaleManager is null, cannot set default app locale");
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index de4d127..ec17dd3 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -67,7 +67,6 @@
import com.android.settings.datausage.DataUsageUtils;
import com.android.settings.location.WifiScanningFragment;
import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.utils.AnnotationSpan;
import com.android.settings.wifi.AddNetworkFragment;
import com.android.settings.wifi.AddWifiNetworkPreference;
import com.android.settings.wifi.ConfigureWifiEntryFragment;
@@ -829,12 +828,10 @@
return;
}
if (TextUtils.isEmpty(mWifiStatusMessagePreference.getTitle())) {
- AnnotationSpan.LinkInfo info = new AnnotationSpan.LinkInfo(
- AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION,
- v -> launchWifiScanningFragment());
- CharSequence text = AnnotationSpan.linkify(
- context.getText(R.string.wifi_scan_notify_message), info);
- mWifiStatusMessagePreference.setTitle(text);
+ mWifiStatusMessagePreference.setTitle(R.string.wifi_scan_notify_message);
+ mWifiStatusMessagePreference.setLearnMoreText(
+ context.getString(R.string.wifi_scan_change));
+ mWifiStatusMessagePreference.setLearnMoreAction(v -> launchWifiScanningFragment());
}
mWifiStatusMessagePreference.setVisible(true);
}
diff --git a/src/com/android/settings/privacy/PrivacyDashboardFragment.java b/src/com/android/settings/privacy/PrivacyDashboardFragment.java
index df59bd5..75ed225 100644
--- a/src/com/android/settings/privacy/PrivacyDashboardFragment.java
+++ b/src/com/android/settings/privacy/PrivacyDashboardFragment.java
@@ -25,6 +25,7 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
+import android.provider.SearchIndexableResource;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
@@ -36,6 +37,7 @@
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
@SearchIndexable
@@ -72,12 +74,6 @@
replaceEnterpriseStringSummary("work_policy_info",
WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY,
R.string.work_policy_privacy_settings_summary);
-
- }
-
- @Override
- protected int getPreferenceScreenResId() {
- return R.xml.privacy_dashboard_settings;
}
@Override
@@ -90,6 +86,19 @@
return buildPreferenceControllers(context, getSettingsLifecycle());
}
+ @Override
+ protected int getPreferenceScreenResId() {
+ return getPreferenceScreenResId(getContext());
+ }
+
+ private static int getPreferenceScreenResId(Context context) {
+ if (SafetyCenterManagerWrapper.get().isEnabled(context)) {
+ return R.xml.privacy_advanced_settings;
+ } else {
+ return R.xml.privacy_dashboard_settings;
+ }
+ }
+
private static List<AbstractPreferenceController> buildPreferenceControllers(
Context context, Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
@@ -108,17 +117,19 @@
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.privacy_dashboard_settings) {
+ new BaseSearchIndexProvider() {
+ @Override
+ public List<SearchIndexableResource> getXmlResourcesToIndex(
+ Context context, boolean enabled) {
+ final SearchIndexableResource sir = new SearchIndexableResource(context);
+ sir.xmlResId = getPreferenceScreenResId(context);
+ return Arrays.asList(sir);
+ }
@Override
public List<AbstractPreferenceController> createPreferenceControllers(
Context context) {
return buildPreferenceControllers(context, null);
}
-
- @Override
- protected boolean isPageSearchEnabled(Context context) {
- return !SafetyCenterManagerWrapper.get().isEnabled(context);
- }
};
}
diff --git a/src/com/android/settings/widget/SettingsMainSwitchPreference.java b/src/com/android/settings/widget/SettingsMainSwitchPreference.java
index b7c6901..9264911 100644
--- a/src/com/android/settings/widget/SettingsMainSwitchPreference.java
+++ b/src/com/android/settings/widget/SettingsMainSwitchPreference.java
@@ -22,6 +22,7 @@
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.view.ViewGroup;
import android.widget.Switch;
import androidx.core.content.res.TypedArrayUtils;
@@ -33,6 +34,8 @@
import com.android.settingslib.RestrictedPreferenceHelper;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
+import com.google.android.setupdesign.util.LayoutStyler;
+
import java.util.ArrayList;
import java.util.List;
@@ -48,6 +51,7 @@
new ArrayList<>();
private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>();
+ private boolean mApplyPartnerCustomizationPaddingStyle;
private SettingsMainSwitchBar mMainSwitchBar;
private CharSequence mTitle;
private EnforcedAdmin mEnforcedAdmin;
@@ -95,6 +99,12 @@
} else {
mMainSwitchBar.hide();
}
+
+ if (mApplyPartnerCustomizationPaddingStyle) {
+ // TODO(b/232494666): Replace all margins of the root view with the padding
+ final ViewGroup parentView = (ViewGroup) mMainSwitchBar.getParent();
+ LayoutStyler.applyPartnerCustomizationLayoutPaddingStyle(parentView);
+ }
}
private void init(Context context, AttributeSet attrs) {
@@ -241,6 +251,14 @@
}
}
+ /**
+ * Apples the padding style of the partner's customization. It's used in the SetupWizard.
+ */
+ public void applyPartnerCustomizationPaddingStyle() {
+ mApplyPartnerCustomizationPaddingStyle = true;
+ notifyChanged();
+ }
+
private void initMainSwitchBar() {
if (mMainSwitchBar != null) {
mMainSwitchBar.setTitle(mTitle);
diff --git a/src/com/android/settings/wifi/WifiScanModeActivity.java b/src/com/android/settings/wifi/WifiScanModeActivity.java
index 9d50281..d372135 100644
--- a/src/com/android/settings/wifi/WifiScanModeActivity.java
+++ b/src/com/android/settings/wifi/WifiScanModeActivity.java
@@ -20,26 +20,30 @@
import android.app.settings.SettingsEnums;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.WindowManager;
+import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.wifi.WifiPermissionChecker;
/**
* This activity requests users permission to allow scanning even when Wi-Fi is turned off
*/
public class WifiScanModeActivity extends FragmentActivity {
private DialogFragment mDialog;
- private String mApp;
+ @VisibleForTesting
+ String mApp;
+ @VisibleForTesting
+ WifiPermissionChecker mWifiPermissionChecker;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -50,13 +54,7 @@
if (savedInstanceState == null) {
if (intent != null && WifiManager.ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE
.equals(intent.getAction())) {
- ApplicationInfo ai;
- mApp = getCallingPackage();
- try {
- PackageManager pm = getPackageManager();
- ai = pm.getApplicationInfo(mApp, 0);
- mApp = (String)pm.getApplicationLabel(ai);
- } catch (PackageManager.NameNotFoundException e) { }
+ refreshAppLabel();
} else {
finish();
return;
@@ -67,6 +65,19 @@
createDialog();
}
+ @VisibleForTesting
+ void refreshAppLabel() {
+ if (mWifiPermissionChecker == null) {
+ mWifiPermissionChecker = new WifiPermissionChecker(this);
+ }
+ String packageName = mWifiPermissionChecker.getLaunchedPackage();
+ if (TextUtils.isEmpty(packageName)) {
+ mApp = null;
+ return;
+ }
+ mApp = Utils.getApplicationLabel(getApplicationContext(), packageName).toString();
+ }
+
private void createDialog() {
if (mDialog == null) {
mDialog = AlertDialogFragment.newInstance(mApp);
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettings.java b/src/com/android/settings/wifi/calling/WifiCallingSettings.java
index 25508f7..b4951bf 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettings.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettings.java
@@ -30,13 +30,12 @@
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import com.android.internal.util.CollectionUtils;
import com.android.settings.R;
-import com.android.settings.core.InstrumentedFragment;
+import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.network.ActiveSubscriptionsListener;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.ims.WifiCallingQueryImsState;
@@ -54,7 +53,8 @@
* "Wi-Fi Calling settings" screen. This is the container fragment which holds
* {@link WifiCallingSettingsForSub} fragments.
*/
-public class WifiCallingSettings extends InstrumentedFragment implements HelpResourceProvider {
+public class WifiCallingSettings extends SettingsPreferenceFragment
+ implements HelpResourceProvider {
private static final String TAG = "WifiCallingSettings";
private int mConstructionSubId;
private List<SubscriptionInfo> mSil;
@@ -317,17 +317,7 @@
}
// close this fragment
- finish();
- }
-
- protected void finish() {
- FragmentActivity activity = getActivity();
- if (activity == null) return;
- if (getFragmentManager().getBackStackEntryCount() > 0) {
- getFragmentManager().popBackStack();
- } else {
- activity.finish();
- }
+ finishFragment();
}
protected int [] subscriptionIdList(List<SubscriptionInfo> subInfoList) {
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
index 749af3e..492a228 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
@@ -27,7 +27,6 @@
import android.os.Bundle;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -40,7 +39,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.Switch;
-import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
@@ -56,8 +54,7 @@
import com.android.settings.Utils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.network.ims.WifiCallingQueryImsState;
-import com.android.settings.widget.SettingsMainSwitchBar;
-import com.android.settings.wifi.calling.LinkifyDescriptionPreference;
+import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
import java.util.List;
@@ -72,6 +69,7 @@
private static final String TAG = "WifiCallingForSub";
//String keys for preference lookup
+ private static final String SWITCH_BAR = "wifi_calling_switch_bar";
private static final String BUTTON_WFC_MODE = "wifi_calling_mode";
private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key";
@@ -91,7 +89,7 @@
public static final int LAUCH_APP_UPDATE = 1;
//UI objects
- private SettingsMainSwitchBar mSwitchBar;
+ private SettingsMainSwitchPreference mSwitchBar;
private ListWithEntrySummaryPreference mButtonWfcMode;
private ListWithEntrySummaryPreference mButtonWfcRoamingMode;
private Preference mUpdateAddress;
@@ -119,41 +117,57 @@
@Override
public void onCallStateChanged(int state) {
final SettingsActivity activity = (SettingsActivity) getActivity();
- final boolean isNonTtyOrTtyOnVolteEnabled =
- queryImsState(WifiCallingSettingsForSub.this.mSubId).isAllowUserControl();
- final boolean isWfcEnabled = mSwitchBar.isChecked()
- && isNonTtyOrTtyOnVolteEnabled;
- boolean isCallStateIdle = getTelephonyManagerForSub(
- WifiCallingSettingsForSub.this.mSubId).getCallState()
- == TelephonyManager.CALL_STATE_IDLE;
- mSwitchBar.setEnabled(isCallStateIdle
- && isNonTtyOrTtyOnVolteEnabled);
+
+ boolean isWfcEnabled = false;
+ boolean isCallStateIdle = false;
+
+ final SettingsMainSwitchPreference prefSwitch = (SettingsMainSwitchPreference)
+ getPreferenceScreen().findPreference(SWITCH_BAR);
+ if (prefSwitch != null) {
+ isWfcEnabled = prefSwitch.isChecked();
+ isCallStateIdle = getTelephonyManagerForSub(
+ WifiCallingSettingsForSub.this.mSubId).getCallState()
+ == TelephonyManager.CALL_STATE_IDLE;
+
+ boolean isNonTtyOrTtyOnVolteEnabled = true;
+ if (isWfcEnabled || isCallStateIdle) {
+ isNonTtyOrTtyOnVolteEnabled =
+ queryImsState(WifiCallingSettingsForSub.this.mSubId)
+ .isAllowUserControl();
+ }
+
+ isWfcEnabled = isWfcEnabled && isNonTtyOrTtyOnVolteEnabled;
+ prefSwitch.setEnabled(isCallStateIdle && isNonTtyOrTtyOnVolteEnabled);
+ }
boolean isWfcModeEditable = true;
boolean isWfcRoamingModeEditable = false;
- final CarrierConfigManager configManager = (CarrierConfigManager)
- activity.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null) {
- PersistableBundle b =
- configManager.getConfigForSubId(WifiCallingSettingsForSub.this.mSubId);
- if (b != null) {
- isWfcModeEditable = b.getBoolean(
- CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
- isWfcRoamingModeEditable = b.getBoolean(
- CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
+ if (isWfcEnabled && isCallStateIdle) {
+ final CarrierConfigManager configManager = (CarrierConfigManager)
+ activity.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ PersistableBundle b = configManager.getConfigForSubId(
+ WifiCallingSettingsForSub.this.mSubId);
+ if (b != null) {
+ isWfcModeEditable = b.getBoolean(
+ CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
+ isWfcRoamingModeEditable = b.getBoolean(
+ CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
+ }
}
+ } else {
+ isWfcModeEditable = false;
+ isWfcRoamingModeEditable = false;
}
final Preference pref = getPreferenceScreen().findPreference(BUTTON_WFC_MODE);
if (pref != null) {
- pref.setEnabled(isWfcEnabled && isWfcModeEditable
- && isCallStateIdle);
+ pref.setEnabled(isWfcModeEditable);
}
final Preference pref_roam =
getPreferenceScreen().findPreference(BUTTON_WFC_ROAMING_MODE);
if (pref_roam != null) {
- pref_roam.setEnabled(isWfcEnabled && isWfcRoamingModeEditable
- && isCallStateIdle);
+ pref_roam.setEnabled(isWfcRoamingModeEditable);
}
}
}
@@ -184,20 +198,6 @@
}
};
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- mSwitchBar = getView().findViewById(R.id.switch_bar);
- mSwitchBar.show();
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- mSwitchBar.hide();
- }
-
@VisibleForTesting
void showAlert(Intent intent) {
final Context context = getActivity();
@@ -292,6 +292,8 @@
mProvisioningManager = getImsProvisioningManager();
mImsMmTelManager = getImsMmTelManager();
+ mSwitchBar = (SettingsMainSwitchPreference) findPreference(SWITCH_BAR);
+
mButtonWfcMode = findPreference(BUTTON_WFC_MODE);
mButtonWfcMode.setOnPreferenceChangeListener(this);
diff --git a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
index 5d28bcc..10ee241 100644
--- a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
+++ b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
@@ -522,7 +522,7 @@
final LayoutInflater layoutInflater = LayoutInflater.from(getPrefContext());
final View root = layoutInflater.inflate(R.layout.dialog_edittext, null /* root */);
mDeviceNameText = root.findViewById(R.id.edittext);
- mDeviceNameText.setFilters(new InputFilter[] {new InputFilter.LengthFilter(30)});
+ mDeviceNameText.setFilters(new InputFilter[] {new InputFilter.LengthFilter(22)});
if (mSavedDeviceName != null) {
mDeviceNameText.setText(mSavedDeviceName);
mDeviceNameText.setSelection(mSavedDeviceName.length());
diff --git a/src/com/android/settings/wifi/tether/TetherService.java b/src/com/android/settings/wifi/tether/TetherService.java
index 8a557ba..b0e8fd8 100644
--- a/src/com/android/settings/wifi/tether/TetherService.java
+++ b/src/com/android/settings/wifi/tether/TetherService.java
@@ -256,6 +256,7 @@
}
private void disableTethering(final int tetheringType) {
+ Log.w(TAG, "Disable tethering, type:" + tetheringType);
final TetheringManager tm = (TetheringManager) getSystemService(Context.TETHERING_SERVICE);
tm.stopTethering(tetheringType);
}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
index 580bc39..e88931c 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
@@ -31,6 +31,7 @@
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
import android.widget.Switch;
import androidx.annotation.VisibleForTesting;
@@ -47,6 +48,8 @@
*/
public class WifiTetherSwitchBarController implements
LifecycleObserver, OnStart, OnStop, DataSaverBackend.Listener, OnMainSwitchChangeListener {
+
+ private static final String TAG = "WifiTetherSBC";
private static final IntentFilter WIFI_INTENT_FILTER;
private final Context mContext;
@@ -63,8 +66,8 @@
@Override
public void onTetheringFailed() {
super.onTetheringFailed();
- mSwitchBar.setChecked(false);
- updateWifiSwitch();
+ Log.e(TAG, "Failed to start Wi-Fi Tethering.");
+ handleWifiApStateChanged(mWifiManager.getWifiApState());
}
};
@@ -111,16 +114,28 @@
}
void stopTether() {
+ if (!isWifiApActivated()) return;
+
mSwitchBar.setEnabled(false);
mConnectivityManager.stopTethering(TETHERING_WIFI);
}
void startTether() {
+ if (isWifiApActivated()) return;
+
mSwitchBar.setEnabled(false);
mConnectivityManager.startTethering(TETHERING_WIFI, true /* showProvisioningUi */,
mOnStartTetheringCallback, new Handler(Looper.getMainLooper()));
}
+ private boolean isWifiApActivated() {
+ final int wifiApState = mWifiManager.getWifiApState();
+ if (wifiApState == WIFI_AP_STATE_ENABLED || wifiApState == WIFI_AP_STATE_ENABLING) {
+ return true;
+ }
+ return false;
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
index 0418906..bddaed5 100644
--- a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
@@ -16,6 +16,8 @@
package com.android.settings.accessibility;
+import static com.android.settings.accessibility.TextReadingPreferenceFragment.RESET_KEY;
+
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -25,6 +27,7 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
+import com.android.settingslib.widget.LayoutPreference;
import com.google.android.setupdesign.GlifPreferenceLayout;
@@ -51,6 +54,9 @@
MockitoAnnotations.initMocks(this);
mFragment = spy(new TextReadingPreferenceFragmentForSetupWizard());
+ final LayoutPreference resetPreference =
+ new LayoutPreference(mContext, R.layout.accessibility_text_reading_reset_button);
+ doReturn(resetPreference).when(mFragment).findPreference(RESET_KEY);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiScanModeActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiScanModeActivityTest.java
index 3e6ba00..1e3afdb 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiScanModeActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiScanModeActivityTest.java
@@ -16,16 +16,75 @@
package com.android.settings.wifi;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.text.TextUtils;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.wifi.WifiPermissionChecker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowUtils.class})
public class WifiScanModeActivityTest {
+
+ static final String LAUNCHED_PACKAGE = "launched_package";
+ static final String APP_LABEL = "app_label";
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Mock
+ WifiPermissionChecker mWifiPermissionChecker;
+
+ WifiScanModeActivity mActivity;
+
+ @Before
+ public void setUp() {
+ mActivity = spy(Robolectric.setupActivity(WifiScanModeActivity.class));
+ mActivity.mWifiPermissionChecker = mWifiPermissionChecker;
+ }
+
+ @After
+ public void tearDown() {
+ ShadowUtils.reset();
+ }
+
@Test
public void launchActivity_noIntentAction_shouldNotFatalException() {
WifiScanModeActivity wifiScanModeActivity =
Robolectric.setupActivity(WifiScanModeActivity.class);
}
+
+ @Test
+ public void refreshAppLabel_noPackageName_shouldNotFatalException() {
+ when(mWifiPermissionChecker.getLaunchedPackage()).thenReturn(null);
+
+ mActivity.refreshAppLabel();
+
+ assertThat(TextUtils.isEmpty(mActivity.mApp)).isTrue();
+ }
+
+ @Test
+ public void refreshAppLabel_hasPackageName_shouldHasAppLabel() {
+ ShadowUtils.setApplicationLabel(LAUNCHED_PACKAGE, APP_LABEL);
+ when(mWifiPermissionChecker.getLaunchedPackage()).thenReturn(LAUNCHED_PACKAGE);
+
+ mActivity.refreshAppLabel();
+
+ assertThat(mActivity.mApp).isEqualTo(APP_LABEL);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
index 74bddda..e2c5ca3 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
@@ -55,6 +55,7 @@
import com.android.settings.network.ims.WifiCallingQueryImsState;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settings.widget.SettingsMainSwitchPreference;
import org.junit.Before;
import org.junit.Test;
@@ -72,6 +73,7 @@
public class WifiCallingSettingsForSubTest {
private static final int SUB_ID = 2;
+ private static final String SWITCH_BAR = "wifi_calling_switch_bar";
private static final String BUTTON_WFC_MODE = "wifi_calling_mode";
private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
private static final String PREFERENCE_NO_OPTIONS_DESC = "no_options_description";
@@ -100,6 +102,8 @@
@Mock
private View mView;
@Mock
+ private SettingsMainSwitchPreference mSwitchBarPreference;
+ @Mock
private LinkifyDescriptionPreference mDescriptionView;
@Mock
private ListWithEntrySummaryPreference mButtonWfcMode;
@@ -116,6 +120,7 @@
doReturn(mContext.getTheme()).when(mActivity).getTheme();
mFragment = spy(new TestFragment());
+ mFragment.setSwitchBar(mSwitchBarPreference);
doReturn(mActivity).when(mFragment).getActivity();
doReturn(mContext).when(mFragment).getContext();
doReturn(mock(Intent.class)).when(mActivity).getIntent();
@@ -125,10 +130,6 @@
final Bundle bundle = new Bundle();
when(mFragment.getArguments()).thenReturn(bundle);
doNothing().when(mFragment).addPreferencesFromResource(anyInt());
- doReturn(mock(ListWithEntrySummaryPreference.class)).when(mFragment).findPreference(any());
- doReturn(mButtonWfcMode).when(mFragment).findPreference(BUTTON_WFC_MODE);
- doReturn(mButtonWfcRoamingMode).when(mFragment).findPreference(BUTTON_WFC_ROAMING_MODE);
- doReturn(mDescriptionView).when(mFragment).findPreference(PREFERENCE_NO_OPTIONS_DESC);
doNothing().when(mFragment).finish();
doReturn(mView).when(mFragment).getView();
@@ -344,6 +345,29 @@
}
protected class TestFragment extends WifiCallingSettingsForSub {
+ private SettingsMainSwitchPreference mSwitchPref;
+
+ protected void setSwitchBar(SettingsMainSwitchPreference switchPref) {
+ mSwitchPref = switchPref;
+ }
+
+ @Override
+ public <T extends Preference> T findPreference(CharSequence key) {
+ if (SWITCH_BAR.equals(key)) {
+ return (T) mSwitchPref;
+ }
+ if (BUTTON_WFC_MODE.equals(key)) {
+ return (T) mButtonWfcMode;
+ }
+ if (BUTTON_WFC_ROAMING_MODE.equals(key)) {
+ return (T) mButtonWfcRoamingMode;
+ }
+ if (PREFERENCE_NO_OPTIONS_DESC.equals(key)) {
+ return (T) mDescriptionView;
+ }
+ return (T) mock(ListWithEntrySummaryPreference.class);
+ }
+
@Override
protected Object getSystemService(final String name) {
switch (name) {
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSwitchBarControllerTest.java
index 7c17c5f..ca0247f 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSwitchBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSwitchBarControllerTest.java
@@ -84,7 +84,44 @@
}
@Test
+ public void startTether_wifiApIsActivated_doNothing() {
+ when(mWifiManager.getWifiApState()).thenReturn(WIFI_AP_STATE_ENABLED);
+
+ mController.startTether();
+
+ verify(mConnectivityManager, never()).startTethering(anyInt(), anyBoolean(), any(), any());
+ }
+
+ @Test
+ public void startTether_wifiApNotActivated_startTethering() {
+ when(mWifiManager.getWifiApState()).thenReturn(WIFI_AP_STATE_DISABLED);
+
+ mController.startTether();
+
+ verify(mConnectivityManager).startTethering(anyInt(), anyBoolean(), any(), any());
+ }
+
+ @Test
+ public void stopTether_wifiApIsActivated_stopTethering() {
+ when(mWifiManager.getWifiApState()).thenReturn(WIFI_AP_STATE_ENABLED);
+
+ mController.stopTether();
+
+ verify(mConnectivityManager).stopTethering(anyInt());
+ }
+
+ @Test
+ public void stopTether_wifiApNotActivated_doNothing() {
+ when(mWifiManager.getWifiApState()).thenReturn(WIFI_AP_STATE_DISABLED);
+
+ mController.stopTether();
+
+ verify(mConnectivityManager, never()).stopTethering(anyInt());
+ }
+
+ @Test
public void startTether_fail_resetSwitchBar() {
+ when(mWifiManager.getWifiApState()).thenReturn(WIFI_AP_STATE_DISABLED);
when(mDataSaverBackend.isDataSaverEnabled()).thenReturn(false);
mController.startTether();
@@ -130,6 +167,7 @@
@Test
public void onSwitchChanged_isNotChecked_stopTethering() {
+ when(mWifiManager.getWifiApState()).thenReturn(WIFI_AP_STATE_ENABLED);
when(mSwitch.isChecked()).thenReturn(false);
mController.onSwitchChanged(mSwitch, mSwitch.isChecked());
diff --git a/tests/unit/src/com/android/settings/privacy/PrivacyDashboardActivityTest.java b/tests/unit/src/com/android/settings/privacy/PrivacyDashboardActivityTest.java
index 1cfee0f..ae42c84 100644
--- a/tests/unit/src/com/android/settings/privacy/PrivacyDashboardActivityTest.java
+++ b/tests/unit/src/com/android/settings/privacy/PrivacyDashboardActivityTest.java
@@ -44,54 +44,79 @@
@RunWith(AndroidJUnit4.class)
public class PrivacyDashboardActivityTest {
-
private static final String DEFAULT_FRAGMENT_CLASSNAME = "DefaultFragmentClassname";
-
@Mock
private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
private Settings.PrivacyDashboardActivity mActivity;
+ private static final String ACTION_PRIVACY_ADVANCED_SETTINGS =
+ "android.settings.PRIVACY_ADVANCED_SETTINGS";
@Before
- public void setUp() throws Exception {
+ public void setUp() {
MockitoAnnotations.initMocks(this);
-
SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
- final Intent intent = new Intent();
- intent.setAction(android.provider.Settings.ACTION_PRIVACY_SETTINGS);
- intent.setClass(InstrumentationRegistry.getInstrumentation().getTargetContext(),
- Settings.PrivacyDashboardActivity.class);
- intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, DEFAULT_FRAGMENT_CLASSNAME);
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- try {
- mActivity =
- spy((Settings.PrivacyDashboardActivity) InstrumentationRegistry
- .getInstrumentation().newActivity(
- getClass().getClassLoader(),
- Settings.PrivacyDashboardActivity.class.getName(),
- intent));
- } catch (Exception e) {
- throw new RuntimeException(e); // nothing to do
- }
- });
- doNothing().when(mActivity).startActivity(any(Intent.class));
}
@Test
- public void onCreate_whenSafetyCenterEnabled_redirectsToSafetyCenter() {
+ public void onCreate_whenSafetyCenterEnabled_redirectsToSafetyCenter() throws Exception {
+ startActivityUsingIntent(android.provider.Settings.ACTION_PRIVACY_SETTINGS);
when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(true);
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-
mActivity.handleSafetyCenterRedirection();
-
verify(mActivity).startActivity(intentCaptor.capture());
assertThat(intentCaptor.getValue().getAction()).isEqualTo(Intent.ACTION_SAFETY_CENTER);
}
@Test
- public void onCreate_whenSafetyCenterDisabled_doesntRedirectToSafetyCenter() {
+ public void onCreateWithAdvancedIntent_whenSafetyCenterEnabled_doesntRedirectToSafetyCenter()
+ throws Exception {
+ startActivityUsingIntent(ACTION_PRIVACY_ADVANCED_SETTINGS);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(true);
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ mActivity.handleSafetyCenterRedirection();
+ verify(mActivity, times(0)).startActivity(any());
+ }
+
+ @Test
+ public void onCreate_whenSafetyCenterDisabled_doesntRedirectToSafetyCenter() throws Exception {
+ startActivityUsingIntent(android.provider.Settings.ACTION_PRIVACY_SETTINGS);
when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(false);
mActivity.handleSafetyCenterRedirection();
-
verify(mActivity, times(0)).startActivity(any());
}
+
+ @Test
+ public void onCreateWithAdvancedIntent_whenSafetyCenterDisabled_doesntRedirectToSafetyCenter()
+ throws Exception {
+ startActivityUsingIntent(ACTION_PRIVACY_ADVANCED_SETTINGS);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(true);
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ mActivity.handleSafetyCenterRedirection();
+ verify(mActivity, times(0)).startActivity(any());
+ }
+
+ private void startActivityUsingIntent(String intentAction) throws Exception {
+ MockitoAnnotations.initMocks(this);
+ SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
+ final Intent intent = new Intent();
+ intent.setAction(intentAction);
+ intent.setClass(InstrumentationRegistry.getInstrumentation().getTargetContext(),
+ Settings.PrivacyDashboardActivity.class);
+ intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, DEFAULT_FRAGMENT_CLASSNAME);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ try {
+ Settings.PrivacyDashboardActivity activity =
+ (Settings.PrivacyDashboardActivity) InstrumentationRegistry
+ .getInstrumentation().newActivity(
+ getClass().getClassLoader(),
+ Settings.PrivacyDashboardActivity.class.getName(),
+ intent);
+ activity.setIntent(intent);
+ mActivity = spy(activity);
+ } catch (Exception e) {
+ throw new RuntimeException(e); // nothing to do
+ }
+ });
+ doNothing().when(mActivity).startActivity(any(Intent.class));
+ }
}